support path list for playlist generation, #258
This commit is contained in:
parent
af985cb351
commit
e752a7a951
@ -34,7 +34,7 @@ use crate::utils::{
|
|||||||
errors::ServiceError,
|
errors::ServiceError,
|
||||||
files::{
|
files::{
|
||||||
browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject,
|
browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject,
|
||||||
PathObject,
|
PathObject, norm_abs_path,
|
||||||
},
|
},
|
||||||
naive_date_time_from_str,
|
naive_date_time_from_str,
|
||||||
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
||||||
@ -72,6 +72,12 @@ struct FileObj {
|
|||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct PathsObj {
|
||||||
|
#[serde(default)]
|
||||||
|
paths: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct ImportObj {
|
pub struct ImportObj {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -734,16 +740,33 @@ pub async fn save_playlist(
|
|||||||
/// A new playlist will be generated and response.
|
/// A new playlist will be generated and response.
|
||||||
///
|
///
|
||||||
/// ```BASH
|
/// ```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 <TOKEN>'
|
/// -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
||||||
|
/// /// -- data '{ "paths": [<list of paths>] }' # <- data is optional
|
||||||
/// ```
|
/// ```
|
||||||
#[get("/playlist/{id}/generate/{date}")]
|
#[post("/playlist/{id}/generate/{date}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn gen_playlist(
|
pub async fn gen_playlist(
|
||||||
pool: web::Data<Pool<Sqlite>>,
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
params: web::Path<(i32, String)>,
|
params: web::Path<(i32, String)>,
|
||||||
|
data: Option<web::Json<PathsObj>>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
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)),
|
Ok(playlist) => Ok(web::Json(playlist)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ pub struct VideoFile {
|
|||||||
///
|
///
|
||||||
/// This function takes care, that it is not possible to break out from root_path.
|
/// This function takes care, that it is not possible to break out from root_path.
|
||||||
/// It also gives alway a relative path back.
|
/// 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 mut path = PathBuf::from(root_path);
|
||||||
let path_relative = RelativePath::new(root_path)
|
let path_relative = RelativePath::new(root_path)
|
||||||
.normalize()
|
.normalize()
|
||||||
|
@ -5,7 +5,7 @@ use sqlx::{Pool, Sqlite};
|
|||||||
|
|
||||||
use crate::utils::{errors::ServiceError, playout_config};
|
use crate::utils::{errors::ServiceError, playout_config};
|
||||||
use ffplayout_lib::utils::{
|
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(
|
pub async fn read_playlist(
|
||||||
@ -78,14 +78,10 @@ pub async fn write_playlist(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn generate_playlist(
|
pub async fn generate_playlist(
|
||||||
conn: &Pool<Sqlite>,
|
config: PlayoutConfig,
|
||||||
id: i32,
|
channel: String,
|
||||||
date: String,
|
|
||||||
) -> Result<JsonPlaylist, ServiceError> {
|
) -> Result<JsonPlaylist, ServiceError> {
|
||||||
let (mut config, channel) = playout_config(conn, &id).await?;
|
match playlist_generator(&config, Some(channel)) {
|
||||||
config.general.generate = Some(vec![date.clone()]);
|
|
||||||
|
|
||||||
match playlist_generator(&config, Some(channel.name)) {
|
|
||||||
Ok(playlists) => {
|
Ok(playlists) => {
|
||||||
if !playlists.is_empty() {
|
if !playlists.is_empty() {
|
||||||
Ok(playlists[0].clone())
|
Ok(playlists[0].clone())
|
||||||
|
@ -27,6 +27,13 @@ pub struct Args {
|
|||||||
)]
|
)]
|
||||||
pub generate: Option<Vec<String>>,
|
pub generate: Option<Vec<String>>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
help = "Optional path list for playlist generations",
|
||||||
|
multiple_values = true
|
||||||
|
)]
|
||||||
|
pub paths: Option<Vec<String>>,
|
||||||
|
|
||||||
#[clap(short = 'm', long, help = "Playing mode: folder, playlist")]
|
#[clap(short = 'm', long, help = "Playing mode: folder, playlist")]
|
||||||
pub play_mode: Option<ProcessMode>,
|
pub play_mode: Option<ProcessMode>,
|
||||||
|
|
||||||
@ -46,7 +53,7 @@ pub struct Args {
|
|||||||
)]
|
)]
|
||||||
pub import: Option<String>,
|
pub import: Option<String>,
|
||||||
|
|
||||||
#[clap(short, long, help = "Path from playlist")]
|
#[clap(short, long, help = "Path to playlist, or playlist root folder.")]
|
||||||
pub playlist: Option<String>,
|
pub playlist: Option<String>,
|
||||||
|
|
||||||
#[clap(
|
#[clap(
|
||||||
|
@ -38,6 +38,10 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
|||||||
config.general.generate = Some(gen);
|
config.general.generate = Some(gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(paths) = args.paths {
|
||||||
|
config.storage.paths = paths;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(log_path) = args.log {
|
if let Some(log_path) = args.log {
|
||||||
if Path::new(&log_path).is_dir() {
|
if Path::new(&log_path).is_dir() {
|
||||||
config.logging.log_to_file = true;
|
config.logging.log_to_file = true;
|
||||||
|
@ -213,6 +213,8 @@ pub struct Playlist {
|
|||||||
pub struct Storage {
|
pub struct Storage {
|
||||||
pub help_text: String,
|
pub help_text: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
|
pub paths: Vec<String>,
|
||||||
pub filler_clip: String,
|
pub filler_clip: String,
|
||||||
pub extensions: Vec<String>,
|
pub extensions: Vec<String>,
|
||||||
pub shuffle: bool,
|
pub shuffle: bool,
|
||||||
|
@ -32,18 +32,28 @@ impl FolderSource {
|
|||||||
current_list: Arc<Mutex<Vec<Media>>>,
|
current_list: Arc<Mutex<Vec<Media>>>,
|
||||||
global_index: Arc<AtomicUsize>,
|
global_index: Arc<AtomicUsize>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let mut path_list = vec![];
|
||||||
let mut media_list = vec![];
|
let mut media_list = vec![];
|
||||||
let mut index: usize = 0;
|
let mut index: usize = 0;
|
||||||
|
|
||||||
if !Path::new(&config.storage.path).is_dir() {
|
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 path in &path_list {
|
||||||
|
if !Path::new(path).is_dir() {
|
||||||
error!(
|
error!(
|
||||||
"Path not exists: <b><magenta>{}</></b>",
|
"Path not exists: <b><magenta>{}</></b>",
|
||||||
config.storage.path
|
path
|
||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for entry in WalkDir::new(config.storage.path.clone())
|
for entry in WalkDir::new(path.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|e| e.ok())
|
.flat_map(|e| e.ok())
|
||||||
.filter(|f| f.path().is_file())
|
.filter(|f| f.path().is_file())
|
||||||
@ -53,11 +63,12 @@ impl FolderSource {
|
|||||||
media_list.push(media);
|
media_list.push(media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if media_list.is_empty() {
|
if media_list.is_empty() {
|
||||||
error!(
|
error!(
|
||||||
"no playable files found under: <b><magenta>{}</></b>",
|
"no playable files found under: <b><magenta>{:?}</></b>",
|
||||||
config.storage.path
|
path_list
|
||||||
);
|
);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user