fix broken uploads (fix #540), simplify cfg, fix format
This commit is contained in:
parent
02b4e9d964
commit
14abd7d5be
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -1231,7 +1231,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout"
|
name = "ffplayout"
|
||||||
version = "0.20.4"
|
version = "0.20.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1253,7 +1253,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout-api"
|
name = "ffplayout-api"
|
||||||
version = "0.20.4"
|
version = "0.20.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
@ -1292,7 +1292,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout-lib"
|
name = "ffplayout-lib"
|
||||||
version = "0.20.4"
|
version = "0.20.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@ -1302,6 +1302,7 @@ dependencies = [
|
|||||||
"lettre",
|
"lettre",
|
||||||
"lexical-sort",
|
"lexical-sort",
|
||||||
"log",
|
"log",
|
||||||
|
"num-traits",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -3461,7 +3462,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tests"
|
name = "tests"
|
||||||
version = "0.20.4"
|
version = "0.20.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
@ -4,7 +4,7 @@ default-members = ["ffplayout-api", "ffplayout-engine", "tests"]
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.20.4"
|
version = "0.20.5"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/ffplayout/ffplayout"
|
repository = "https://github.com/ffplayout/ffplayout"
|
||||||
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
||||||
|
@ -994,10 +994,18 @@ pub async fn remove(
|
|||||||
async fn save_file(
|
async fn save_file(
|
||||||
pool: web::Data<Pool<Sqlite>>,
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
|
req: HttpRequest,
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
obj: web::Query<FileObj>,
|
obj: web::Query<FileObj>,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
upload(&pool.into_inner(), *id, payload, &obj.path, false).await
|
let size: u64 = req
|
||||||
|
.headers()
|
||||||
|
.get("content-length")
|
||||||
|
.and_then(|cl| cl.to_str().ok())
|
||||||
|
.and_then(|cls| cls.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
upload(&pool.into_inner(), *id, size, payload, &obj.path, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Get File**
|
/// **Get File**
|
||||||
@ -1070,6 +1078,7 @@ async fn get_public(public: web::Path<String>) -> Result<actix_files::NamedFile,
|
|||||||
async fn import_playlist(
|
async fn import_playlist(
|
||||||
pool: web::Data<Pool<Sqlite>>,
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
|
req: HttpRequest,
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
obj: web::Query<ImportObj>,
|
obj: web::Query<ImportObj>,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
@ -1077,8 +1086,14 @@ async fn import_playlist(
|
|||||||
let path = env::temp_dir().join(file);
|
let path = env::temp_dir().join(file);
|
||||||
let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?;
|
let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?;
|
||||||
let channel = handles::select_channel(&pool.clone().into_inner(), &id).await?;
|
let channel = handles::select_channel(&pool.clone().into_inner(), &id).await?;
|
||||||
|
let size: u64 = req
|
||||||
|
.headers()
|
||||||
|
.get("content-length")
|
||||||
|
.and_then(|cl| cl.to_str().ok())
|
||||||
|
.and_then(|cls| cls.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
upload(&pool.into_inner(), *id, payload, &path, true).await?;
|
upload(&pool.into_inner(), *id, size, payload, &path, true).await?;
|
||||||
import_file(&config, &obj.date, Some(channel.name), &path)?;
|
import_file(&config, &obj.date, Some(channel.name), &path)?;
|
||||||
|
|
||||||
fs::remove_file(path)?;
|
fs::remove_file(path)?;
|
||||||
|
@ -11,8 +11,7 @@ use actix_web::{
|
|||||||
use actix_web_grants::authorities::AttachAuthorities;
|
use actix_web_grants::authorities::AttachAuthorities;
|
||||||
use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication};
|
use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication};
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(all(not(debug_assertions), feature = "embed_frontend"))]
|
||||||
#[cfg(feature = "embed_frontend")]
|
|
||||||
use actix_web_static_files::ResourceFiles;
|
use actix_web_static_files::ResourceFiles;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
@ -34,8 +33,7 @@ use utils::public_path;
|
|||||||
|
|
||||||
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(all(not(debug_assertions), feature = "embed_frontend"))]
|
||||||
#[cfg(feature = "embed_frontend")]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -172,8 +170,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
web_app = web_app.service(get_public);
|
web_app = web_app.service(get_public);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(all(not(debug_assertions), feature = "embed_frontend"))]
|
||||||
#[cfg(feature = "embed_frontend")]
|
|
||||||
{
|
{
|
||||||
// in release mode embed frontend
|
// in release mode embed frontend
|
||||||
let generated = generate();
|
let generated = generate();
|
||||||
@ -184,10 +181,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
#[cfg(any(debug_assertions, not(feature = "embed_frontend")))]
|
#[cfg(any(debug_assertions, not(feature = "embed_frontend")))]
|
||||||
{
|
{
|
||||||
// in debug mode get frontend from path
|
// in debug mode get frontend from path
|
||||||
web_app = web_app.service(
|
web_app = web_app.service(Files::new("/", public_path()).index_file("index.html"));
|
||||||
Files::new("/", public_path())
|
|
||||||
.index_file("index.html"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
web_app
|
web_app
|
||||||
|
@ -308,6 +308,7 @@ async fn valid_path(conn: &Pool<Sqlite>, id: i32, path: &str) -> Result<PathBuf,
|
|||||||
pub async fn upload(
|
pub async fn upload(
|
||||||
conn: &Pool<Sqlite>,
|
conn: &Pool<Sqlite>,
|
||||||
id: i32,
|
id: i32,
|
||||||
|
_size: u64,
|
||||||
mut payload: Multipart,
|
mut payload: Multipart,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
abs_path: bool,
|
abs_path: bool,
|
||||||
@ -324,23 +325,47 @@ pub async fn upload(
|
|||||||
.get_filename()
|
.get_filename()
|
||||||
.map_or_else(|| rand_string.to_string(), sanitize_filename::sanitize);
|
.map_or_else(|| rand_string.to_string(), sanitize_filename::sanitize);
|
||||||
|
|
||||||
let filepath;
|
let filepath = if abs_path {
|
||||||
|
path.to_path_buf()
|
||||||
if abs_path {
|
|
||||||
filepath = path.to_path_buf();
|
|
||||||
} else {
|
} else {
|
||||||
let target_path = valid_path(conn, id, &path.to_string_lossy()).await?;
|
valid_path(conn, id, &path.to_string_lossy())
|
||||||
filepath = target_path.join(filename);
|
.await?
|
||||||
}
|
.join(filename)
|
||||||
|
};
|
||||||
|
let filepath_clone = filepath.clone();
|
||||||
|
|
||||||
|
let _file_size = match filepath.metadata() {
|
||||||
|
Ok(metadata) => metadata.len(),
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// INFO: File exist check should be enough because file size and content length are different.
|
||||||
|
// The error catching in the loop should normally prevent unfinished files from existing on disk.
|
||||||
|
// If this is not enough, a second check can be implemented: is_close(file_size as i64, size as i64, 1000)
|
||||||
if filepath.is_file() {
|
if filepath.is_file() {
|
||||||
return Err(ServiceError::BadRequest("Target already exists!".into()));
|
return Err(ServiceError::Conflict("Target already exists!".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut f = web::block(|| std::fs::File::create(filepath)).await??;
|
let mut f = web::block(|| std::fs::File::create(filepath_clone)).await??;
|
||||||
|
|
||||||
while let Some(chunk) = field.try_next().await? {
|
loop {
|
||||||
f = web::block(move || f.write_all(&chunk).map(|_| f)).await??;
|
match field.try_next().await {
|
||||||
|
Ok(Some(chunk)) => {
|
||||||
|
f = web::block(move || f.write_all(&chunk).map(|_| f)).await??;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None) => break,
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
if e.to_string().contains("stream is incomplete") {
|
||||||
|
info!("Delete non finished file: {filepath:?}");
|
||||||
|
|
||||||
|
tokio::fs::remove_file(filepath).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,9 +140,9 @@ pub fn db_path() -> Result<&'static str, Box<dyn std::error::Error>> {
|
|||||||
return Ok(Box::leak(
|
return Ok(Box::leak(
|
||||||
absolute_path.to_string_lossy().to_string().into_boxed_str(),
|
absolute_path.to_string_lossy().to_string().into_boxed_str(),
|
||||||
));
|
));
|
||||||
} else {
|
|
||||||
error!("Given database path is not writable!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error!("Given database path is not writable!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,12 +169,10 @@ pub fn public_path() -> PathBuf {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
let path = PathBuf::from("./ffplayout-frontend/.output/public/");
|
||||||
{
|
|
||||||
let path = PathBuf::from("./ffplayout-frontend/.output/public/");
|
if cfg!(debug_assertions) && path.is_dir() {
|
||||||
if path.is_dir() {
|
return path;
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PathBuf::from("./public/")
|
PathBuf::from("./public/")
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 28bcfcd1a399a9dd5363541f5f01203c7d968e18
|
Subproject commit 8eb543ddae035e448ea0886ec1d44256de2e6f07
|
@ -17,6 +17,7 @@ file-rotate = "0.7"
|
|||||||
lettre = { version = "0.11", features = ["builder", "rustls-tls", "smtp-transport"], default-features = false }
|
lettre = { version = "0.11", features = ["builder", "rustls-tls", "smtp-transport"], default-features = false }
|
||||||
lexical-sort = "0.3"
|
lexical-sort = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
num-traits = "0.2"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
|
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
|
||||||
|
@ -435,12 +435,8 @@ pub fn file_extension(filename: &Path) -> Option<&str> {
|
|||||||
|
|
||||||
/// Test if given numbers are close to each other,
|
/// Test if given numbers are close to each other,
|
||||||
/// with a third number for setting the maximum range.
|
/// with a third number for setting the maximum range.
|
||||||
pub fn is_close(a: f64, b: f64, to: f64) -> bool {
|
pub fn is_close<T: num_traits::Signed + std::cmp::PartialOrd>(a: T, b: T, to: T) -> bool {
|
||||||
if (a - b).abs() < to {
|
(a - b).abs() < to
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add duration from all media clips
|
/// add duration from all media clips
|
||||||
|
Loading…
Reference in New Issue
Block a user