diff --git a/.vscode/settings.json b/.vscode/settings.json index b05144c0..4938f010 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -48,12 +48,13 @@ }, "cSpell.words": [ "actix", + "canonicalize", "ffpengine", "flexi", "lettre", "libc", - "nuxt", "neli", + "nuxt", "paris", "reqwest", "rsplit", diff --git a/README.md b/README.md index 98e83604..b35be2c3 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,9 @@ If you are in playlist mode and move backwards or forwards in time, the time shi (Endless) streaming over multiple days will only work if config has a **day_start** value and the **length** value is **24 hours**. If you only need a few hours for each day, use a *cron* job or something similar. +## Note +This project includes the DejaVu font, which are licensed under the [Bitstream Vera Fonts License](/assets/FONT_LICENSE.txt). + ----- ## Sponsoring diff --git a/assets/DejaVuSans.ttf b/assets/DejaVuSans.ttf new file mode 100644 index 00000000..e5f7eecc Binary files /dev/null and b/assets/DejaVuSans.ttf differ diff --git a/assets/FONT_LICENSE.txt b/assets/FONT_LICENSE.txt new file mode 100644 index 00000000..8d719586 --- /dev/null +++ b/assets/FONT_LICENSE.txt @@ -0,0 +1,187 @@ +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +TeX Gyre DJV Math +----------------- +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. + +Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski +(on behalf of TeX users groups) are in public domain. + +Letters imported from Euler Fraktur from AMSfonts are (c) American +Mathematical Society (see below). +Bitstream Vera Fonts Copyright +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera +is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license (“Fonts”) and associated +documentation +files (the “Font Software”), to reproduce and distribute the Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, +and/or sell copies of the Font Software, and to permit persons to whom +the Font Software is furnished to do so, subject to the following +conditions: + +The above copyright and trademark notices and this permission notice +shall be +included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional +glyphs or characters may be added to the Fonts, only if the fonts are +renamed +to names not containing either the words “Bitstream” or the word “Vera”. + +This License becomes null and void to the extent applicable to Fonts or +Font Software +that has been modified and is distributed under the “Bitstream Vera” +names. + +The Font Software may be sold as part of a larger software package but +no copy +of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, +SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN +ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR +INABILITY TO USE +THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. +Except as contained in this notice, the names of GNOME, the GNOME +Foundation, +and Bitstream Inc., shall not be used in advertising or otherwise to promote +the sale, use or other dealings in this Font Software without prior written +authorization from the GNOME Foundation or Bitstream Inc., respectively. +For further information, contact: fonts at gnome dot org. + +AMSFonts (v. 2.2) copyright + +The PostScript Type 1 implementation of the AMSFonts produced by and +previously distributed by Blue Sky Research and Y&Y, Inc. are now freely +available for general use. This has been accomplished through the +cooperation +of a consortium of scientific publishers with Blue Sky Research and Y&Y. +Members of this consortium include: + +Elsevier Science IBM Corporation Society for Industrial and Applied +Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) + +In order to assure the authenticity of these fonts, copyright will be +held by +the American Mathematical Society. This is not meant to restrict in any way +the legitimate use of the fonts, such as (but not limited to) electronic +distribution of documents containing these fonts, inclusion of these fonts +into other public domain or commercial font collections or computer +applications, use of the outline data to create derivative fonts and/or +faces, etc. However, the AMS does require that the AMS copyright notice be +removed from any derivative versions of the fonts which have been altered in +any way. In addition, to ensure the fidelity of TeX documents using Computer +Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, +has requested that any alterations which yield different font metrics be +given a different name. + +$Id$ diff --git a/assets/dummy.vtt b/assets/dummy.vtt new file mode 100644 index 00000000..5ea46166 --- /dev/null +++ b/assets/dummy.vtt @@ -0,0 +1 @@ +WEBVTT diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 42aa9ce5..57a4d848 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -78,7 +78,6 @@ static-files = "0.2" name = "ffplayout" path = "src/main.rs" - # DEBIAN DEB PACKAGE [package.metadata.deb] name = "ffplayout" @@ -99,6 +98,21 @@ assets = [ "/lib/systemd/system/", "644", ], + [ + "../assets/dummy.vtt", + "/usr/share/ffplayout/", + "644", + ], + [ + "../assets/DejaVuSans.ttf", + "/usr/share/ffplayout/", + "644", + ], + [ + "../assets/FONT_LICENSE.txt", + "/usr/share/ffplayout/", + "644", + ], [ "../assets/logo.png", "/usr/share/ffplayout/", @@ -135,6 +149,21 @@ assets = [ "/lib/systemd/system/", "644", ], + [ + "../assets/dummy.vtt", + "/usr/share/ffplayout/", + "644", + ], + [ + "../assets/DejaVuSans.ttf", + "/usr/share/ffplayout/", + "644", + ], + [ + "../assets/FONT_LICENSE.txt", + "/usr/share/ffplayout/", + "644", + ], [ "../assets/logo.png", "/usr/share/ffplayout/", @@ -157,7 +186,7 @@ assets = [ ], ] -# REHL RPM PACKAGE +# RHEL RPM PACKAGE [package.metadata.generate-rpm] name = "ffplayout" license = "GPL-3.0" @@ -167,6 +196,9 @@ assets = [ { source = "../README.md", dest = "/usr/share/doc/ffplayout/README", mode = "644" }, { source = "../assets/ffplayout.1.gz", dest = "/usr/share/man/man1/ffplayout.1.gz", mode = "644", doc = true }, { source = "../LICENSE", dest = "/usr/share/doc/ffplayout/LICENSE", mode = "644" }, + { source = "../assets/dummy.vtt", dest = "/usr/share/ffplayout/dummy.vtt", mode = "644" }, + { source = "../assets/DejaVuSans.ttf", dest = "/usr/share/ffplayout/DejaVuSans.ttf", mode = "644" }, + { source = "../assets/FONT_LICENSE.txt", dest = "/usr/share/ffplayout/FONT_LICENSE.txt", mode = "644" }, { source = "../assets/logo.png", dest = "/usr/share/ffplayout/logo.png", mode = "644" }, { source = "../assets/ffplayout.conf", dest = "/usr/share/ffplayout/ffplayout.conf.example", mode = "644" }, { source = "../debian/postinst", dest = "/usr/share/ffplayout/postinst", mode = "755" }, diff --git a/engine/src/api/routes.rs b/engine/src/api/routes.rs index ed4745bf..7a5ae8ea 100644 --- a/engine/src/api/routes.rs +++ b/engine/src/api/routes.rs @@ -480,7 +480,11 @@ async fn patch_channel( role: AuthDetails, user: web::ReqData, ) -> Result { - let manager = controllers.lock().unwrap().get(*id).unwrap(); + let manager = controllers + .lock() + .unwrap() + .get(*id) + .ok_or(format!("Channel {id} not found!"))?; let mut data = data.into_inner(); if !role.has_authority(&Role::GlobalAdmin) { diff --git a/engine/src/db/models.rs b/engine/src/db/models.rs index 88ba5eb0..ca97b726 100644 --- a/engine/src/db/models.rs +++ b/engine/src/db/models.rs @@ -302,6 +302,10 @@ pub struct Configuration { pub processing_volume: f64, #[serde(default)] pub processing_filter: String, + #[serde(default)] + pub processing_vtt_enable: bool, + #[serde(default)] + pub processing_vtt_dummy: Option, pub ingest_help: String, pub ingest_enable: bool, @@ -375,6 +379,8 @@ impl Configuration { processing_audio_channels: config.processing.audio_channels, processing_volume: config.processing.volume, processing_filter: config.processing.custom_filter, + processing_vtt_enable: config.processing.vtt_enable, + processing_vtt_dummy: config.processing.vtt_dummy, ingest_help: config.ingest.help_text, ingest_enable: config.ingest.enable, ingest_param: config.ingest.input_param, diff --git a/engine/src/main.rs b/engine/src/main.rs index 003db364..812cf7b8 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -199,6 +199,8 @@ async fn main() -> std::io::Result<()> { .workers(thread_count) .run() .await?; + } else if ARGS.drop_db { + db_drop().await; } else { let channels = ARGS.channels.clone().unwrap_or_else(|| vec![1]); @@ -267,8 +269,6 @@ async fn main() -> std::io::Result<()> { playlist, Arc::new(AtomicBool::new(false)), ); - } else if ARGS.drop_db { - db_drop().await; } else if !ARGS.init { error!("Run ffplayout with parameters! Run ffplayout -h for more information."); } diff --git a/engine/src/utils/args_parse.rs b/engine/src/utils/args_parse.rs index 5d7d7908..43351f40 100644 --- a/engine/src/utils/args_parse.rs +++ b/engine/src/utils/args_parse.rs @@ -4,11 +4,12 @@ use std::{ }; #[cfg(target_family = "unix")] -use std::{fs, process::exit}; +use std::process::exit; use clap::Parser; use rpassword::read_password; use sqlx::{Pool, Sqlite}; +use tokio::fs; use crate::db::{ handles, @@ -17,6 +18,7 @@ use crate::db::{ use crate::utils::{ advanced_config::AdvancedConfig, config::{OutputMode, PlayoutConfig}, + copy_assets, }; use crate::ARGS; @@ -197,6 +199,13 @@ fn global_user(args: &mut Args) { } pub async fn run_args(pool: &Pool) -> Result<(), i32> { + let mut user = None; + let mut fix_permission = false; + + if cfg!(target_family = "unix") { + user = nix::unistd::User::from_name("ffpu").unwrap_or_default(); + } + let mut args = ARGS.clone(); if !args.dump_advanced && !args.dump_config && !args.drop_db { @@ -212,18 +221,12 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { let mut error_code = -1; if args.init { - #[cfg(target_family = "unix")] - let process_user = nix::unistd::User::from_name("ffpu").unwrap_or_default(); - - #[cfg(target_family = "unix")] - let mut fix_permission = false; - #[cfg(target_family = "unix")] { let uid = nix::unistd::Uid::current(); let current_user = nix::unistd::User::from_uid(uid).unwrap_or_default(); - if current_user != process_user { + if current_user != user { let user_name = current_user.unwrap().name; let mut fix_perm = String::new(); @@ -353,7 +356,11 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { channel.playlist_path = global.playlist_root; channel.storage_path = global.storage_root; + let mut storage_path = PathBuf::from(channel.storage_path.clone()); + if global.shared_storage { + storage_path = storage_path.join("1"); + channel.preview_url = "http://127.0.0.1:8787/1/stream.m3u8".to_string(); channel.hls_path = Path::new(&channel.hls_path) .join("1") @@ -363,22 +370,27 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { .join("1") .to_string_lossy() .to_string(); - channel.storage_path = Path::new(&channel.storage_path) - .join("1") - .to_string_lossy() - .to_string(); + channel.storage_path = storage_path.to_string_lossy().to_string(); + }; + + if let Err(e) = copy_assets(&storage_path, fix_permission, user.clone()).await { + eprintln!("{e}"); }; handles::update_channel(pool, 1, channel).await.unwrap(); #[cfg(target_family = "unix")] if fix_permission { - let db_path = Path::new(db_path().unwrap()).with_extension(""); - let user = process_user.unwrap(); + let user = user.clone().unwrap(); + let db_path = Path::new(db_path().unwrap()); - let db = fs::canonicalize(db_path.with_extension("db")).unwrap(); - let shm = fs::canonicalize(db_path.with_extension("db-shm")).unwrap(); - let wal = fs::canonicalize(db_path.with_extension("db-wal")).unwrap(); + let db = fs::canonicalize(db_path).await.unwrap(); + let shm = fs::canonicalize(db_path.with_extension("db-shm")) + .await + .unwrap(); + let wal = fs::canonicalize(db_path.with_extension("db-wal")) + .await + .unwrap(); nix::unistd::chown(&db, Some(user.uid), Some(user.gid)).expect("Change DB owner"); @@ -410,7 +422,7 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { let chl: Vec = channels.clone().iter().map(|c| c.id).collect(); - let user = User { + let ff_user = User { id: 0, mail: Some(args.mail.unwrap()), username: username.clone(), @@ -420,7 +432,7 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { token: None, }; - if let Err(e) = handles::insert_user(pool, user).await { + if let Err(e) = handles::insert_user(pool, ff_user).await { eprintln!("{e}"); error_code = 1; }; @@ -449,8 +461,11 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { let mut channel = handles::select_channel(pool, &1) .await .expect("Select Channel 1"); + let mut storage_path = PathBuf::from(global.storage_root.clone()); if args.shared_storage { + storage_path = storage_path.join("1"); + channel.hls_path = Path::new(&global.public_root) .join("1") .to_string_lossy() @@ -459,16 +474,17 @@ pub async fn run_args(pool: &Pool) -> Result<(), i32> { .join("1") .to_string_lossy() .to_string(); - channel.storage_path = Path::new(&global.storage_root) - .join("1") - .to_string_lossy() - .to_string(); + channel.storage_path = storage_path.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(); } + if let Err(e) = copy_assets(&storage_path, false, user).await { + eprintln!("{e}"); + }; + match handles::update_global(pool, global.clone()).await { Ok(_) => println!("Update globals done..."), Err(e) => { diff --git a/engine/src/utils/channels.rs b/engine/src/utils/channels.rs index ac48a924..b1ec3359 100644 --- a/engine/src/utils/channels.rs +++ b/engine/src/utils/channels.rs @@ -1,7 +1,6 @@ use std::{ - ffi::OsStr, io, - path::Path, + path::PathBuf, sync::{Arc, Mutex}, }; @@ -11,7 +10,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::get_config, errors::ServiceError}; +use crate::utils::{config::get_config, copy_assets, errors::ServiceError}; async fn map_global_admins(conn: &Pool) -> Result<(), ServiceError> { let channels = handles::select_related_channels(conn, None).await?; @@ -29,58 +28,32 @@ async fn map_global_admins(conn: &Pool) -> Result<(), ServiceError> { Ok(()) } -fn preview_url(url: &str, id: i32) -> String { - let url_path = Path::new(url); - - if let Some(parent) = url_path.parent() { - if let Some(filename) = url_path.file_name() { - let new_path = if parent - .file_name() - .unwrap_or_else(|| OsStr::new("0")) - .to_string_lossy() - .to_string() - .parse::() - .is_ok() - { - parent.join(filename) - } else { - parent.join(id.to_string()).join(filename) - }; - - if let Some(new_url) = new_path.to_str() { - return new_url.to_string(); - } - } - } - url.to_string() -} - pub async fn create_channel( conn: &Pool, controllers: Arc>, queue: Arc>>>>, target_channel: Channel, ) -> Result { - let global = handles::select_global(conn).await?; - let mut channel = handles::insert_channel(conn, target_channel).await?; + let channel = handles::insert_channel(conn, target_channel).await?; + let storage_path = PathBuf::from(channel.storage_path.clone()); + let mut user = None; + let mut fix_permission = false; + + if cfg!(target_family = "unix") { + user = nix::unistd::User::from_name("ffpu").unwrap_or_default(); + let uid = nix::unistd::Uid::current(); + let current_user = nix::unistd::User::from_uid(uid).unwrap_or_default(); + + if current_user.unwrap().name == "root" { + fix_permission = true; + }; + } + handles::new_channel_presets(conn, channel.id).await?; - channel.preview_url = preview_url(&channel.preview_url, channel.id); - - if global.shared_storage { - channel.hls_path = Path::new(&global.public_root) - .join(channel.id.to_string()) - .to_string_lossy() - .to_string(); - channel.playlist_path = Path::new(&global.playlist_root) - .join(channel.id.to_string()) - .to_string_lossy() - .to_string(); - channel.storage_path = Path::new(&global.storage_root) - .join(channel.id.to_string()) - .to_string_lossy() - .to_string(); - } + if let Err(e) = copy_assets(&storage_path, fix_permission, user).await { + error!("{e}"); + }; handles::update_channel(conn, channel.id, channel.clone()).await?; diff --git a/engine/src/utils/config.rs b/engine/src/utils/config.rs index 03e21427..107714ff 100644 --- a/engine/src/utils/config.rs +++ b/engine/src/utils/config.rs @@ -328,6 +328,8 @@ pub struct Processing { pub audio_channels: u8, pub volume: f64, pub custom_filter: String, + pub vtt_enable: bool, + pub vtt_dummy: Option, #[serde(skip_serializing, skip_deserializing)] pub cmd: Option>, } @@ -355,6 +357,8 @@ impl Processing { audio_channels: config.processing_audio_channels, volume: config.processing_volume, custom_filter: config.processing_filter.clone(), + vtt_enable: config.processing_vtt_enable, + vtt_dummy: config.processing_vtt_dummy.clone(), cmd: None, } } diff --git a/engine/src/utils/mod.rs b/engine/src/utils/mod.rs index 3c6d3d69..b2d19de6 100644 --- a/engine/src/utils/mod.rs +++ b/engine/src/utils/mod.rs @@ -310,3 +310,54 @@ pub fn round_to_nearest_ten(num: i64) -> i64 { (num / 10) * 10 } } + +pub async fn copy_assets( + storage_path: &Path, + fix_permission: bool, + user: Option, +) -> Result<(), std::io::Error> { + if storage_path.is_dir() { + let target = storage_path.join("00-assets"); + let mut dummy_source = Path::new("/usr/share/ffplayout/dummy.vtt"); + let mut font_source = Path::new("/usr/share/ffplayout/DejaVuSans.ttf"); + let mut logo_source = Path::new("/usr/share/ffplayout/logo.png"); + + if !dummy_source.is_file() { + dummy_source = Path::new("./assets/dummy.vtt") + } + if !font_source.is_file() { + font_source = Path::new("./assets/DejaVuSans.ttf") + } + if !logo_source.is_file() { + logo_source = Path::new("./assets/logo.png") + } + + if !target.is_dir() { + let dummy_target = target.join("dummy.vtt"); + let font_target = target.join("DejaVuSans.ttf"); + let logo_target = target.join("logo.png"); + + fs::create_dir(&target).await?; + fs::copy(&dummy_source, &dummy_target).await?; + fs::copy(&font_source, &font_target).await?; + fs::copy(&logo_source, &logo_target).await?; + + #[cfg(target_family = "unix")] + if fix_permission { + let user = user.unwrap(); + + if dummy_target.is_file() { + nix::unistd::chown(&dummy_target, Some(user.uid), Some(user.gid))?; + } + if font_target.is_file() { + nix::unistd::chown(&font_target, Some(user.uid), Some(user.gid))?; + } + if logo_target.is_file() { + nix::unistd::chown(&logo_target, Some(user.uid), Some(user.gid))?; + } + } + } + } + + Ok(()) +} diff --git a/frontend/components/ConfigChannel.vue b/frontend/components/ConfigChannel.vue index 7a67d0e5..60c0c90d 100644 --- a/frontend/components/ConfigChannel.vue +++ b/frontend/components/ConfigChannel.vue @@ -120,7 +120,7 @@ function newChannel() { newChannel.id = channels.length + 1 newChannel.name = `Channel ${newChannel.id}` - newChannel.preview_url = `${window.location.protocol}//${window.location.host}/live/${newChannel.id}/stream.m3u8` + newChannel.preview_url = `${window.location.protocol}//${window.location.host}/${newChannel.id}/live/stream.m3u8` newChannel.hls_path = `${rmId(newChannel.hls_path)}/${newChannel.id}` newChannel.playlist_path = `${rmId(newChannel.playlist_path)}/${newChannel.id}` newChannel.storage_path = `${rmId(newChannel.storage_path)}/${newChannel.id}` @@ -158,6 +158,7 @@ async function deleteChannel() { }) config.splice(configStore.id, 1) + configStore.channelsRaw.splice(configStore.id, 1) configStore.channels = config configStore.id = configStore.channels.length - 1 diff --git a/frontend/components/ConfigPlayout.vue b/frontend/components/ConfigPlayout.vue index 9f034c38..0bbe2a3a 100644 --- a/frontend/components/ConfigPlayout.vue +++ b/frontend/components/ConfigPlayout.vue @@ -15,7 +15,15 @@ class="form-control w-full" :class="[typeof prop === 'boolean' && 'flex-row', name.toString() !== 'help_text' && 'mt-2']" > -