diff --git a/Cargo.lock b/Cargo.lock index 4929b47f..08e3b438 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,7 +1112,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "ffplayout" -version = "0.20.0-beta3" +version = "0.20.0-beta4" dependencies = [ "chrono", "clap", @@ -1134,7 +1134,7 @@ dependencies = [ [[package]] name = "ffplayout-api" -version = "0.20.0-beta3" +version = "0.20.0-beta4" dependencies = [ "actix-files", "actix-multipart", @@ -1167,7 +1167,7 @@ dependencies = [ [[package]] name = "ffplayout-lib" -version = "0.20.0-beta3" +version = "0.20.0-beta4" dependencies = [ "chrono", "crossbeam-channel", @@ -3126,7 +3126,7 @@ dependencies = [ [[package]] name = "tests" -version = "0.20.0-beta3" +version = "0.20.0-beta4" dependencies = [ "chrono", "crossbeam-channel", diff --git a/Cargo.toml b/Cargo.toml index 4ad5672f..87bda8ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ default-members = ["ffplayout-api", "ffplayout-engine", "tests"] resolver = "2" [workspace.package] -version = "0.20.0-beta3" +version = "0.20.0-beta4" license = "GPL-3.0" repository = "https://github.com/ffplayout/ffplayout" authors = ["Jonathan Baecker "] diff --git a/ffplayout-api/src/api/routes.rs b/ffplayout-api/src/api/routes.rs index 776ab6fb..8d718061 100644 --- a/ffplayout-api/src/api/routes.rs +++ b/ffplayout-api/src/api/routes.rs @@ -8,11 +8,20 @@ /// /// For all endpoints an (Bearer) authentication is required.\ /// `{id}` represent the channel id, and at default is 1. -use std::{collections::HashMap, env, fs, path::PathBuf}; +use std::{collections::HashMap, env, fs, path::PathBuf, sync::{Arc, Mutex}}; +use actix_files; use actix_multipart::Multipart; -use actix_web::{delete, get, http::StatusCode, patch, post, put, web, HttpResponse, Responder}; +use actix_web::{ + delete, get, + http::{ + header::{ContentDisposition, DispositionType}, + StatusCode, + }, + patch, post, put, web, Error, HttpRequest, HttpResponse, Responder, +}; use actix_web_grants::{permissions::AuthDetails, proc_macro::has_any_role}; + use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, SaltString}, Argon2, PasswordHasher, PasswordVerifier, @@ -37,7 +46,7 @@ use crate::utils::{ }, naive_date_time_from_str, playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist}, - playout_config, read_log_file, read_playout_config, Role, + read_log_file, read_playout_config, Role, }; use crate::{ auth::{create_jwt, Claims}, @@ -438,6 +447,7 @@ async fn update_playout_config( pool: web::Data>, id: web::Path, data: web::Json, + config: web::Data>>, ) -> Result { if let Ok(channel) = handles::select_channel(&pool.into_inner(), &id).await { if let Ok(f) = std::fs::OpenOptions::new() @@ -446,6 +456,7 @@ async fn update_playout_config( .open(channel.config_path) { serde_yaml::to_writer(f, &data).unwrap(); + *config.lock().unwrap() = data.into_inner(); return Ok("Update playout config success."); } else { @@ -765,8 +776,10 @@ pub async fn gen_playlist( pool: web::Data>, params: web::Path<(i32, String)>, data: Option>, + global_config: web::Data>>, ) -> Result { - let (mut config, channel) = playout_config(&pool.into_inner(), ¶ms.0).await?; + let channel = handles::select_channel(&pool.into_inner(), ¶ms.0).await?; + let mut config = global_config.lock().unwrap(); config.general.generate = Some(vec![params.1.clone()]); if let Some(obj) = data { @@ -785,7 +798,7 @@ pub async fn gen_playlist( config.general.template = obj.template.clone(); } - match generate_playlist(config, channel.name).await { + match generate_playlist(config.to_owned(), channel.name).await { Ok(playlist) => Ok(web::Json(playlist)), Err(e) => Err(e), } @@ -919,6 +932,23 @@ async fn save_file( upload(&pool.into_inner(), *id, payload, &obj.path, false).await } +#[get("/file/{id}/{filename:.*}")] +#[has_any_role("Role::Admin", "Role::User", type = "Role")] +async fn get_file(req: HttpRequest, config: web::Data>>,) -> Result { + let storage_path = &config.lock().unwrap().storage.path; + let file_path = req.match_info().query("filename"); + let (path, _, _) = norm_abs_path(storage_path, file_path); + println!("{path:?}"); + + let file = actix_files::NamedFile::open(path)?; + Ok(file + .use_last_modified(true) + .set_content_disposition(ContentDisposition { + disposition: DispositionType::Attachment, + parameters: vec![], + })) +} + /// **Import playlist** /// /// Import text/m3u file and convert it to a playlist @@ -935,11 +965,12 @@ async fn import_playlist( id: web::Path, payload: Multipart, obj: web::Query, + global_config: web::Data>>, ) -> Result { let file = obj.file.file_name().unwrap_or_default(); let path = env::temp_dir().join(file); - let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?; let channel = handles::select_channel(&pool.clone().into_inner(), &id).await?; + let config = global_config.lock().unwrap(); upload(&pool.into_inner(), *id, payload, &path, true).await?; import_file(&config, &obj.date, Some(channel.name), &path)?; @@ -977,8 +1008,9 @@ async fn get_program( pool: web::Data>, id: web::Path, obj: web::Query, + global_config: web::Data>>, ) -> Result { - let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?; + let config = global_config.lock().unwrap(); let start_sec = config.playlist.start_sec.unwrap(); let mut days = 0; let mut program = vec![]; diff --git a/ffplayout-api/src/main.rs b/ffplayout-api/src/main.rs index 11b6820d..cce05395 100644 --- a/ffplayout-api/src/main.rs +++ b/ffplayout-api/src/main.rs @@ -1,4 +1,4 @@ -use std::{path::Path, process::exit}; +use std::{path::Path, process::exit, sync::{Arc, Mutex}}; use actix_files::Files; use actix_web::{dev::ServiceRequest, middleware, web, App, Error, HttpMessage, HttpServer}; @@ -14,14 +14,7 @@ pub mod utils; use api::{ auth, - routes::{ - add_channel, add_dir, add_preset, add_user, control_playout, del_playlist, delete_preset, - file_browser, gen_playlist, get_all_channels, get_channel, get_log, get_playlist, - get_playout_config, get_presets, get_program, get_user, import_playlist, login, - media_current, media_last, media_next, move_rename, patch_channel, process_control, remove, - remove_channel, save_file, save_playlist, send_text_message, update_playout_config, - update_preset, update_user, - }, + routes::*, }; use db::{db_pool, models::LoginUser}; use utils::{args_parse::Args, control::ProcessControl, db_path, init_config, run_args, Role}; @@ -92,6 +85,7 @@ async fn main() -> std::io::Result<()> { let addr = ip_port[0]; let port = ip_port[1].parse::().unwrap(); let engine_process = web::Data::new(ProcessControl::new()); + let global_config = Arc::new(Mutex::new(config)); info!("running ffplayout API, listen on {conn}"); @@ -99,9 +93,11 @@ async fn main() -> std::io::Result<()> { HttpServer::new(move || { let auth = HttpAuthentication::bearer(validator); let db_pool = web::Data::new(pool.clone()); + let global = web::Data::new(global_config.clone()); App::new() .app_data(db_pool) + .app_data(global) .app_data(engine_process.clone()) .wrap(middleware::Logger::default()) .service(login) @@ -138,6 +134,7 @@ async fn main() -> std::io::Result<()> { .service(move_rename) .service(remove) .service(save_file) + .service(get_file) .service(import_playlist) .service(get_program), )