diff --git a/Cargo.lock b/Cargo.lock index e6a9ad0b..9c12cf29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -963,7 +963,7 @@ dependencies = [ [[package]] name = "ffplayout" -version = "0.17.0-beta1" +version = "0.17.0-beta2" dependencies = [ "chrono", "clap", @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "ffplayout-api" -version = "0.8.4" +version = "0.9.0-beta1" dependencies = [ "actix-files", "actix-multipart", @@ -1015,7 +1015,7 @@ dependencies = [ [[package]] name = "ffplayout-lib" -version = "0.17.0-beta1" +version = "0.17.0-beta2" dependencies = [ "chrono", "crossbeam-channel", diff --git a/ffplayout-api/Cargo.toml b/ffplayout-api/Cargo.toml index e71c58d0..feccfde0 100644 --- a/ffplayout-api/Cargo.toml +++ b/ffplayout-api/Cargo.toml @@ -4,7 +4,7 @@ description = "Rest API for ffplayout" license = "GPL-3.0" authors = ["Jonathan Baecker jonbae77@gmail.com"] readme = "README.md" -version = "0.8.4" +version = "0.9.0-beta1" edition = "2021" [dependencies] diff --git a/ffplayout-api/src/api/routes.rs b/ffplayout-api/src/api/routes.rs index 60e6eef8..f7c49aa7 100644 --- a/ffplayout-api/src/api/routes.rs +++ b/ffplayout-api/src/api/routes.rs @@ -33,8 +33,8 @@ use crate::utils::{ control::{control_service, control_state, media_info, send_message, Process}, errors::ServiceError, files::{ - browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject, - PathObject, + browser, create_directory, norm_abs_path, remove_file_or_folder, rename_file, upload, + MoveObject, PathObject, }, naive_date_time_from_str, playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist}, @@ -72,6 +72,12 @@ struct FileObj { path: String, } +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct PathsObj { + #[serde(default)] + paths: Vec, +} + #[derive(Debug, Deserialize, Serialize)] pub struct ImportObj { #[serde(default)] @@ -734,16 +740,33 @@ pub async fn save_playlist( /// A new playlist will be generated and response. /// /// ```BASH -/// curl -X GET http://127.0.0.1:8787/api/playlist/1/generate/2022-06-20 +/// curl -X POST http://127.0.0.1:8787/api/playlist/1/generate/2022-06-20 /// -H 'Content-Type: application/json' -H 'Authorization: Bearer ' +/// /// -- data '{ "paths": [] }' # <- data is optional /// ``` -#[get("/playlist/{id}/generate/{date}")] +#[post("/playlist/{id}/generate/{date}")] #[has_any_role("Role::Admin", "Role::User", type = "Role")] pub async fn gen_playlist( pool: web::Data>, params: web::Path<(i32, String)>, + data: Option>, ) -> Result { - match generate_playlist(&pool.into_inner(), params.0, params.1.clone()).await { + let (mut config, channel) = playout_config(&pool.into_inner(), ¶ms.0).await?; + config.general.generate = Some(vec![params.1.clone()]); + + if let Some(obj) = data { + let mut path_list = vec![]; + + for path in &obj.paths { + let (p, _, _) = norm_abs_path(&config.storage.path, path); + + path_list.push(p.to_string_lossy().to_string()); + } + + config.storage.paths = path_list; + } + + match generate_playlist(config, channel.name).await { Ok(playlist) => Ok(web::Json(playlist)), Err(e) => Err(e), } diff --git a/ffplayout-api/src/utils/files.rs b/ffplayout-api/src/utils/files.rs index 11c19166..91c87375 100644 --- a/ffplayout-api/src/utils/files.rs +++ b/ffplayout-api/src/utils/files.rs @@ -20,6 +20,8 @@ pub struct PathObject { parent: Option, folders: Option>, files: Option>, + #[serde(default)] + pub folders_only: bool, } impl PathObject { @@ -29,6 +31,7 @@ impl PathObject { parent, folders: Some(vec![]), files: Some(vec![]), + folders_only: false, } } } @@ -49,7 +52,7 @@ pub struct VideoFile { /// /// This function takes care, that it is not possible to break out from root_path. /// It also gives alway a relative path back. -fn norm_abs_path(root_path: &str, input_path: &str) -> (PathBuf, String, String) { +pub fn norm_abs_path(root_path: &str, input_path: &str) -> (PathBuf, String, String) { let mut path = PathBuf::from(root_path); let path_relative = RelativePath::new(root_path) .normalize() @@ -105,6 +108,7 @@ pub async fn browser( let (path, parent, path_component) = norm_abs_path(&config.storage.path, &path_obj.source); let mut obj = PathObject::new(path_component, Some(parent)); + obj.folders_only = path_obj.folders_only; let mut paths: Vec = match fs::read_dir(path) { Ok(p) => p.filter_map(|r| r.ok()).map(|p| p.path()).collect(), @@ -126,7 +130,7 @@ pub async fn browser( if path.is_dir() { folders.push(path.file_name().unwrap().to_string_lossy().to_string()); - } else if path.is_file() { + } else if path.is_file() && !path_obj.folders_only { if let Some(ext) = file_extension(&path) { if extensions.contains(&ext.to_string().to_lowercase()) { let media = MediaProbe::new(&path.display().to_string()); diff --git a/ffplayout-api/src/utils/playlist.rs b/ffplayout-api/src/utils/playlist.rs index d693396a..65cee698 100644 --- a/ffplayout-api/src/utils/playlist.rs +++ b/ffplayout-api/src/utils/playlist.rs @@ -5,7 +5,7 @@ use sqlx::{Pool, Sqlite}; use crate::utils::{errors::ServiceError, playout_config}; use ffplayout_lib::utils::{ - generate_playlist as playlist_generator, json_reader, json_writer, JsonPlaylist, + generate_playlist as playlist_generator, json_reader, json_writer, JsonPlaylist, PlayoutConfig, }; pub async fn read_playlist( @@ -78,14 +78,10 @@ pub async fn write_playlist( } pub async fn generate_playlist( - conn: &Pool, - id: i32, - date: String, + config: PlayoutConfig, + channel: String, ) -> Result { - let (mut config, channel) = playout_config(conn, &id).await?; - config.general.generate = Some(vec![date.clone()]); - - match playlist_generator(&config, Some(channel.name)) { + match playlist_generator(&config, Some(channel)) { Ok(playlists) => { if !playlists.is_empty() { Ok(playlists[0].clone()) diff --git a/ffplayout-engine/Cargo.toml b/ffplayout-engine/Cargo.toml index 0390f4e3..680fbec9 100644 --- a/ffplayout-engine/Cargo.toml +++ b/ffplayout-engine/Cargo.toml @@ -4,7 +4,7 @@ description = "24/7 playout based on rust and ffmpeg" license = "GPL-3.0" authors = ["Jonathan Baecker jonbae77@gmail.com"] readme = "README.md" -version = "0.17.0-beta1" +version = "0.17.0-beta2" edition = "2021" default-run = "ffplayout" diff --git a/ffplayout-engine/src/utils/arg_parse.rs b/ffplayout-engine/src/utils/arg_parse.rs index bf349bd5..12fa91a5 100644 --- a/ffplayout-engine/src/utils/arg_parse.rs +++ b/ffplayout-engine/src/utils/arg_parse.rs @@ -27,6 +27,13 @@ pub struct Args { )] pub generate: Option>, + #[clap( + long, + help = "Optional path list for playlist generations", + multiple_values = true + )] + pub paths: Option>, + #[clap(short = 'm', long, help = "Playing mode: folder, playlist")] pub play_mode: Option, @@ -46,7 +53,7 @@ pub struct Args { )] pub import: Option, - #[clap(short, long, help = "Path from playlist")] + #[clap(short, long, help = "Path to playlist, or playlist root folder.")] pub playlist: Option, #[clap( diff --git a/ffplayout-engine/src/utils/mod.rs b/ffplayout-engine/src/utils/mod.rs index bc9322c3..422ba12e 100644 --- a/ffplayout-engine/src/utils/mod.rs +++ b/ffplayout-engine/src/utils/mod.rs @@ -38,6 +38,10 @@ pub fn get_config(args: Args) -> PlayoutConfig { config.general.generate = Some(gen); } + if let Some(paths) = args.paths { + config.storage.paths = paths; + } + if let Some(log_path) = args.log { if Path::new(&log_path).is_dir() { config.logging.log_to_file = true; diff --git a/ffplayout-frontend b/ffplayout-frontend index 072002c6..92c7a8b0 160000 --- a/ffplayout-frontend +++ b/ffplayout-frontend @@ -1 +1 @@ -Subproject commit 072002c68451a5e7e62046a72ddac1d4bae7fcea +Subproject commit 92c7a8b041c897fe9ed4819a61c500e61fc5318f diff --git a/lib/Cargo.toml b/lib/Cargo.toml index bfa4d9f4..53331aee 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -4,7 +4,7 @@ description = "Library for ffplayout" license = "GPL-3.0" authors = ["Jonathan Baecker jonbae77@gmail.com"] readme = "README.md" -version = "0.17.0-beta1" +version = "0.17.0-beta2" edition = "2021" [dependencies] diff --git a/lib/src/utils/config.rs b/lib/src/utils/config.rs index edf9c53f..709310a2 100644 --- a/lib/src/utils/config.rs +++ b/lib/src/utils/config.rs @@ -213,6 +213,8 @@ pub struct Playlist { pub struct Storage { pub help_text: String, pub path: String, + #[serde(skip_serializing, skip_deserializing)] + pub paths: Vec, pub filler_clip: String, pub extensions: Vec, pub shuffle: bool, diff --git a/lib/src/utils/folder.rs b/lib/src/utils/folder.rs index a5c8b58d..962a5fb4 100644 --- a/lib/src/utils/folder.rs +++ b/lib/src/utils/folder.rs @@ -1,6 +1,5 @@ use std::{ path::Path, - process::exit, sync::{ atomic::{AtomicUsize, Ordering}, {Arc, Mutex}, @@ -32,35 +31,40 @@ impl FolderSource { current_list: Arc>>, global_index: Arc, ) -> Self { + let mut path_list = vec![]; let mut media_list = vec![]; let mut index: usize = 0; - if !Path::new(&config.storage.path).is_dir() { - error!( - "Path not exists: {}", - config.storage.path - ); - exit(1); + if config.general.generate.is_some() && !config.storage.paths.is_empty() { + for path in &config.storage.paths { + path_list.push(path.clone()) + } + } else { + path_list.push(config.storage.path.clone()) } - for entry in WalkDir::new(config.storage.path.clone()) - .into_iter() - .flat_map(|e| e.ok()) - .filter(|f| f.path().is_file()) - { - if include_file(config.clone(), entry.path()) { - let media = Media::new(0, &entry.path().to_string_lossy(), false); - media_list.push(media); + for path in &path_list { + if !Path::new(path).is_dir() { + error!("Path not exists: {path}"); + } + + for entry in WalkDir::new(path.clone()) + .into_iter() + .flat_map(|e| e.ok()) + .filter(|f| f.path().is_file()) + { + if include_file(config.clone(), entry.path()) { + let media = Media::new(0, &entry.path().to_string_lossy(), false); + media_list.push(media); + } } } if media_list.is_empty() { error!( - "no playable files found under: {}", - config.storage.path + "no playable files found under: {:?}", + path_list ); - - exit(1); } if config.storage.shuffle {