unify init process, simplify argument names and model values

This commit is contained in:
Jonathan Baecker 2024-09-30 17:56:48 +02:00
parent 7463512fa6
commit 2f5911fd35
36 changed files with 274 additions and 336 deletions

View File

@ -61,6 +61,7 @@
"reqwest", "reqwest",
"rsplit", "rsplit",
"rustls", "rustls",
"sqlite",
"sqlx", "sqlx",
"starttls", "starttls",
"tokio", "tokio",

View File

@ -12,7 +12,7 @@ COPY <<-EOT /run.sh
#!/bin/sh #!/bin/sh
if [ ! -f /db/ffplayout.db ]; then if [ ! -f /db/ffplayout.db ]; then
ffplayout -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared-storage ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared
fi fi
/usr/bin/ffplayout -l "0.0.0.0:8787" /usr/bin/ffplayout -l "0.0.0.0:8787"

View File

@ -14,7 +14,7 @@ COPY <<-EOT /run.sh
#!/bin/sh #!/bin/sh
if [ ! -f /db/ffplayout.db ]; then if [ ! -f /db/ffplayout.db ]; then
ffplayout -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared-storage ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared
fi fi
/usr/bin/ffplayout -l "0.0.0.0:8787" /usr/bin/ffplayout -l "0.0.0.0:8787"

View File

@ -204,7 +204,7 @@ COPY <<-EOT /run.sh
#!/bin/sh #!/bin/sh
if [ ! -f /db/ffplayout.db ]; then if [ ! -f /db/ffplayout.db ]; then
ffplayout -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared-storage ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared
fi fi
/usr/bin/ffplayout -l "0.0.0.0:8787" /usr/bin/ffplayout -l "0.0.0.0:8787"

View File

@ -1,23 +1,19 @@
### Install ffplayout ### Install ffplayout
**Note:** This is the official and supported way.
ffplayout provides ***.deb** and ***.rpm** packages, which makes it more easy to install and use, but there is still some steps to do. ffplayout provides ***.deb** and ***.rpm** packages, which makes it more easy to install and use, but there is still some steps to do.
1. download the latest ffplayout from [release](https://github.com/ffplayout/ffplayout/releases/latest) page and place the package in the **/tmp** folder. 1. download the latest ffplayout from [release](https://github.com/ffplayout/ffplayout/releases/latest) page and place the package in the **/tmp** folder.
2. install it with `apt install /tmp/ffplayout_<VERSION>_amd64.deb` 2. install it with `apt install /tmp/ffplayout_<VERSION>_amd64.deb`
3. install ffmpeg/ffprobe, or compile and copy it to **/usr/local/bin/** 3. install ffmpeg/ffprobe, or compile and copy it to **/usr/local/bin/**
4. activate systemd services: 4. initial defaults and add global admin user: `sudo -u ffpu ffplayout -i`
- `systemctl enable ffplayout` 5. use a revers proxy for SSL, Port is **8787**.
5. initial defaults and add global admin user: 6. login with your browser, address without proxy would be: **http://[IP ADDRESS]:8787**
- `sudo -u ffpu ffplayout -i`
6. start ffplayout:
- `systemctl start ffplayout`
7. use a revers proxy for SSL, Port is **8787**.
8. login with your browser, address without proxy would be: **http://[IP ADDRESS]:8787**
Default location for playlists and media files are: **/var/lib/ffplayout/**.
### Manual Install ### Manual Install
-----
**Note:** This is for advanced user only.
- install ffmpeg/ffprobe, or compile and copy it to **/usr/local/bin/** - install ffmpeg/ffprobe, or compile and copy it to **/usr/local/bin/**
- download the latest archive from [release](https://github.com/ffplayout/ffplayout/releases/latest) page - download the latest archive from [release](https://github.com/ffplayout/ffplayout/releases/latest) page

View File

@ -135,7 +135,7 @@ assets = [
], ],
] ]
maintainer-scripts = "../debian/" maintainer-scripts = "../debian/"
systemd-units = { enable = false, unit-scripts = "../assets" } systemd-units = { enable = true, unit-scripts = "../assets" }
[package.metadata.deb.variants.arm64] [package.metadata.deb.variants.arm64]
assets = [ assets = [

View File

@ -490,9 +490,9 @@ async fn patch_channel(
if !role.has_authority(&Role::GlobalAdmin) { if !role.has_authority(&Role::GlobalAdmin) {
let channel = handles::select_channel(&pool, &id).await?; let channel = handles::select_channel(&pool, &id).await?;
data.hls_path = channel.hls_path; data.public = channel.public;
data.playlist_path = channel.playlist_path; data.playlists = channel.playlists;
data.storage_path = channel.storage_path; data.storage = channel.storage;
} }
handles::update_channel(&pool, *id, data).await?; handles::update_channel(&pool, *id, data).await?;
@ -669,13 +669,13 @@ async fn update_playout_config(
user: web::ReqData<UserMeta>, user: web::ReqData<UserMeta>,
) -> Result<impl Responder, ServiceError> { ) -> Result<impl Responder, ServiceError> {
let manager = controllers.lock().unwrap().get(*id).unwrap(); let manager = controllers.lock().unwrap().get(*id).unwrap();
let p = manager.channel.lock().unwrap().storage_path.clone(); let p = manager.channel.lock().unwrap().storage.clone();
let storage_path = Path::new(&p); let storage = Path::new(&p);
let config_id = manager.config.lock().unwrap().general.id; let config_id = manager.config.lock().unwrap().general.id;
let (_, _, logo) = norm_abs_path(storage_path, &data.processing.logo)?; let (_, _, logo) = norm_abs_path(storage, &data.processing.logo)?;
let (_, _, filler) = norm_abs_path(storage_path, &data.storage.filler)?; let (_, _, filler) = norm_abs_path(storage, &data.storage.filler)?;
let (_, _, font) = norm_abs_path(storage_path, &data.text.font)?; let (_, _, font) = norm_abs_path(storage, &data.text.font)?;
data.processing.logo = logo; data.processing.logo = logo;
data.storage.filler = filler; data.storage.filler = filler;
@ -1064,14 +1064,14 @@ pub async fn gen_playlist(
) -> Result<impl Responder, ServiceError> { ) -> Result<impl Responder, ServiceError> {
let manager = controllers.lock().unwrap().get(params.0).unwrap(); let manager = controllers.lock().unwrap().get(params.0).unwrap();
manager.config.lock().unwrap().general.generate = Some(vec![params.1.clone()]); manager.config.lock().unwrap().general.generate = Some(vec![params.1.clone()]);
let storage_path = manager.config.lock().unwrap().channel.storage_path.clone(); let storage = manager.config.lock().unwrap().channel.storage.clone();
if let Some(obj) = data { if let Some(obj) = data {
if let Some(paths) = &obj.paths { if let Some(paths) = &obj.paths {
let mut path_list = vec![]; let mut path_list = vec![];
for path in paths { for path in paths {
let (p, _, _) = norm_abs_path(&storage_path, path)?; let (p, _, _) = norm_abs_path(&storage, path)?;
path_list.push(p); path_list.push(p);
} }
@ -1306,9 +1306,9 @@ async fn get_file(
let id: i32 = req.match_info().query("id").parse()?; let id: i32 = req.match_info().query("id").parse()?;
let manager = controllers.lock().unwrap().get(id).unwrap(); let manager = controllers.lock().unwrap().get(id).unwrap();
let config = manager.config.lock().unwrap(); let config = manager.config.lock().unwrap();
let storage_path = config.channel.storage_path.clone(); let storage = config.channel.storage.clone();
let file_path = req.match_info().query("filename"); let file_path = req.match_info().query("filename");
let (path, _, _) = norm_abs_path(&storage_path, file_path)?; let (path, _, _) = norm_abs_path(&storage, file_path)?;
let file = actix_files::NamedFile::open(path)?; let file = actix_files::NamedFile::open(path)?;
Ok(file Ok(file
@ -1339,7 +1339,7 @@ async fn get_public(
{ {
let manager = controllers.lock().unwrap().get(id).unwrap(); let manager = controllers.lock().unwrap().get(id).unwrap();
let config = manager.config.lock().unwrap(); let config = manager.config.lock().unwrap();
config.channel.hls_path.join(public) config.channel.public.join(public)
} else { } else {
public_path() public_path()
} }

View File

@ -36,7 +36,8 @@ pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<(), Box<dyn std::error::E
} }
pub async fn select_global(conn: &Pool<Sqlite>) -> Result<GlobalSettings, sqlx::Error> { pub async fn select_global(conn: &Pool<Sqlite>) -> Result<GlobalSettings, sqlx::Error> {
let query = "SELECT id, secret, logging_path, playlist_root, public_root, storage_root, shared_storage FROM global WHERE id = 1"; let query =
"SELECT id, secret, logs, playlists, public, storage, shared FROM global WHERE id = 1";
sqlx::query_as(query).fetch_one(conn).await sqlx::query_as(query).fetch_one(conn).await
} }
@ -45,15 +46,15 @@ pub async fn update_global(
conn: &Pool<Sqlite>, conn: &Pool<Sqlite>,
global: GlobalSettings, global: GlobalSettings,
) -> Result<SqliteQueryResult, sqlx::Error> { ) -> Result<SqliteQueryResult, sqlx::Error> {
let query = "UPDATE global SET logging_path = $2, playlist_root = $3, public_root = $4, storage_root = $5, shared_storage = $6 WHERE id = 1"; let query = "UPDATE global SET logs = $2, playlists = $3, public = $4, storage = $5, shared = $6 WHERE id = 1";
sqlx::query(query) sqlx::query(query)
.bind(global.id) .bind(global.id)
.bind(global.logging_path) .bind(global.logs)
.bind(global.playlist_root) .bind(global.playlists)
.bind(global.public_root) .bind(global.public)
.bind(global.storage_root) .bind(global.storage)
.bind(global.shared_storage) .bind(global.shared)
.execute(conn) .execute(conn)
.await .await
} }
@ -73,7 +74,7 @@ pub async fn select_related_channels(
) -> Result<Vec<Channel>, sqlx::Error> { ) -> Result<Vec<Channel>, sqlx::Error> {
let query = match user_id { let query = match user_id {
Some(id) => format!( Some(id) => format!(
"SELECT c.id, c.name, c.preview_url, c.extra_extensions, c.active, c.hls_path, c.playlist_path, c.storage_path, c.last_date, c.time_shift FROM channels c "SELECT c.id, c.name, c.preview_url, c.extra_extensions, c.active, c.public, c.playlists, c.storage, c.last_date, c.time_shift FROM channels c
left join user_channels uc on uc.channel_id = c.id left join user_channels uc on uc.channel_id = c.id
left join user u on u.id = uc.user_id left join user u on u.id = uc.user_id
WHERE u.id = {id} ORDER BY c.id ASC;" WHERE u.id = {id} ORDER BY c.id ASC;"
@ -110,16 +111,16 @@ pub async fn update_channel(
channel: Channel, channel: Channel,
) -> Result<SqliteQueryResult, sqlx::Error> { ) -> Result<SqliteQueryResult, sqlx::Error> {
let query = let query =
"UPDATE channels SET name = $2, preview_url = $3, extra_extensions = $4, hls_path = $5, playlist_path = $6, storage_path = $7 WHERE id = $1"; "UPDATE channels SET name = $2, preview_url = $3, extra_extensions = $4, public = $5, playlists = $6, storage = $7 WHERE id = $1";
sqlx::query(query) sqlx::query(query)
.bind(id) .bind(id)
.bind(channel.name) .bind(channel.name)
.bind(channel.preview_url) .bind(channel.preview_url)
.bind(channel.extra_extensions) .bind(channel.extra_extensions)
.bind(channel.hls_path) .bind(channel.public)
.bind(channel.playlist_path) .bind(channel.playlists)
.bind(channel.storage_path) .bind(channel.storage)
.execute(conn) .execute(conn)
.await .await
} }
@ -151,14 +152,14 @@ pub async fn update_player(
} }
pub async fn insert_channel(conn: &Pool<Sqlite>, channel: Channel) -> Result<Channel, sqlx::Error> { pub async fn insert_channel(conn: &Pool<Sqlite>, channel: Channel) -> Result<Channel, sqlx::Error> {
let query = "INSERT INTO channels (name, preview_url, extra_extensions, hls_path, playlist_path, storage_path) VALUES($1, $2, $3, $4, $5, $6)"; let query = "INSERT INTO channels (name, preview_url, extra_extensions, public, playlists, storage) VALUES($1, $2, $3, $4, $5, $6)";
let result = sqlx::query(query) let result = sqlx::query(query)
.bind(channel.name) .bind(channel.name)
.bind(channel.preview_url) .bind(channel.preview_url)
.bind(channel.extra_extensions) .bind(channel.extra_extensions)
.bind(channel.hls_path) .bind(channel.public)
.bind(channel.playlist_path) .bind(channel.playlists)
.bind(channel.storage_path) .bind(channel.storage)
.execute(conn) .execute(conn)
.await?; .await?;

View File

@ -16,11 +16,11 @@ use crate::utils::config::PlayoutConfig;
pub struct GlobalSettings { pub struct GlobalSettings {
pub id: i32, pub id: i32,
pub secret: Option<String>, pub secret: Option<String>,
pub logging_path: String, pub logs: String,
pub playlist_root: String, pub playlists: String,
pub public_root: String, pub public: String,
pub storage_root: String, pub storage: String,
pub shared_storage: bool, pub shared: bool,
} }
impl GlobalSettings { impl GlobalSettings {
@ -32,11 +32,11 @@ impl GlobalSettings {
Err(_) => GlobalSettings { Err(_) => GlobalSettings {
id: 0, id: 0,
secret: None, secret: None,
logging_path: String::new(), logs: String::new(),
playlist_root: String::new(), playlists: String::new(),
public_root: String::new(), public: String::new(),
storage_root: String::new(), storage: String::new(),
shared_storage: false, shared: false,
}, },
} }
} }
@ -61,9 +61,9 @@ pub struct Channel {
pub preview_url: String, pub preview_url: String,
pub extra_extensions: String, pub extra_extensions: String,
pub active: bool, pub active: bool,
pub hls_path: String, pub public: String,
pub playlist_path: String, pub playlists: String,
pub storage_path: String, pub storage: String,
pub last_date: Option<String>, pub last_date: Option<String>,
pub time_shift: f64, pub time_shift: f64,

View File

@ -243,7 +243,7 @@ async fn main() -> std::io::Result<()> {
exit(1); exit(1);
}; };
} else if ARGS.validate { } else if ARGS.validate {
let mut playlist_path = config.channel.playlist_path.clone(); let mut playlist_path = config.channel.playlists.clone();
let start_sec = config.playlist.start_sec.unwrap(); let start_sec = config.playlist.start_sec.unwrap();
let date = get_date(false, start_sec, false); let date = get_date(false, start_sec, false);

View File

@ -349,7 +349,7 @@ pub fn start_channel(manager: ChannelManager) -> Result<(), ProcessError> {
let channel_id = config.general.channel_id; let channel_id = config.general.channel_id;
drain_hls_path( drain_hls_path(
&config.channel.hls_path, &config.channel.public,
&config.output.output_cmd.clone().unwrap_or_default(), &config.output.output_cmd.clone().unwrap_or_default(),
channel_id, channel_id,
)?; )?;

View File

@ -29,7 +29,7 @@ pub fn watchman(
sources: Arc<Mutex<Vec<Media>>>, sources: Arc<Mutex<Vec<Media>>>,
) { ) {
let id = config.general.channel_id; let id = config.general.channel_id;
let path = Path::new(&config.channel.storage_path); let path = Path::new(&config.channel.storage);
if !path.exists() { if !path.exists() {
error!(target: Target::file_mail(), channel = id; "Folder path not exists: '{path:?}'"); error!(target: Target::file_mail(), channel = id; "Folder path not exists: '{path:?}'");

View File

@ -75,7 +75,7 @@ pub fn ingest_server(
let ingest_is_running = channel_mgr.ingest_is_running.clone(); let ingest_is_running = channel_mgr.ingest_is_running.clone();
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if let Some(ingest_input_cmd) = config.advanced.ingest.input_cmd { if let Some(ingest_input_cmd) = config.advanced.ingest.input_cmd {

View File

@ -28,7 +28,7 @@ pub fn source_generator(manager: ChannelManager) -> Box<dyn Iterator<Item = Medi
info!(target: Target::file_mail(), channel = id; "Playout in folder mode"); info!(target: Target::file_mail(), channel = id; "Playout in folder mode");
debug!(target: Target::file_mail(), channel = id; debug!(target: Target::file_mail(), channel = id;
"Monitor folder: <b><magenta>{:?}</></b>", "Monitor folder: <b><magenta>{:?}</></b>",
config.channel.storage_path config.channel.storage
); );
let config_clone = config.clone(); let config_clone = config.clone();

View File

@ -314,14 +314,11 @@ impl CurrentProgram {
self.current_node = self.current_node =
handle_list_init(&self.config, node_clone, &self.manager, last_index); handle_list_init(&self.config, node_clone, &self.manager, last_index);
if self.current_node.source.contains( if self
&self .current_node
.config .source
.channel .contains(&self.config.channel.storage.to_string_lossy().to_string())
.storage_path || self.current_node.source.contains("color=c=#121212")
.to_string_lossy()
.to_string(),
) || self.current_node.source.contains("color=c=#121212")
{ {
is_filler = true; is_filler = true;
} }

View File

@ -65,7 +65,7 @@ fn ingest_to_hls_server(manager: ChannelManager) -> Result<(), ProcessError> {
if config.processing.vtt_enable { if config.processing.vtt_enable {
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_dummy.is_file() { if vtt_dummy.is_file() {

View File

@ -40,7 +40,7 @@ impl FolderSource {
path_list.push(path) path_list.push(path)
} }
} else { } else {
path_list.push(&config.channel.storage_path) path_list.push(&config.channel.storage)
} }
for path in &path_list { for path in &path_list {

View File

@ -28,13 +28,13 @@ pub fn import_file(
program: vec![], program: vec![],
}; };
let playlist_root = &config.channel.playlist_path; let playlist_root = &config.channel.playlists;
if !playlist_root.is_dir() { if !playlist_root.is_dir() {
return Err(Error::new( return Err(Error::new(
ErrorKind::Other, ErrorKind::Other,
format!( format!(
"Playlist folder <b><magenta>{:?}</></b> not exists!", "Playlist folder <b><magenta>{:?}</></b> not exists!",
config.channel.playlist_path, config.channel.playlists,
), ),
)); ));
} }

View File

@ -100,11 +100,11 @@ pub fn read_json(
) -> JsonPlaylist { ) -> JsonPlaylist {
let id = config.general.channel_id; let id = config.general.channel_id;
let config_clone = config.clone(); let config_clone = config.clone();
let mut playlist_path = config.channel.playlist_path.clone(); let mut playlist_path = config.channel.playlists.clone();
let start_sec = config.playlist.start_sec.unwrap(); let start_sec = config.playlist.start_sec.unwrap();
let date = get_date(seek, start_sec, get_next); let date = get_date(seek, start_sec, get_next);
if playlist_path.is_dir() || is_remote(&config.channel.playlist_path.to_string_lossy()) { if playlist_path.is_dir() || is_remote(&config.channel.playlists.to_string_lossy()) {
let d: Vec<&str> = date.split('-').collect(); let d: Vec<&str> = date.split('-').collect();
playlist_path = playlist_path playlist_path = playlist_path
.join(d[0]) .join(d[0])

View File

@ -69,7 +69,7 @@ pub fn prepare_output_cmd(
let re_v = Regex::new(r"\[?0:v(:0)?\]?").unwrap(); let re_v = Regex::new(r"\[?0:v(:0)?\]?").unwrap();
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if let Some(mut filter) = filters.clone() { if let Some(mut filter) = filters.clone() {
@ -622,7 +622,7 @@ pub fn loop_image(config: &PlayoutConfig, node: &Media) -> Vec<String> {
let vtt_file = Path::new(&node.source).with_extension("vtt"); let vtt_file = Path::new(&node.source).with_extension("vtt");
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if node.seek > 0.5 { if node.seek > 0.5 {
@ -663,7 +663,7 @@ pub fn loop_filler(config: &PlayoutConfig, node: &Media) -> Vec<String> {
let vtt_file = Path::new(&node.source).with_extension("vtt"); let vtt_file = Path::new(&node.source).with_extension("vtt");
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_file.is_file() { if vtt_file.is_file() {
@ -737,7 +737,7 @@ pub fn seek_and_length(config: &PlayoutConfig, node: &mut Media) -> Vec<String>
let vtt_file = Path::new(&node.source).with_extension("vtt"); let vtt_file = Path::new(&node.source).with_extension("vtt");
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if node.seek > 0.5 { if node.seek > 0.5 {
@ -788,7 +788,7 @@ pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec<String>)
if config.processing.vtt_enable { if config.processing.vtt_enable {
let vtt_dummy = config let vtt_dummy = config
.channel .channel
.storage_path .storage
.join(config.processing.vtt_dummy.clone().unwrap_or_default()); .join(config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_dummy.is_file() { if vtt_dummy.is_file() {

View File

@ -45,7 +45,7 @@ pub struct Args {
)] )]
pub init: bool, pub init: bool,
#[clap(short, long, help_heading = Some("Initial Setup"), help = "Add a global admin user")] #[clap(short, long, help_heading = Some("Initial Setup"), help = "Add a global admin")]
pub add: bool, pub add: bool,
#[clap(short, long, help_heading = Some("Initial Setup"), help = "Create admin user")] #[clap(short, long, help_heading = Some("Initial Setup"), help = "Create admin user")]
@ -66,16 +66,16 @@ pub struct Args {
help_heading = Some("Initial Setup"), help_heading = Some("Initial Setup"),
help = "Share storage across channels, important for running in Containers" help = "Share storage across channels, important for running in Containers"
)] )]
pub shared_storage: bool, pub shared: bool,
#[clap(long, env, help_heading = Some("Initial Setup / General"), help = "Logging path")] #[clap(long, env, help_heading = Some("Initial Setup / General"), help = "Logging path")]
pub log_path: Option<PathBuf>, pub logs: Option<String>,
#[clap(long, env, help_heading = Some("Initial Setup / General"), help = "Path to public files, also HLS playlists")] #[clap(long, env, help_heading = Some("Initial Setup / General"), help = "Path to public files, also HLS playlists")]
pub public: Option<String>, pub public: Option<String>,
#[clap(long, help_heading = Some("Initial Setup / Playlist"), help = "Path to playlist, or playlist root folder.")] #[clap(long, help_heading = Some("Initial Setup / Playlist"), help = "Path to playlist, or playlist root folder.")]
pub playlist: Option<String>, pub playlists: Option<String>,
#[clap(long, env, help_heading = Some("General"), help = "Path to database file")] #[clap(long, env, help_heading = Some("General"), help = "Path to database file")]
pub db: Option<PathBuf>, pub db: Option<PathBuf>,
@ -129,7 +129,7 @@ pub struct Args {
long, long,
env, env,
help_heading = Some("General / Playout"), help_heading = Some("General / Playout"),
help = "Channels by ids to process (for export config, foreground running, etc.)", help = "Channels by ids to process (for export config, generate playlist, foreground running, etc.)",
num_args = 1.., num_args = 1..,
)] )]
pub channels: Option<Vec<i32>>, pub channels: Option<Vec<i32>>,
@ -184,29 +184,35 @@ fn global_user(args: &mut Args) {
let mut user = String::new(); let mut user = String::new();
let mut mail = String::new(); let mut mail = String::new();
print!("Global admin: "); if args.username.is_none() {
stdout().flush().unwrap(); print!("Global admin: ");
stdout().flush().unwrap();
stdin() stdin()
.read_line(&mut user) .read_line(&mut user)
.expect("Did not enter a correct name?"); .expect("Did not enter a correct name?");
args.username = Some(user.trim().to_string()); args.username = Some(user.trim().to_string());
}
print!("Password: "); if args.password.is_none() {
stdout().flush().unwrap(); print!("Password: ");
let password = read_password(); stdout().flush().unwrap();
let password = read_password();
args.password = password.ok(); args.password = password.ok();
}
print!("Mail: "); if args.mail.is_none() {
stdout().flush().unwrap(); print!("Mail: ");
stdout().flush().unwrap();
stdin() stdin()
.read_line(&mut mail) .read_line(&mut mail)
.expect("Did not enter a correct name?"); .expect("Did not enter a correct name?");
args.mail = Some(mail.trim().to_string()); args.mail = Some(mail.trim().to_string());
}
} }
pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> { pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
@ -230,94 +236,114 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
let mut storage = String::new(); let mut storage = String::new();
let mut playlist = String::new(); let mut playlist = String::new();
let mut logging = String::new(); let mut logging = String::new();
let mut hls = String::new(); let mut public = String::new();
let mut shared_store = String::new(); let mut shared_store = String::new();
let mut global = GlobalSettings { let mut global = GlobalSettings {
id: 0, id: 0,
secret: None, secret: None,
logging_path: String::new(), logs: String::new(),
playlist_root: String::new(), playlists: String::new(),
public_root: String::new(), public: String::new(),
storage_root: String::new(), storage: String::new(),
shared_storage: false, shared: false,
}; };
if check_user.unwrap_or_default().is_empty() { if check_user.unwrap_or_default().is_empty() {
global_user(&mut args); global_user(&mut args);
} }
print!("Storage path [/var/lib/ffplayout/tv-media]: "); if let Some(st) = args.storage {
stdout().flush().unwrap(); global.storage = st;
stdin()
.read_line(&mut storage)
.expect("Did not enter a correct path?");
if storage.trim().is_empty() {
global.storage_root = "/var/lib/ffplayout/tv-media".to_string();
} else { } else {
global.storage_root = storage print!("Storage path [/var/lib/ffplayout/tv-media]: ");
.trim() stdout().flush().unwrap();
.trim_matches(|c| c == '"' || c == '\'')
.to_string(); stdin()
.read_line(&mut storage)
.expect("Did not enter a correct path?");
global.storage = if storage.trim().is_empty() {
"/var/lib/ffplayout/tv-media".to_string()
} else {
storage
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string()
};
} }
print!("Playlist path [/var/lib/ffplayout/playlists]: "); if let Some(pl) = args.playlists {
stdout().flush().unwrap(); global.playlists = pl
stdin()
.read_line(&mut playlist)
.expect("Did not enter a correct path?");
if playlist.trim().is_empty() {
global.playlist_root = "/var/lib/ffplayout/playlists".to_string();
} else { } else {
global.playlist_root = playlist print!("Playlist path [/var/lib/ffplayout/playlists]: ");
.trim() stdout().flush().unwrap();
.trim_matches(|c| c == '"' || c == '\'')
.to_string(); stdin()
.read_line(&mut playlist)
.expect("Did not enter a correct path?");
global.playlists = if playlist.trim().is_empty() {
"/var/lib/ffplayout/playlists".to_string()
} else {
playlist
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string()
};
} }
print!("Logging path [/var/log/ffplayout]: "); if let Some(lp) = args.logs {
stdout().flush().unwrap(); global.logs = lp;
stdin()
.read_line(&mut logging)
.expect("Did not enter a correct path?");
if logging.trim().is_empty() {
global.logging_path = "/var/log/ffplayout".to_string();
} else { } else {
global.logging_path = logging print!("Logging path [/var/log/ffplayout]: ");
.trim() stdout().flush().unwrap();
.trim_matches(|c| c == '"' || c == '\'')
.to_string(); stdin()
.read_line(&mut logging)
.expect("Did not enter a correct path?");
global.logs = if logging.trim().is_empty() {
"/var/log/ffplayout".to_string()
} else {
logging
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string()
}
} }
print!("Public (HLS) path [/usr/share/ffplayout/public]: "); if let Some(p) = args.public {
stdout().flush().unwrap(); global.public = p;
stdin()
.read_line(&mut hls)
.expect("Did not enter a correct path?");
if hls.trim().is_empty() {
global.public_root = "/usr/share/ffplayout/public".to_string();
} else { } else {
global.public_root = hls print!("Public (HLS) path [/usr/share/ffplayout/public]: ");
.trim() stdout().flush().unwrap();
.trim_matches(|c| c == '"' || c == '\'')
.to_string(); stdin()
.read_line(&mut public)
.expect("Did not enter a correct path?");
global.public = if public.trim().is_empty() {
"/usr/share/ffplayout/public".to_string()
} else {
public
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string()
};
} }
print!("Shared storage [Y/n]: "); if args.shared {
stdout().flush().unwrap(); global.shared = true;
} else {
print!("Shared storage [Y/n]: ");
stdout().flush().unwrap();
stdin() stdin()
.read_line(&mut shared_store) .read_line(&mut shared_store)
.expect("Did not enter a yes or no?"); .expect("Did not enter a yes or no?");
global.shared_storage = shared_store.trim().to_lowercase().starts_with('y'); global.shared = shared_store.trim().to_lowercase().starts_with('y');
}
if let Err(e) = handles::update_global(pool, global.clone()).await { if let Err(e) = handles::update_global(pool, global.clone()).await {
eprintln!("{e}"); eprintln!("{e}");
@ -325,25 +351,25 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
}; };
let mut channel = handles::select_channel(pool, &1).await.unwrap(); let mut channel = handles::select_channel(pool, &1).await.unwrap();
channel.hls_path = global.public_root; channel.public = global.public;
channel.playlist_path = global.playlist_root; channel.playlists = global.playlists;
channel.storage_path = global.storage_root; channel.storage = global.storage;
let mut storage_path = PathBuf::from(channel.storage_path.clone()); let mut storage_path = PathBuf::from(channel.storage.clone());
if global.shared_storage { if global.shared {
storage_path = storage_path.join("1"); storage_path = storage_path.join("1");
channel.preview_url = "http://127.0.0.1:8787/1/stream.m3u8".to_string(); channel.preview_url = "http://127.0.0.1:8787/1/stream.m3u8".to_string();
channel.hls_path = Path::new(&channel.hls_path) channel.public = Path::new(&channel.public)
.join("1") .join("1")
.to_string_lossy() .to_string_lossy()
.to_string(); .to_string();
channel.playlist_path = Path::new(&channel.playlist_path) channel.playlists = Path::new(&channel.playlists)
.join("1") .join("1")
.to_string_lossy() .to_string_lossy()
.to_string(); .to_string();
channel.storage_path = storage_path.to_string_lossy().to_string(); channel.storage = storage_path.to_string_lossy().to_string();
}; };
if let Err(e) = copy_assets(&storage_path).await { if let Err(e) = copy_assets(&storage_path).await {
@ -358,20 +384,13 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
} }
println!("\nSet global settings done..."); println!("\nSet global settings done...");
} } else if args.add {
if args.add {
global_user(&mut args); global_user(&mut args);
} }
if let Some(username) = args.username { if let Some(username) = args.username {
error_code = 0; error_code = 0;
if args.mail.is_none() || args.password.is_none() {
eprintln!("Mail/password missing!");
error_code = 1;
}
let chl: Vec<i32> = channels.clone().iter().map(|c| c.id).collect(); let chl: Vec<i32> = channels.clone().iter().map(|c| c.id).collect();
let ff_user = User { let ff_user = User {
@ -392,73 +411,6 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
println!("Create global admin user \"{username}\" done..."); println!("Create global admin user \"{username}\" done...");
} }
if !args.init
&& args.storage.is_some()
&& args.playlist.is_some()
&& args.public.is_some()
&& args.log_path.is_some()
{
error_code = 0;
let global = GlobalSettings {
id: 0,
secret: None,
logging_path: args.log_path.unwrap().to_string_lossy().to_string(),
playlist_root: args.playlist.unwrap(),
public_root: args.public.unwrap(),
storage_root: args.storage.unwrap(),
shared_storage: args.shared_storage,
};
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()
.to_string();
channel.playlist_path = Path::new(&global.playlist_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).await {
eprintln!("{e}");
};
match handles::update_global(pool, global.clone()).await {
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;
}
};
#[cfg(target_family = "unix")]
{
update_permissions().await;
}
}
if ARGS.list_channels { if ARGS.list_channels {
let chl = channels let chl = channels
.iter() .iter()

View File

@ -35,7 +35,7 @@ pub async fn create_channel(
target_channel: Channel, target_channel: Channel,
) -> Result<Channel, ServiceError> { ) -> Result<Channel, ServiceError> {
let 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 storage_path = PathBuf::from(channel.storage.clone());
handles::new_channel_presets(conn, channel.id).await?; handles::new_channel_presets(conn, channel.id).await?;

View File

@ -176,21 +176,21 @@ pub struct PlayoutConfig {
#[derive(Debug, Default, Clone, Deserialize, Serialize)] #[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct Channel { pub struct Channel {
pub logging_path: PathBuf, pub logs: PathBuf,
pub hls_path: PathBuf, pub public: PathBuf,
pub playlist_path: PathBuf, pub playlists: PathBuf,
pub storage_path: PathBuf, pub storage: PathBuf,
pub shared_storage: bool, pub shared: bool,
} }
impl Channel { impl Channel {
pub fn new(config: &models::GlobalSettings, channel: models::Channel) -> Self { pub fn new(config: &models::GlobalSettings, channel: models::Channel) -> Self {
Self { Self {
logging_path: PathBuf::from(config.logging_path.clone()), logs: PathBuf::from(config.logs.clone()),
hls_path: PathBuf::from(channel.hls_path.clone()), public: PathBuf::from(channel.public.clone()),
playlist_path: PathBuf::from(channel.playlist_path.clone()), playlists: PathBuf::from(channel.playlists.clone()),
storage_path: PathBuf::from(channel.storage_path.clone()), storage: PathBuf::from(channel.storage.clone()),
shared_storage: config.shared_storage, shared: config.shared,
} }
} }
} }
@ -588,27 +588,23 @@ impl PlayoutConfig {
let task = Task::new(&config); let task = Task::new(&config);
let mut output = Output::new(&config); let mut output = Output::new(&config);
if !channel.storage_path.is_dir() { if !channel.storage.is_dir() {
tokio::fs::create_dir_all(&channel.storage_path) tokio::fs::create_dir_all(&channel.storage)
.await .await
.unwrap_or_else(|_| { .unwrap_or_else(|_| panic!("Can't create storage folder: {:#?}", channel.storage));
panic!("Can't create storage folder: {:#?}", channel.storage_path)
});
} }
let mut storage = let mut storage = Storage::new(&config, channel.storage.clone(), global.shared);
Storage::new(&config, channel.storage_path.clone(), global.shared_storage);
if !channel.playlist_path.is_dir() { if !channel.playlists.is_dir() {
tokio::fs::create_dir_all(&channel.playlist_path).await?; tokio::fs::create_dir_all(&channel.playlists).await?;
} }
if !channel.logging_path.is_dir() { if !channel.logs.is_dir() {
tokio::fs::create_dir_all(&channel.logging_path).await?; tokio::fs::create_dir_all(&channel.logs).await?;
} }
let (filler_path, _, filler) = let (filler_path, _, filler) = norm_abs_path(&channel.storage, &config.storage_filler)?;
norm_abs_path(&channel.storage_path, &config.storage_filler)?;
storage.filler = filler; storage.filler = filler;
storage.filler_path = filler_path; storage.filler_path = filler_path;
@ -621,7 +617,7 @@ impl PlayoutConfig {
playlist.length_sec = Some(86400.0); playlist.length_sec = Some(86400.0);
} }
let (logo_path, _, logo) = norm_abs_path(&channel.storage_path, &processing.logo)?; let (logo_path, _, logo) = norm_abs_path(&channel.storage, &processing.logo)?;
if processing.add_logo && !logo_path.is_file() { if processing.add_logo && !logo_path.is_file() {
processing.add_logo = false; processing.add_logo = false;
@ -709,13 +705,13 @@ impl PlayoutConfig {
for item in cmd.iter_mut() { for item in cmd.iter_mut() {
if item.ends_with(".ts") || (item.ends_with(".m3u8") && item != "master.m3u8") { if item.ends_with(".ts") || (item.ends_with(".m3u8") && item != "master.m3u8") {
if let Ok((hls_path, _, _)) = norm_abs_path(&channel.hls_path, item) { if let Ok((public, _, _)) = norm_abs_path(&channel.public, item) {
let parent = hls_path.parent().ok_or("HLS parent path")?; let parent = public.parent().ok_or("HLS parent path")?;
if !parent.is_dir() { if !parent.is_dir() {
fs::create_dir_all(parent).await?; fs::create_dir_all(parent).await?;
} }
item.clone_from(&hls_path.to_string_lossy().to_string()); item.clone_from(&public.to_string_lossy().to_string());
}; };
} }
} }
@ -736,7 +732,7 @@ impl PlayoutConfig {
text.node_pos = None; text.node_pos = None;
} }
let (font_path, _, font) = norm_abs_path(&channel.storage_path, &text.font)?; let (font_path, _, font) = norm_abs_path(&channel.storage, &text.font)?;
text.font = font; text.font = font;
text.font_path = font_path.to_string_lossy().to_string(); text.font_path = font_path.to_string_lossy().to_string();
@ -853,12 +849,12 @@ pub async fn get_config(
config.storage.paths = paths; config.storage.paths = paths;
} }
if let Some(playlist) = args.playlist { if let Some(playlist) = args.playlists {
config.channel.playlist_path = PathBuf::from(&playlist); config.channel.playlists = PathBuf::from(&playlist);
} }
if let Some(folder) = args.folder { if let Some(folder) = args.folder {
config.channel.storage_path = folder; config.channel.storage = folder;
config.processing.mode = ProcessMode::Folder; config.processing.mode = ProcessMode::Folder;
} }
@ -877,10 +873,10 @@ pub async fn get_config(
} }
} }
if args.shared_storage { if args.shared {
// config.channel.shared_storage could be true already, // config.channel.shared could be true already,
// so should not be overridden with false when args.shared_storage is not set // so should not be overridden with false when args.shared is not set
config.channel.shared_storage = args.shared_storage config.channel.shared = args.shared
} }
if let Some(volume) = args.volume { if let Some(volume) = args.volume {

View File

@ -115,13 +115,12 @@ pub async fn browser(
let mut extensions = config.storage.extensions.clone(); let mut extensions = config.storage.extensions.clone();
extensions.append(&mut channel_extensions); extensions.append(&mut channel_extensions);
let (path, parent, path_component) = let (path, parent, path_component) = norm_abs_path(&config.channel.storage, &path_obj.source)?;
norm_abs_path(&config.channel.storage_path, &path_obj.source)?;
let parent_path = if !path_component.is_empty() { let parent_path = if !path_component.is_empty() {
path.parent().unwrap() path.parent().unwrap()
} else { } else {
&config.channel.storage_path &config.channel.storage
}; };
let mut obj = PathObject::new(path_component, Some(parent)); let mut obj = PathObject::new(path_component, Some(parent));
@ -212,7 +211,7 @@ pub async fn create_directory(
config: &PlayoutConfig, config: &PlayoutConfig,
path_obj: &PathObject, path_obj: &PathObject,
) -> Result<HttpResponse, ServiceError> { ) -> Result<HttpResponse, ServiceError> {
let (path, _, _) = norm_abs_path(&config.channel.storage_path, &path_obj.source)?; let (path, _, _) = norm_abs_path(&config.channel.storage, &path_obj.source)?;
if let Err(e) = fs::create_dir_all(&path).await { if let Err(e) = fs::create_dir_all(&path).await {
return Err(ServiceError::BadRequest(e.to_string())); return Err(ServiceError::BadRequest(e.to_string()));
@ -281,8 +280,8 @@ pub async fn rename_file(
config: &PlayoutConfig, config: &PlayoutConfig,
move_object: &MoveObject, move_object: &MoveObject,
) -> Result<MoveObject, ServiceError> { ) -> Result<MoveObject, ServiceError> {
let (source_path, _, _) = norm_abs_path(&config.channel.storage_path, &move_object.source)?; let (source_path, _, _) = norm_abs_path(&config.channel.storage, &move_object.source)?;
let (mut target_path, _, _) = norm_abs_path(&config.channel.storage_path, &move_object.target)?; let (mut target_path, _, _) = norm_abs_path(&config.channel.storage, &move_object.target)?;
if !source_path.exists() { if !source_path.exists() {
return Err(ServiceError::BadRequest("Source file not exist!".into())); return Err(ServiceError::BadRequest("Source file not exist!".into()));
@ -314,7 +313,7 @@ pub async fn remove_file_or_folder(
config: &PlayoutConfig, config: &PlayoutConfig,
source_path: &str, source_path: &str,
) -> Result<(), ServiceError> { ) -> Result<(), ServiceError> {
let (source, _, _) = norm_abs_path(&config.channel.storage_path, source_path)?; let (source, _, _) = norm_abs_path(&config.channel.storage, source_path)?;
if !source.exists() { if !source.exists() {
return Err(ServiceError::BadRequest("Source does not exists!".into())); return Err(ServiceError::BadRequest("Source does not exists!".into()));
@ -346,7 +345,7 @@ pub async fn remove_file_or_folder(
} }
async fn valid_path(config: &PlayoutConfig, path: &str) -> Result<PathBuf, ServiceError> { async fn valid_path(config: &PlayoutConfig, path: &str) -> Result<PathBuf, ServiceError> {
let (test_path, _, _) = norm_abs_path(&config.channel.storage_path, path)?; let (test_path, _, _) = norm_abs_path(&config.channel.storage, path)?;
if !test_path.is_dir() { if !test_path.is_dir() {
return Err(ServiceError::BadRequest("Target folder not exists!".into())); return Err(ServiceError::BadRequest("Target folder not exists!".into()));

View File

@ -206,7 +206,7 @@ pub fn playlist_generator(manager: &ChannelManager) -> Result<Vec<JsonPlaylist>,
} }
} }
}; };
let playlist_root = &config.channel.playlist_path; let playlist_root = &config.channel.playlists;
let mut playlists = vec![]; let mut playlists = vec![];
let mut date_range = vec![]; let mut date_range = vec![];
let mut from_template = false; let mut from_template = false;
@ -215,7 +215,7 @@ pub fn playlist_generator(manager: &ChannelManager) -> Result<Vec<JsonPlaylist>,
error!( error!(
target: Target::all(), channel = id; target: Target::all(), channel = id;
"Playlist folder <b><magenta>{:?}</></b> not exists!", "Playlist folder <b><magenta>{:?}</></b> not exists!",
config.channel.playlist_path config.channel.playlists
); );
} }

View File

@ -299,11 +299,7 @@ fn file_formatter(
pub fn log_file_path() -> PathBuf { pub fn log_file_path() -> PathBuf {
let config = GlobalSettings::global(); let config = GlobalSettings::global();
let mut log_path = PathBuf::from(&ARGS.logs.as_ref().unwrap_or(&config.logs));
let mut log_path = ARGS
.log_path
.clone()
.unwrap_or(PathBuf::from(&config.logging_path));
if !log_path.is_absolute() { if !log_path.is_absolute() {
log_path = env::current_dir().unwrap().join(log_path); log_path = env::current_dir().unwrap().join(log_path);

View File

@ -203,7 +203,7 @@ pub fn public_path() -> PathBuf {
let dev_path = env::current_dir() let dev_path = env::current_dir()
.unwrap_or_default() .unwrap_or_default()
.join("frontend/.output/public/"); .join("frontend/.output/public/");
let mut public_path = PathBuf::from(&config.public_root); let mut public_path = PathBuf::from(&config.public);
if let Some(p) = &ARGS.public { if let Some(p) = &ARGS.public {
// When public path is set as argument use this path for serving static files. // When public path is set as argument use this path for serving static files.

View File

@ -14,7 +14,7 @@ pub async fn read_playlist(
date: String, date: String,
) -> Result<JsonPlaylist, ServiceError> { ) -> Result<JsonPlaylist, ServiceError> {
let d: Vec<&str> = date.split('-').collect(); let d: Vec<&str> = date.split('-').collect();
let mut playlist_path = config.channel.playlist_path.clone(); let mut playlist_path = config.channel.playlists.clone();
playlist_path = playlist_path playlist_path = playlist_path
.join(d[0]) .join(d[0])
@ -34,7 +34,7 @@ pub async fn write_playlist(
) -> Result<String, ServiceError> { ) -> Result<String, ServiceError> {
let date = json_data.date.clone(); let date = json_data.date.clone();
let d: Vec<&str> = date.split('-').collect(); let d: Vec<&str> = date.split('-').collect();
let mut playlist_path = config.channel.playlist_path.clone(); let mut playlist_path = config.channel.playlists.clone();
if !playlist_path if !playlist_path
.extension() .extension()
@ -93,7 +93,7 @@ pub fn generate_playlist(manager: ChannelManager) -> Result<JsonPlaylist, Servic
for path in &source.paths { for path in &source.paths {
let (safe_path, _, _) = let (safe_path, _, _) =
norm_abs_path(&config.channel.storage_path, &path.to_string_lossy())?; norm_abs_path(&config.channel.storage, &path.to_string_lossy())?;
paths.push(safe_path); paths.push(safe_path);
} }
@ -124,7 +124,7 @@ pub fn generate_playlist(manager: ChannelManager) -> Result<JsonPlaylist, Servic
pub async fn delete_playlist(config: &PlayoutConfig, date: &str) -> Result<String, ServiceError> { pub async fn delete_playlist(config: &PlayoutConfig, date: &str) -> Result<String, ServiceError> {
let d: Vec<&str> = date.split('-').collect(); let d: Vec<&str> = date.split('-').collect();
let mut playlist_path = PathBuf::from(&config.channel.playlist_path); let mut playlist_path = PathBuf::from(&config.channel.playlists);
playlist_path = playlist_path playlist_path = playlist_path
.join(d[0]) .join(d[0])

View File

@ -118,7 +118,7 @@ pub fn stat(config: PlayoutConfig) -> SystemStat {
for disk in &*disks { for disk in &*disks {
if disk.mount_point().to_string_lossy().len() > 1 if disk.mount_point().to_string_lossy().len() > 1
&& config.channel.storage_path.starts_with(disk.mount_point()) && config.channel.storage.starts_with(disk.mount_point())
{ {
storage.path = disk.name().to_string_lossy().to_string(); storage.path = disk.name().to_string_lossy().to_string();
storage.total = disk.total_space(); storage.total = disk.total_space();

View File

@ -5,11 +5,11 @@ CREATE TABLE
global ( global (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
secret TEXT NOT NULL, secret TEXT NOT NULL,
logging_path TEXT NOT NULL DEFAULT "/var/log/ffplayout", logs TEXT NOT NULL DEFAULT "/var/log/ffplayout",
playlist_root TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists", playlists TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists",
public_root TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public", public TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
storage_root TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media", storage TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
shared_storage INTEGER NOT NULL DEFAULT 0, shared INTEGER NOT NULL DEFAULT 0,
UNIQUE (secret) UNIQUE (secret)
); );
@ -27,9 +27,9 @@ CREATE TABLE
preview_url TEXT NOT NULL, preview_url TEXT NOT NULL,
extra_extensions TEXT NOT NULL DEFAULT 'jpg,jpeg,png', extra_extensions TEXT NOT NULL DEFAULT 'jpg,jpeg,png',
active INTEGER NOT NULL DEFAULT 0, active INTEGER NOT NULL DEFAULT 0,
hls_path TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public", public TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
playlist_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists", playlists TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists",
storage_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media", storage TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
last_date TEXT, last_date TEXT,
time_shift REAL NOT NULL DEFAULT 0 time_shift REAL NOT NULL DEFAULT 0
); );

View File

@ -39,8 +39,8 @@ name = "api_routes"
path = "src/api_routes.rs" path = "src/api_routes.rs"
[[test]] [[test]]
name = "lib_utils" name = "utils"
path = "src/lib_utils.rs" path = "src/utils.rs"
[[test]] [[test]]
name = "engine_playlist" name = "engine_playlist"

View File

@ -22,8 +22,8 @@ async fn prepare_config() -> (PlayoutConfig, ChannelManager, Pool<Sqlite>) {
sqlx::query( sqlx::query(
r#" r#"
UPDATE global SET public_root = "assets/hls", logging_path = "assets/log", playlist_root = "assets/playlists", storage_root = "assets/storage"; UPDATE global SET public = "assets/hls", logs = "assets/log", playlists = "assets/playlists", storage = "assets/storage";
UPDATE channels SET hls_path = "assets/hls", playlist_path = "assets/playlists", storage_path = "assets/storage"; UPDATE channels SET public = "assets/hls", playlists = "assets/playlists", storage = "assets/storage";
"#, "#,
) )
.execute(&pool) .execute(&pool)

View File

@ -22,8 +22,8 @@ async fn prepare_config() -> (PlayoutConfig, ChannelManager) {
sqlx::query( sqlx::query(
r#" r#"
UPDATE global SET public_root = "assets/hls", logging_path = "assets/log", playlist_root = "assets/playlists", storage_root = "assets/storage"; UPDATE global SET public = "assets/hls", logs = "assets/log", playlists = "assets/playlists", storage = "assets/storage";
UPDATE channels SET hls_path = "assets/hls", playlist_path = "assets/playlists", storage_path = "assets/storage"; UPDATE channels SET public = "assets/hls", playlists = "assets/playlists", storage = "assets/storage";
UPDATE configurations SET processing_width = 1024, processing_height = 576; UPDATE configurations SET processing_width = 1024, processing_height = 576;
"#, "#,
) )

View File

@ -25,8 +25,8 @@ async fn prepare_config() -> (PlayoutConfig, ChannelManager) {
sqlx::query( sqlx::query(
r#" r#"
UPDATE global SET public_root = "assets/hls", logging_path = "assets/log", playlist_root = "assets/playlists", storage_root = "assets/storage"; UPDATE global SET public = "assets/hls", logs = "assets/log", playlists = "assets/playlists", storages = "assets/storage";
UPDATE channels SET hls_path = "assets/hls", playlist_path = "assets/playlists", storage_path = "assets/storage"; UPDATE channels SET public = "assets/hls", playlists = "assets/playlists", storage = "assets/storage";
UPDATE configurations SET processing_width = 1024, processing_height = 576; UPDATE configurations SET processing_width = 1024, processing_height = 576;
"#, "#,
) )
@ -104,7 +104,7 @@ fn test_generate_playlist_from_folder() {
config.processing.mode = Playlist; config.processing.mode = Playlist;
config.storage.filler = "assets/".into(); config.storage.filler = "assets/".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
let playlist = generate_playlist(manager); let playlist = generate_playlist(manager);
@ -149,7 +149,7 @@ fn test_generate_playlist_from_template() {
config.processing.mode = Playlist; config.processing.mode = Playlist;
config.storage.filler = "assets/".into(); config.storage.filler = "assets/".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
let playlist = generate_playlist(manager); let playlist = generate_playlist(manager);

View File

@ -24,8 +24,8 @@ async fn prepare_config() -> (PlayoutConfig, ChannelManager) {
sqlx::query( sqlx::query(
r#" r#"
UPDATE global SET public_root = "assets/hls", logging_path = "assets/log", playlist_root = "assets/playlists", storage_root = "assets/storage"; UPDATE global SET public = "assets/hls", logs = "assets/log", playlists = "assets/playlists", storage = "assets/storage";
UPDATE channels SET hls_path = "assets/hls", playlist_path = "assets/playlists", storage_path = "assets/storage"; UPDATE channels SET public = "assets/hls", playlists = "assets/playlists", storage = "assets/storage";
UPDATE configurations SET processing_width = 1024, processing_height = 576; UPDATE configurations SET processing_width = 1024, processing_height = 576;
"#, "#,
) )
@ -67,7 +67,7 @@ fn test_gen_source() {
config.playlist.start_sec = Some(0.0); config.playlist.start_sec = Some(0.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
let mut valid_source_with_probe = Media::new(0, "assets/media_mix/av_sync.mp4", true); let mut valid_source_with_probe = Media::new(0, "assets/media_mix/av_sync.mp4", true);
@ -114,7 +114,7 @@ fn playlist_missing() {
config.playlist.start_sec = Some(0.0); config.playlist.start_sec = Some(0.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;
@ -148,7 +148,7 @@ fn playlist_next_missing() {
config.playlist.start_sec = Some(0.0); config.playlist.start_sec = Some(0.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;
@ -182,7 +182,7 @@ fn playlist_to_short() {
config.playlist.start_sec = Some(21600.0); config.playlist.start_sec = Some(21600.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;
@ -216,7 +216,7 @@ fn playlist_init_after_list_end() {
config.playlist.start_sec = Some(21600.0); config.playlist.start_sec = Some(21600.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;
@ -250,7 +250,7 @@ fn playlist_change_at_midnight() {
config.playlist.start_sec = Some(0.0); config.playlist.start_sec = Some(0.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;
@ -284,7 +284,7 @@ fn playlist_change_before_midnight() {
config.playlist.start_sec = Some(0.0); config.playlist.start_sec = Some(0.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;
@ -318,7 +318,7 @@ fn playlist_change_at_six() {
config.playlist.start_sec = Some(21600.0); config.playlist.start_sec = Some(21600.0);
config.playlist.length = "24:00:00".into(); config.playlist.length = "24:00:00".into();
config.playlist.length_sec = Some(86400.0); config.playlist.length_sec = Some(86400.0);
config.channel.playlist_path = "assets/playlists".into(); config.channel.playlists = "assets/playlists".into();
config.storage.filler = "assets/media_filler/filler_0.mp4".into(); config.storage.filler = "assets/media_filler/filler_0.mp4".into();
config.output.mode = Null; config.output.mode = Null;
config.output.output_count = 1; config.output.output_count = 1;

View File

@ -18,8 +18,8 @@ async fn prepare_config() -> (PlayoutConfig, ChannelManager) {
sqlx::query( sqlx::query(
r#" r#"
UPDATE global SET public_root = "assets/hls", logging_path = "assets/log", playlist_root = "assets/playlists", storage_root = "assets/storage"; UPDATE global SET public = "assets/hls", logs = "assets/log", playlists = "assets/playlists", storage = "assets/storage";
UPDATE channels SET hls_path = "assets/hls", playlist_path = "assets/playlists", storage_path = "assets/storage"; UPDATE channels SET public = "assets/hls", playlists = "assets/playlists", storage = "assets/storage";
UPDATE configurations SET processing_width = 1024, processing_height = 576; UPDATE configurations SET processing_width = 1024, processing_height = 576;
"#, "#,
) )