diff --git a/docker/Dockerfile b/docker/Dockerfile index 0feccbf0..81f356bb 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:latest -ARG FFPLAYOUT_VERSION=0.24.0-beta1 +ARG FFPLAYOUT_VERSION=0.24.0-beta2 ARG SHARED_STORAGE=false ENV DB=/db @@ -12,7 +12,7 @@ COPY <<-EOT /run.sh #!/bin/sh if [ ! -f /db/ffplayout.db ]; then - ffplayout -u admin -p admin -m contact@example.com --storage-path "/tv-media" --playlist-path "/playlists" --hls-path "/hls" --log-path "/logging" --shared-storage + ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public-root "/public" --log-path "/logging" --shared-storage fi /usr/bin/ffplayout -l "0.0.0.0:8787" diff --git a/docker/README.md b/docker/README.md index 71a13e37..3d023f06 100644 --- a/docker/README.md +++ b/docker/README.md @@ -39,7 +39,7 @@ docker build -f nvidia.Dockerfile -t ffplayout-image:nvidia . example of command to start the container: ```BASH -docker run -it -v /path/to/db:/db -v /path/to/storage:/tv-media -v /path/to/playlists:/playlists -v /path/to/hls:/hls -v /path/to/logging:/logging --name ffplayout -p 8787:8787 ffplayout-image +docker run -it -v /path/to/db:/db -v /path/to/storage:/tv-media -v /path/to/playlists:/playlists -v /path/to/public:/public -v /path/to/logging:/logging --name ffplayout -p 8787:8787 ffplayout-image # run in daemon mode docker run -d --name ffplayout -p 8787:8787 ffplayout-image diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2def622b..9bb197e1 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,6 +11,6 @@ services: - ./data/storage:/tv-media - ./data/playlists:/playlists - ./data/logging:/logging - - ./data/hls:/hls + - ./data/public:/public ports: - '8787:8787' diff --git a/docker/nonfree.Dockerfile b/docker/nonfree.Dockerfile index d568bb4e..88c955d1 100644 --- a/docker/nonfree.Dockerfile +++ b/docker/nonfree.Dockerfile @@ -1,6 +1,6 @@ FROM alpine:latest -ARG FFPLAYOUT_VERSION=0.24.0-beta1 +ARG FFPLAYOUT_VERSION=0.24.0-beta2 ARG SHARED_STORAGE=false ENV DB=/db @@ -14,7 +14,7 @@ COPY <<-EOT /run.sh #!/bin/sh if [ ! -f /db/ffplayout.db ]; then - ffplayout -u admin -p admin -m contact@example.com --storage-path "/tv-media" --playlist-path "/playlists" --hls-path "/hls" --log-path "/logging" --shared-storage + ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public-root "/public" --log-path "/logging" --shared-storage fi /usr/bin/ffplayout -l "0.0.0.0:8787" diff --git a/docker/nvidia.Dockerfile b/docker/nvidia.Dockerfile index 71560177..10d668b7 100644 --- a/docker/nvidia.Dockerfile +++ b/docker/nvidia.Dockerfile @@ -1,6 +1,6 @@ FROM nvidia/cuda:12.5.0-runtime-rockylinux9 -ARG FFPLAYOUT_VERSION=0.24.0-beta1 +ARG FFPLAYOUT_VERSION=0.24.0-beta2 ARG SHARED_STORAGE=false ENV DB=/db @@ -204,7 +204,7 @@ COPY <<-EOT /run.sh #!/bin/sh if [ ! -f /db/ffplayout.db ]; then - ffplayout -u admin -p admin -m contact@example.com --storage-path "/tv-media" --playlist-path "/playlists" --hls-path "/hls" --log-path "/logging" --shared-storage + ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public-root "/public" --log-path "/logging" --shared-storage fi /usr/bin/ffplayout -l "0.0.0.0:8787" diff --git a/ffplayout/src/api/routes.rs b/ffplayout/src/api/routes.rs index 0e757e6c..a0938c88 100644 --- a/ffplayout/src/api/routes.rs +++ b/ffplayout/src/api/routes.rs @@ -41,7 +41,7 @@ use tokio::fs; use crate::db::models::Role; use crate::utils::{ channels::{create_channel, delete_channel}, - config::{PlayoutConfig, Template}, + config::{get_config, PlayoutConfig, Template}, control::{control_state, send_message, ControlParams, Process, ProcessCtl}, errors::ServiceError, files::{ @@ -475,9 +475,11 @@ async fn patch_channel( pool: web::Data>, id: web::Path, data: web::Json, + controllers: web::Data>, role: AuthDetails, user: web::ReqData, ) -> Result { + let manager = controllers.lock().unwrap().get(*id).unwrap(); let mut data = data.into_inner(); if !role.has_authority(&Role::GlobalAdmin) { @@ -488,11 +490,11 @@ async fn patch_channel( data.storage_path = channel.storage_path; } - if handles::update_channel(&pool, *id, data).await.is_ok() { - return Ok("Update Success"); - }; + handles::update_channel(&pool, *id, data).await?; + let new_config = get_config(&pool, *id).await?; + manager.update_config(new_config); - Err(ServiceError::InternalServerError) + Ok("Update Success") } /// **Create new Channel** @@ -597,7 +599,7 @@ async fn update_advanced_config( let manager = controllers.lock().unwrap().get(*id).unwrap(); handles::update_advanced_configuration(&pool, *id, data.clone()).await?; - let new_config = PlayoutConfig::new(&pool, *id).await; + let new_config = get_config(&pool, *id).await?; manager.update_config(new_config); @@ -653,7 +655,7 @@ async fn update_playout_config( let config_id = manager.config.lock().unwrap().general.id; handles::update_configuration(&pool, config_id, data.clone()).await?; - let new_config = PlayoutConfig::new(&pool, *id).await; + let new_config = get_config(&pool, *id).await?; manager.update_config(new_config); diff --git a/ffplayout/src/db/handles.rs b/ffplayout/src/db/handles.rs index d6374b3e..713c5f7f 100644 --- a/ffplayout/src/db/handles.rs +++ b/ffplayout/src/db/handles.rs @@ -3,7 +3,6 @@ use argon2::{ Argon2, PasswordHasher, }; -use log::*; use rand::{distributions::Alphanumeric, Rng}; use sqlx::{sqlite::SqliteQueryResult, Pool, Row, Sqlite}; use tokio::task; @@ -13,9 +12,8 @@ use crate::db::models::{Channel, GlobalSettings, Role, TextPreset, User}; use crate::utils::{advanced_config::AdvancedConfig, config::PlayoutConfig, local_utc_offset}; pub async fn db_migrate(conn: &Pool) -> Result<&'static str, Box> { - match sqlx::migrate!("../migrations").run(conn).await { - Ok(_) => info!("Database migration successfully"), - Err(e) => panic!("{e}"), + if let Err(e) = sqlx::migrate!("../migrations").run(conn).await { + panic!("{e}"); } if select_global(conn).await.is_err() { diff --git a/ffplayout/src/db/mod.rs b/ffplayout/src/db/mod.rs index 2f89848b..e6db90fc 100644 --- a/ffplayout/src/db/mod.rs +++ b/ffplayout/src/db/mod.rs @@ -1,3 +1,5 @@ +use std::io::{stdin, stdout, Write}; + use sqlx::{migrate::MigrateDatabase, Pool, Sqlite, SqlitePool}; pub mod handles; @@ -16,3 +18,23 @@ pub async fn db_pool() -> Result, sqlx::Error> { Ok(conn) } + +pub async fn db_drop() { + let mut drop_answer = String::new(); + + print!("Drop Database [Y/n]: "); + stdout().flush().unwrap(); + + stdin() + .read_line(&mut drop_answer) + .expect("Did not enter a yes or no?"); + + let drop = drop_answer.trim().to_lowercase().starts_with('y'); + + if drop { + match Sqlite::drop_database(db_path().unwrap()).await { + Ok(_) => println!("Successfully dropped DB"), + Err(e) => eprintln!("{e}"), + }; + }; +} diff --git a/ffplayout/src/main.rs b/ffplayout/src/main.rs index ae428d6a..78cec9e3 100644 --- a/ffplayout/src/main.rs +++ b/ffplayout/src/main.rs @@ -2,7 +2,7 @@ use std::{ collections::HashSet, env, fs::File, - io::{self, stdin, stdout, Write}, + io, process::exit, sync::{atomic::AtomicBool, Arc, Mutex}, thread, @@ -14,7 +14,6 @@ use actix_web::{ }; use actix_web_grants::authorities::AttachAuthorities; use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; -use sqlx::{migrate::MigrateDatabase, Sqlite}; #[cfg(all(not(debug_assertions), feature = "embed_frontend"))] use actix_web_static_files::ResourceFiles; @@ -25,7 +24,7 @@ use path_clean::PathClean; use ffplayout::{ api::{auth, routes::*}, db::{ - db_pool, handles, + db_drop, db_pool, handles, models::{init_globales, UserMeta}, }, player::{ @@ -36,7 +35,6 @@ use ffplayout::{ utils::{ args_parse::run_args, config::get_config, - db_path, logging::{init_logging, MailQueue}, playlist::generate_playlist, }, @@ -292,23 +290,7 @@ async fn main() -> std::io::Result<()> { Arc::new(AtomicBool::new(false)), ); } else if ARGS.drop_db { - let mut drop_answer = String::new(); - - print!("Drop Database [Y/n]: "); - stdout().flush().unwrap(); - - stdin() - .read_line(&mut drop_answer) - .expect("Did not enter a yes or no?"); - - let drop = drop_answer.trim().to_lowercase().starts_with('y'); - - if drop { - match Sqlite::drop_database(db_path().unwrap()).await { - Ok(_) => info!("Successfully dropped DB"), - Err(e) => error!("{e}"), - }; - } + db_drop().await; } else if !ARGS.init { error!("Run ffplayout with parameters! Run ffplayout -h for more information."); } diff --git a/ffplayout/src/utils/args_parse.rs b/ffplayout/src/utils/args_parse.rs index fb22ccb3..8f1bd9c6 100644 --- a/ffplayout/src/utils/args_parse.rs +++ b/ffplayout/src/utils/args_parse.rs @@ -453,8 +453,39 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { shared_storage: args.shared_storage, }; + let mut channel = handles::select_channel(pool, &1) + .await + .expect("Select Channel 1"); + + if args.shared_storage { + channel.hls_path = Path::new(&global.public_root) + .join("1") + .to_string_lossy() + .to_string(); + channel.playlist_path = Path::new(&global.playlist_root) + .join("1") + .to_string_lossy() + .to_string(); + channel.storage_path = Path::new(&global.storage_root) + .join("1") + .to_string_lossy() + .to_string(); + } else { + channel.hls_path = global.public_root.clone(); + channel.playlist_path = global.playlist_root.clone(); + channel.storage_path = global.storage_root.clone(); + } + match handles::update_global(pool, global.clone()).await { - Ok(_) => println!("Update global paths..."), + Ok(_) => println!("Update globals done..."), + Err(e) => { + eprintln!("{e}"); + error_code = 1; + } + }; + + match handles::update_channel(pool, 1, channel).await { + Ok(_) => println!("Update channel done..."), Err(e) => { eprintln!("{e}"); error_code = 1; diff --git a/ffplayout/src/utils/channels.rs b/ffplayout/src/utils/channels.rs index 7250fb9e..5c7f35c7 100644 --- a/ffplayout/src/utils/channels.rs +++ b/ffplayout/src/utils/channels.rs @@ -11,7 +11,7 @@ use sqlx::{Pool, Sqlite}; use super::logging::MailQueue; use crate::db::{handles, models::Channel}; use crate::player::controller::{ChannelController, ChannelManager}; -use crate::utils::{config::PlayoutConfig, errors::ServiceError}; +use crate::utils::{config::get_config, errors::ServiceError}; async fn map_global_admins(conn: &Pool) -> Result<(), ServiceError> { let channels = handles::select_related_channels(conn, None).await?; @@ -88,7 +88,7 @@ pub async fn create_channel( handles::insert_advanced_configuration(conn, channel.id).await?; handles::insert_configuration(conn, channel.id, output_param).await?; - let config = PlayoutConfig::new(conn, channel.id).await; + let config = get_config(conn, channel.id).await?; let m_queue = Arc::new(Mutex::new(MailQueue::new(channel.id, config.mail.clone()))); let manager = ChannelManager::new(Some(conn.clone()), channel.clone(), config); diff --git a/ffplayout/src/utils/config.rs b/ffplayout/src/utils/config.rs index 3975538a..fe8b6027 100644 --- a/ffplayout/src/utils/config.rs +++ b/ffplayout/src/utils/config.rs @@ -184,12 +184,12 @@ pub struct Channel { } impl Channel { - pub fn new(config: &models::GlobalSettings) -> Self { + pub fn new(config: &models::GlobalSettings, channel: models::Channel) -> Self { Self { logging_path: PathBuf::from(config.logging_path.clone()), - hls_path: PathBuf::from(config.public_root.clone()), - playlist_path: PathBuf::from(config.playlist_root.clone()), - storage_path: PathBuf::from(config.storage_root.clone()), + hls_path: PathBuf::from(channel.hls_path.clone()), + playlist_path: PathBuf::from(channel.playlist_path.clone()), + storage_path: PathBuf::from(channel.storage_path.clone()), shared_storage: config.shared_storage, } } @@ -414,10 +414,12 @@ pub struct Storage { pub filler: PathBuf, pub extensions: Vec, pub shuffle: bool, + #[serde(skip_deserializing)] + pub shared_storage: bool, } impl Storage { - fn new(config: &models::Configuration, path: PathBuf) -> Self { + fn new(config: &models::Configuration, path: PathBuf, shared_storage: bool) -> Self { Self { help_text: config.storage_help.clone(), path, @@ -429,6 +431,7 @@ impl Storage { .map(|s| s.to_string()) .collect(), shuffle: config.storage_shuffle, + shared_storage, } } } @@ -553,6 +556,9 @@ impl PlayoutConfig { let global = handles::select_global(pool) .await .expect("Can't read globals"); + let channel = handles::select_channel(pool, &channel_id) + .await + .expect("Can't read channel"); let config = handles::select_configuration(pool, channel_id) .await .expect("Can't read config"); @@ -560,7 +566,7 @@ impl PlayoutConfig { .await .expect("Can't read advanced config"); - let mut channel = Channel::new(&global); + let channel = Channel::new(&global, channel); let advanced = AdvancedConfig::new(adv_config); let general = General::new(&config); let mail = Mail::new(&config); @@ -572,10 +578,6 @@ impl PlayoutConfig { let task = Task::new(&config); let mut output = Output::new(&config); - if global.shared_storage { - channel.storage_path = channel.storage_path.join(channel_id.to_string()); - } - if !channel.storage_path.is_dir() { tokio::fs::create_dir_all(&channel.storage_path) .await @@ -584,12 +586,8 @@ impl PlayoutConfig { }); } - let mut storage = Storage::new(&config, channel.storage_path.clone()); - - if channel_id > 1 || !global.shared_storage { - channel.playlist_path = channel.playlist_path.join(channel_id.to_string()); - channel.hls_path = channel.hls_path.join(channel_id.to_string()); - } + let mut storage = + Storage::new(&config, channel.storage_path.clone(), global.shared_storage); if !channel.playlist_path.is_dir() { tokio::fs::create_dir_all(&channel.playlist_path) diff --git a/frontend b/frontend index e183320e..fd85411c 160000 --- a/frontend +++ b/frontend @@ -1 +1 @@ -Subproject commit e183320e13bd972d632b841bde722fa7bbed70e5 +Subproject commit fd85411c773f86cd3cef02fdd8c3fd041d9af1a8