fi db fields, import and dump config file, move paths to global table and set rest to relative
This commit is contained in:
parent
933d7a9065
commit
016a0f3b17
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ ffplayout.1.gz
|
||||
/public/
|
||||
tmp/
|
||||
assets/playlist_template.json
|
||||
ffplayout_*.toml
|
||||
|
@ -128,7 +128,7 @@ curl -X DELETE http://127.0.0.1:8787/api/channel/2 -H "Authorization: Bearer <TO
|
||||
curl -X GET http://127.0.0.1:8787/api/playout/config/1 -H 'Authorization: Bearer <TOKEN>'
|
||||
```
|
||||
|
||||
Response is a JSON object from the ffplayout.toml
|
||||
Response is a JSON object
|
||||
|
||||
**Update Config**
|
||||
|
||||
|
@ -38,13 +38,11 @@ use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use tokio::fs;
|
||||
|
||||
use crate::api::auth::{create_jwt, Claims};
|
||||
use crate::db::models::Role;
|
||||
use crate::utils::{
|
||||
channels::{create_channel, delete_channel},
|
||||
config::{
|
||||
string_to_log_level, string_to_output_mode, string_to_processing_mode, PlayoutConfig,
|
||||
Template,
|
||||
},
|
||||
config::{PlayoutConfig, Template},
|
||||
control::{control_state, send_message, ControlParams, Process, ProcessCtl},
|
||||
errors::ServiceError,
|
||||
files::{
|
||||
@ -56,10 +54,6 @@ use crate::utils::{
|
||||
public_path, read_log_file, system, TextFilter,
|
||||
};
|
||||
use crate::vec_strings;
|
||||
use crate::{
|
||||
api::auth::{create_jwt, Claims},
|
||||
db::models::Configuration,
|
||||
};
|
||||
use crate::{
|
||||
db::{
|
||||
handles,
|
||||
@ -179,13 +173,13 @@ pub async fn login(pool: web::Data<Pool<Sqlite>>, credentials: web::Json<User>)
|
||||
|
||||
user.password = "".into();
|
||||
|
||||
if web::block(move || {
|
||||
let verified_password = web::block(move || {
|
||||
let hash = PasswordHash::new(&pass).unwrap();
|
||||
Argon2::default().verify_password(password_clone.as_bytes(), &hash)
|
||||
})
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
.await;
|
||||
|
||||
if verified_password.is_ok() {
|
||||
let claims = Claims::new(user.id, username.clone(), role.clone());
|
||||
|
||||
if let Ok(token) = create_jwt(claims).await {
|
||||
@ -506,7 +500,7 @@ async fn remove_channel(
|
||||
/// curl -X GET http://127.0.0.1:8787/api/playout/config/1 -H 'Authorization: Bearer <TOKEN>'
|
||||
/// ```
|
||||
///
|
||||
/// Response is a JSON object from the ffplayout.toml
|
||||
/// Response is a JSON object
|
||||
#[get("/playout/config/{id}")]
|
||||
#[protect(any("Role::GlobalAdmin", "Role::User"), ty = "Role")]
|
||||
async fn get_playout_config(
|
||||
@ -536,76 +530,96 @@ async fn update_playout_config(
|
||||
controllers: web::Data<Mutex<ChannelController>>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
let manager = controllers.lock().unwrap().get(*id).unwrap();
|
||||
let general_config = manager.config.lock().unwrap().general.clone();
|
||||
let id = general_config.id;
|
||||
let channel_id = general_config.channel_id;
|
||||
let db_config = Configuration::from(id, channel_id, data.into_inner());
|
||||
let id = manager.config.lock().unwrap().general.id;
|
||||
|
||||
if let Err(e) = handles::update_configuration(&pool.into_inner(), db_config.clone()).await {
|
||||
if let Err(e) = handles::update_configuration(&pool.into_inner(), id, data.clone()).await {
|
||||
return Err(ServiceError::Conflict(format!("{e}")));
|
||||
}
|
||||
|
||||
let mut config = manager.config.lock().unwrap();
|
||||
|
||||
config.general.stop_threshold = db_config.stop_threshold;
|
||||
config.mail.subject = db_config.subject;
|
||||
config.mail.smtp_server = db_config.smtp_server;
|
||||
config.mail.starttls = db_config.starttls;
|
||||
config.mail.sender_addr = db_config.sender_addr;
|
||||
config.mail.sender_pass = db_config.sender_pass;
|
||||
config.mail.recipient = db_config.recipient;
|
||||
config.mail.mail_level = string_to_log_level(db_config.mail_level);
|
||||
config.mail.interval = db_config.interval as u64;
|
||||
config.logging.ffmpeg_level = db_config.ffmpeg_level;
|
||||
config.logging.ingest_level = db_config.ingest_level;
|
||||
config.logging.detect_silence = db_config.detect_silence;
|
||||
config.logging.ignore_lines = db_config
|
||||
config.general.stop_threshold = data.general.stop_threshold;
|
||||
config.mail.subject.clone_from(&data.mail.subject);
|
||||
config.mail.smtp_server.clone_from(&data.mail.smtp_server);
|
||||
config.mail.starttls = data.mail.starttls;
|
||||
config.mail.sender_addr.clone_from(&data.mail.sender_addr);
|
||||
config.mail.sender_pass.clone_from(&data.mail.sender_pass);
|
||||
config.mail.recipient.clone_from(&data.mail.recipient);
|
||||
config.mail.mail_level = data.mail.mail_level;
|
||||
config.mail.interval = data.mail.interval;
|
||||
config
|
||||
.logging
|
||||
.ffmpeg_level
|
||||
.clone_from(&data.logging.ffmpeg_level);
|
||||
config
|
||||
.logging
|
||||
.ingest_level
|
||||
.clone_from(&data.logging.ingest_level);
|
||||
config.logging.detect_silence = data.logging.detect_silence;
|
||||
config
|
||||
.logging
|
||||
.ignore_lines
|
||||
.split(';')
|
||||
.map(|l| l.to_string())
|
||||
.collect();
|
||||
config.processing.mode = string_to_processing_mode(db_config.processing_mode);
|
||||
config.processing.audio_only = db_config.audio_only;
|
||||
config.processing.audio_track_index = db_config.audio_track_index;
|
||||
config.processing.copy_audio = db_config.copy_audio;
|
||||
config.processing.copy_video = db_config.copy_video;
|
||||
config.processing.width = db_config.width;
|
||||
config.processing.height = db_config.height;
|
||||
config.processing.aspect = db_config.aspect;
|
||||
config.processing.fps = db_config.fps;
|
||||
config.processing.add_logo = db_config.add_logo;
|
||||
config.processing.logo = db_config.logo;
|
||||
config.processing.logo_scale = db_config.logo_scale;
|
||||
config.processing.logo_opacity = db_config.logo_opacity;
|
||||
config.processing.logo_position = db_config.logo_position;
|
||||
config.processing.audio_tracks = db_config.audio_tracks;
|
||||
config.processing.audio_channels = db_config.audio_channels;
|
||||
config.processing.volume = db_config.volume;
|
||||
config.processing.custom_filter = db_config.decoder_filter;
|
||||
config.ingest.enable = db_config.ingest_enable;
|
||||
config.ingest.input_param = db_config.ingest_param;
|
||||
config.ingest.custom_filter = db_config.ingest_filter;
|
||||
config.playlist.path = PathBuf::from(db_config.playlist_path);
|
||||
config.playlist.day_start = db_config.day_start;
|
||||
config.playlist.length = db_config.length;
|
||||
config.playlist.infinit = db_config.infinit;
|
||||
config.storage.path = PathBuf::from(db_config.storage_path);
|
||||
config.storage.filler = PathBuf::from(db_config.filler);
|
||||
config.storage.extensions = db_config
|
||||
.clone_from(&data.logging.ignore_lines);
|
||||
config.processing.mode.clone_from(&data.processing.mode);
|
||||
config.processing.audio_only = data.processing.audio_only;
|
||||
config.processing.audio_track_index = data.processing.audio_track_index;
|
||||
config.processing.copy_audio = data.processing.copy_audio;
|
||||
config.processing.copy_video = data.processing.copy_video;
|
||||
config.processing.width = data.processing.width;
|
||||
config.processing.height = data.processing.height;
|
||||
config.processing.aspect = data.processing.aspect;
|
||||
config.processing.fps = data.processing.fps;
|
||||
config.processing.add_logo = data.processing.add_logo;
|
||||
config.processing.logo.clone_from(&data.processing.logo);
|
||||
config
|
||||
.processing
|
||||
.logo_scale
|
||||
.clone_from(&data.processing.logo_scale);
|
||||
config.processing.logo_opacity = data.processing.logo_opacity;
|
||||
config
|
||||
.processing
|
||||
.logo_position
|
||||
.clone_from(&data.processing.logo_position);
|
||||
config.processing.audio_tracks = data.processing.audio_tracks;
|
||||
config.processing.audio_channels = data.processing.audio_channels;
|
||||
config.processing.volume = data.processing.volume;
|
||||
config
|
||||
.processing
|
||||
.custom_filter
|
||||
.clone_from(&data.processing.custom_filter);
|
||||
config.ingest.enable = data.ingest.enable;
|
||||
config
|
||||
.ingest
|
||||
.input_param
|
||||
.clone_from(&data.ingest.input_param);
|
||||
config
|
||||
.ingest
|
||||
.custom_filter
|
||||
.clone_from(&data.ingest.custom_filter);
|
||||
config
|
||||
.playlist
|
||||
.day_start
|
||||
.clone_from(&data.playlist.day_start);
|
||||
config.playlist.length.clone_from(&data.playlist.length);
|
||||
config.playlist.infinit = data.playlist.infinit;
|
||||
config.storage.filler.clone_from(&data.storage.filler);
|
||||
config
|
||||
.storage
|
||||
.extensions
|
||||
.split(';')
|
||||
.map(|l| l.to_string())
|
||||
.collect();
|
||||
config.storage.shuffle = db_config.shuffle;
|
||||
config.text.add_text = db_config.add_text;
|
||||
config.text.fontfile = db_config.fontfile;
|
||||
config.text.text_from_filename = db_config.text_from_filename;
|
||||
config.text.style = db_config.style;
|
||||
config.text.regex = db_config.regex;
|
||||
config.task.enable = db_config.task_enable;
|
||||
config.task.path = PathBuf::from(db_config.task_path);
|
||||
config.output.mode = string_to_output_mode(db_config.output_mode);
|
||||
config.output.output_param = db_config.output_param;
|
||||
.clone_from(&data.storage.extensions);
|
||||
config.storage.shuffle = data.storage.shuffle;
|
||||
config.text.add_text = data.text.add_text;
|
||||
config.text.fontfile.clone_from(&data.text.fontfile);
|
||||
config.text.text_from_filename = data.text.text_from_filename;
|
||||
config.text.style.clone_from(&data.text.style);
|
||||
config.text.regex.clone_from(&data.text.regex);
|
||||
config.task.enable = data.task.enable;
|
||||
config.task.path.clone_from(&data.task.path);
|
||||
config.output.mode.clone_from(&data.output.mode);
|
||||
config
|
||||
.output
|
||||
.output_param
|
||||
.clone_from(&data.output.output_param);
|
||||
|
||||
Ok(web::Json("Update success"))
|
||||
}
|
||||
@ -914,7 +928,7 @@ pub async fn gen_playlist(
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
let manager = controllers.lock().unwrap().get(params.0).unwrap();
|
||||
manager.config.lock().unwrap().general.generate = Some(vec![params.1.clone()]);
|
||||
let storage_path = manager.config.lock().unwrap().storage.path.clone();
|
||||
let storage_path = manager.config.lock().unwrap().global.storage_path.clone();
|
||||
|
||||
if let Some(obj) = data {
|
||||
if let Some(paths) = &obj.paths {
|
||||
@ -1113,7 +1127,7 @@ async fn get_file(
|
||||
let id: i32 = req.match_info().query("id").parse()?;
|
||||
let manager = controllers.lock().unwrap().get(id).unwrap();
|
||||
let config = manager.config.lock().unwrap();
|
||||
let storage_path = config.storage.path.clone();
|
||||
let storage_path = config.global.storage_path.clone();
|
||||
let file_path = req.match_info().query("filename");
|
||||
let (path, _, _) = norm_abs_path(&storage_path, file_path)?;
|
||||
let file = actix_files::NamedFile::open(path)?;
|
||||
|
@ -10,7 +10,7 @@ use tokio::task;
|
||||
|
||||
use super::models::{AdvancedConfiguration, Configuration};
|
||||
use crate::db::models::{Channel, GlobalSettings, Role, TextPreset, User};
|
||||
use crate::utils::local_utc_offset;
|
||||
use crate::utils::{config::PlayoutConfig, local_utc_offset};
|
||||
|
||||
pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||
match sqlx::migrate!("../migrations").run(conn).await {
|
||||
@ -166,61 +166,62 @@ pub async fn insert_configuration(
|
||||
|
||||
pub async fn update_configuration(
|
||||
conn: &Pool<Sqlite>,
|
||||
config: Configuration,
|
||||
id: i32,
|
||||
config: PlayoutConfig,
|
||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let query = "UPDATE configurations SET stop_threshold = $2, subject = $3, smtp_server = $4, sender_addr = $5, sender_pass = $6, recipient = $7, starttls = $8, mail_level = $9, interval = $10, ffmpeg_level = $11, ingest_level = $12, detect_silence = $13 , ignore_lines = $14, processing_mode = $15, audio_only = $16, copy_audio = $17, copy_video = $18, width = $19, height = $20, aspect = $21, add_logo = $22, logo = $23, logo_scale = $24, logo_opacity = $25, logo_position = $26, audio_tracks = $27, audio_track_index = $28, audio_channels = $29, volume = $30, decoder_filter = $31, ingest_enable = $32, ingest_param = $33, ingest_filter = $34, playlist_path = $35, day_start = $36, length = $37, infinit = $38, storage_path = $39, filler = $40, extensions = $41, shuffle = $42, add_text = $43, text_from_filename = $44, fontfile = $45, regex = $46, task_enable = $47, task_path = $48, output_mode = $49, output_param = $50 WHERE id = $1";
|
||||
let query = "UPDATE configurations SET general_stop_threshold = $2, mail_subject = $3, mail_smtp = $4, mail_addr = $5, mail_pass = $6, mail_recipient = $7, mail_starttls = $8, mail_level = $9, mail_interval = $10, logging_ffmpeg_level = $11, logging_ingest_level = $12, logging_detect_silence = $13, logging_ignore = $14, processing_mode = $15, processing_audio_only = $16, processing_copy_audio = $17, processing_copy_video = $18, processing_width = $19, processing_height = $20, processing_aspect = $21, processing_fps = $22, processing_add_logo = $23, processing_logo = $24, processing_logo_scale = $25, processing_logo_opacity = $26, processing_logo_position = $27, processing_audio_tracks = $28, processing_audio_track_index = $29, processing_audio_channels = $30, processing_volume = $31, processing_filter = $32, ingest_enable = $33, ingest_param = $34, ingest_filter = $35, playlist_day_start = $36, playlist_length = $37, playlist_infinit = $38, storage_filler = $39, storage_extensions = $40, storage_shuffle = $41, text_add = $42, text_from_filename = $43, text_font = $44, text_style = $45, text_regex = $46, task_enable = $47, task_path = $48, output_mode = $49, output_param = $50 WHERE id = $1";
|
||||
|
||||
sqlx::query(query)
|
||||
.bind(config.id)
|
||||
.bind(config.stop_threshold)
|
||||
.bind(config.subject)
|
||||
.bind(config.smtp_server)
|
||||
.bind(config.sender_addr)
|
||||
.bind(config.sender_pass)
|
||||
.bind(config.recipient)
|
||||
.bind(config.starttls)
|
||||
.bind(config.mail_level)
|
||||
.bind(config.interval)
|
||||
.bind(config.ffmpeg_level)
|
||||
.bind(config.ingest_level)
|
||||
.bind(config.detect_silence)
|
||||
.bind(config.ignore_lines)
|
||||
.bind(config.processing_mode)
|
||||
.bind(config.audio_only)
|
||||
.bind(config.copy_audio)
|
||||
.bind(config.copy_video)
|
||||
.bind(config.width)
|
||||
.bind(config.height)
|
||||
.bind(config.aspect)
|
||||
.bind(config.add_logo)
|
||||
.bind(config.logo)
|
||||
.bind(config.logo_scale)
|
||||
.bind(config.logo_opacity)
|
||||
.bind(config.logo_position)
|
||||
.bind(config.audio_tracks)
|
||||
.bind(config.audio_track_index)
|
||||
.bind(config.audio_channels)
|
||||
.bind(config.volume)
|
||||
.bind(config.decoder_filter)
|
||||
.bind(config.ingest_enable)
|
||||
.bind(config.ingest_param)
|
||||
.bind(config.ingest_filter)
|
||||
.bind(config.playlist_path)
|
||||
.bind(config.day_start)
|
||||
.bind(config.length)
|
||||
.bind(config.infinit)
|
||||
.bind(config.storage_path)
|
||||
.bind(config.filler)
|
||||
.bind(config.extensions)
|
||||
.bind(config.shuffle)
|
||||
.bind(config.add_text)
|
||||
.bind(config.text_from_filename)
|
||||
.bind(config.fontfile)
|
||||
.bind(config.regex)
|
||||
.bind(config.task_enable)
|
||||
.bind(config.task_path)
|
||||
.bind(config.output_mode)
|
||||
.bind(config.output_param)
|
||||
.bind(id)
|
||||
.bind(config.general.stop_threshold)
|
||||
.bind(config.mail.subject)
|
||||
.bind(config.mail.smtp_server)
|
||||
.bind(config.mail.sender_addr)
|
||||
.bind(config.mail.sender_pass)
|
||||
.bind(config.mail.recipient)
|
||||
.bind(config.mail.starttls)
|
||||
.bind(config.mail.mail_level.as_str())
|
||||
.bind(config.mail.interval)
|
||||
.bind(config.logging.ffmpeg_level)
|
||||
.bind(config.logging.ingest_level)
|
||||
.bind(config.logging.detect_silence)
|
||||
.bind(config.logging.ignore_lines.join(";"))
|
||||
.bind(config.processing.mode.to_string())
|
||||
.bind(config.processing.audio_only)
|
||||
.bind(config.processing.copy_audio)
|
||||
.bind(config.processing.copy_video)
|
||||
.bind(config.processing.width)
|
||||
.bind(config.processing.height)
|
||||
.bind(config.processing.aspect)
|
||||
.bind(config.processing.fps)
|
||||
.bind(config.processing.add_logo)
|
||||
.bind(config.processing.logo)
|
||||
.bind(config.processing.logo_scale)
|
||||
.bind(config.processing.logo_opacity)
|
||||
.bind(config.processing.logo_position)
|
||||
.bind(config.processing.audio_tracks)
|
||||
.bind(config.processing.audio_track_index)
|
||||
.bind(config.processing.audio_channels)
|
||||
.bind(config.processing.volume)
|
||||
.bind(config.processing.custom_filter)
|
||||
.bind(config.ingest.enable)
|
||||
.bind(config.ingest.input_param)
|
||||
.bind(config.ingest.custom_filter)
|
||||
.bind(config.playlist.day_start)
|
||||
.bind(config.playlist.length)
|
||||
.bind(config.playlist.infinit)
|
||||
.bind(config.storage.filler.to_string_lossy().to_string())
|
||||
.bind(config.storage.extensions.join(";"))
|
||||
.bind(config.storage.shuffle)
|
||||
.bind(config.text.add_text)
|
||||
.bind(config.text.text_from_filename)
|
||||
.bind(config.text.fontfile)
|
||||
.bind(config.text.style)
|
||||
.bind(config.text.regex)
|
||||
.bind(config.task.enable)
|
||||
.bind(config.task.path.to_string_lossy().to_string())
|
||||
.bind(config.output.mode.to_string())
|
||||
.bind(config.output.output_param)
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
@ -234,52 +234,48 @@ pub struct Configuration {
|
||||
pub id: i32,
|
||||
pub channel_id: i32,
|
||||
pub general_help: String,
|
||||
pub stop_threshold: f64,
|
||||
pub general_stop_threshold: f64,
|
||||
|
||||
pub mail_help: String,
|
||||
pub subject: String,
|
||||
pub smtp_server: String,
|
||||
pub starttls: bool,
|
||||
pub sender_addr: String,
|
||||
pub sender_pass: String,
|
||||
pub recipient: String,
|
||||
pub mail_subject: String,
|
||||
pub mail_smtp: String,
|
||||
pub mail_addr: String,
|
||||
pub mail_pass: String,
|
||||
pub mail_recipient: String,
|
||||
pub mail_starttls: bool,
|
||||
pub mail_level: String,
|
||||
pub interval: i64,
|
||||
pub mail_interval: i64,
|
||||
|
||||
pub logging_help: String,
|
||||
pub ffmpeg_level: String,
|
||||
pub ingest_level: String,
|
||||
pub logging_ffmpeg_level: String,
|
||||
pub logging_ingest_level: String,
|
||||
pub logging_detect_silence: bool,
|
||||
#[serde(default)]
|
||||
pub detect_silence: bool,
|
||||
#[serde(default)]
|
||||
pub ignore_lines: String,
|
||||
pub logging_ignore: String,
|
||||
|
||||
pub processing_help: String,
|
||||
pub processing_mode: String,
|
||||
#[serde(default)]
|
||||
pub audio_only: bool,
|
||||
#[serde(default = "default_track_index")]
|
||||
pub audio_track_index: i32,
|
||||
#[serde(default)]
|
||||
pub copy_audio: bool,
|
||||
#[serde(default)]
|
||||
pub copy_video: bool,
|
||||
pub width: i64,
|
||||
pub height: i64,
|
||||
pub aspect: f64,
|
||||
pub fps: f64,
|
||||
pub add_logo: bool,
|
||||
pub logo: String,
|
||||
pub logo_scale: String,
|
||||
pub logo_opacity: f32,
|
||||
pub logo_position: String,
|
||||
pub processing_audio_only: bool,
|
||||
pub processing_copy_audio: bool,
|
||||
pub processing_copy_video: bool,
|
||||
pub processing_width: i64,
|
||||
pub processing_height: i64,
|
||||
pub processing_aspect: f64,
|
||||
pub processing_fps: f64,
|
||||
pub processing_add_logo: bool,
|
||||
pub processing_logo: String,
|
||||
pub processing_logo_scale: String,
|
||||
pub processing_logo_opacity: f64,
|
||||
pub processing_logo_position: String,
|
||||
#[serde(default = "default_tracks")]
|
||||
pub audio_tracks: i32,
|
||||
pub processing_audio_tracks: i32,
|
||||
#[serde(default = "default_track_index")]
|
||||
pub processing_audio_track_index: i32,
|
||||
#[serde(default = "default_channels")]
|
||||
pub audio_channels: u8,
|
||||
pub volume: f64,
|
||||
pub processing_audio_channels: u8,
|
||||
pub processing_volume: f64,
|
||||
#[serde(default)]
|
||||
pub decoder_filter: String,
|
||||
pub processing_filter: String,
|
||||
|
||||
pub ingest_help: String,
|
||||
pub ingest_enable: bool,
|
||||
@ -288,24 +284,21 @@ pub struct Configuration {
|
||||
pub ingest_filter: String,
|
||||
|
||||
pub playlist_help: String,
|
||||
pub playlist_path: String,
|
||||
pub day_start: String,
|
||||
pub length: String,
|
||||
pub infinit: bool,
|
||||
pub playlist_day_start: String,
|
||||
pub playlist_length: String,
|
||||
pub playlist_infinit: bool,
|
||||
|
||||
pub storage_help: String,
|
||||
pub storage_path: String,
|
||||
#[serde(alias = "filler_clip")]
|
||||
pub filler: String,
|
||||
pub extensions: String,
|
||||
pub shuffle: bool,
|
||||
pub storage_filler: String,
|
||||
pub storage_extensions: String,
|
||||
pub storage_shuffle: bool,
|
||||
|
||||
pub text_help: String,
|
||||
pub add_text: bool,
|
||||
pub fontfile: String,
|
||||
pub text_add: bool,
|
||||
pub text_from_filename: bool,
|
||||
pub style: String,
|
||||
pub regex: String,
|
||||
pub text_font: String,
|
||||
pub text_style: String,
|
||||
pub text_regex: String,
|
||||
|
||||
pub task_help: String,
|
||||
pub task_enable: bool,
|
||||
@ -322,60 +315,58 @@ impl Configuration {
|
||||
id,
|
||||
channel_id,
|
||||
general_help: config.general.help_text,
|
||||
stop_threshold: config.general.stop_threshold,
|
||||
general_stop_threshold: config.general.stop_threshold,
|
||||
mail_help: config.mail.help_text,
|
||||
subject: config.mail.subject,
|
||||
smtp_server: config.mail.smtp_server,
|
||||
starttls: config.mail.starttls,
|
||||
sender_addr: config.mail.sender_addr,
|
||||
sender_pass: config.mail.sender_pass,
|
||||
recipient: config.mail.recipient,
|
||||
mail_subject: config.mail.subject,
|
||||
mail_smtp: config.mail.smtp_server,
|
||||
mail_starttls: config.mail.starttls,
|
||||
mail_addr: config.mail.sender_addr,
|
||||
mail_pass: config.mail.sender_pass,
|
||||
mail_recipient: config.mail.recipient,
|
||||
mail_level: config.mail.mail_level.to_string(),
|
||||
interval: config.mail.interval as i64,
|
||||
mail_interval: config.mail.interval,
|
||||
logging_help: config.logging.help_text,
|
||||
ffmpeg_level: config.logging.ffmpeg_level,
|
||||
ingest_level: config.logging.ingest_level,
|
||||
detect_silence: config.logging.detect_silence,
|
||||
ignore_lines: config.logging.ignore_lines.join(";"),
|
||||
logging_ffmpeg_level: config.logging.ffmpeg_level,
|
||||
logging_ingest_level: config.logging.ingest_level,
|
||||
logging_detect_silence: config.logging.detect_silence,
|
||||
logging_ignore: config.logging.ignore_lines.join(";"),
|
||||
processing_help: config.processing.help_text,
|
||||
processing_mode: config.processing.mode.to_string(),
|
||||
audio_only: config.processing.audio_only,
|
||||
audio_track_index: config.processing.audio_track_index,
|
||||
copy_audio: config.processing.copy_audio,
|
||||
copy_video: config.processing.copy_video,
|
||||
width: config.processing.width,
|
||||
height: config.processing.height,
|
||||
aspect: config.processing.aspect,
|
||||
fps: config.processing.fps,
|
||||
add_logo: config.processing.add_logo,
|
||||
logo: config.processing.logo,
|
||||
logo_scale: config.processing.logo_scale,
|
||||
logo_opacity: config.processing.logo_opacity,
|
||||
logo_position: config.processing.logo_position,
|
||||
audio_tracks: config.processing.audio_tracks,
|
||||
audio_channels: config.processing.audio_channels,
|
||||
volume: config.processing.volume,
|
||||
decoder_filter: config.processing.custom_filter,
|
||||
processing_audio_only: config.processing.audio_only,
|
||||
processing_audio_track_index: config.processing.audio_track_index,
|
||||
processing_copy_audio: config.processing.copy_audio,
|
||||
processing_copy_video: config.processing.copy_video,
|
||||
processing_width: config.processing.width,
|
||||
processing_height: config.processing.height,
|
||||
processing_aspect: config.processing.aspect,
|
||||
processing_fps: config.processing.fps,
|
||||
processing_add_logo: config.processing.add_logo,
|
||||
processing_logo: config.processing.logo,
|
||||
processing_logo_scale: config.processing.logo_scale,
|
||||
processing_logo_opacity: config.processing.logo_opacity,
|
||||
processing_logo_position: config.processing.logo_position,
|
||||
processing_audio_tracks: config.processing.audio_tracks,
|
||||
processing_audio_channels: config.processing.audio_channels,
|
||||
processing_volume: config.processing.volume,
|
||||
processing_filter: config.processing.custom_filter,
|
||||
ingest_help: config.ingest.help_text,
|
||||
ingest_enable: config.ingest.enable,
|
||||
ingest_param: config.ingest.input_param,
|
||||
ingest_filter: config.ingest.custom_filter,
|
||||
playlist_help: config.playlist.help_text,
|
||||
playlist_path: config.playlist.path.to_string_lossy().to_string(),
|
||||
day_start: config.playlist.day_start,
|
||||
length: config.playlist.length,
|
||||
infinit: config.playlist.infinit,
|
||||
playlist_day_start: config.playlist.day_start,
|
||||
playlist_length: config.playlist.length,
|
||||
playlist_infinit: config.playlist.infinit,
|
||||
storage_help: config.storage.help_text,
|
||||
storage_path: config.storage.path.to_string_lossy().to_string(),
|
||||
filler: config.storage.filler.to_string_lossy().to_string(),
|
||||
extensions: config.storage.extensions.join(";"),
|
||||
shuffle: config.storage.shuffle,
|
||||
storage_filler: config.storage.filler.to_string_lossy().to_string(),
|
||||
storage_extensions: config.storage.extensions.join(";"),
|
||||
storage_shuffle: config.storage.shuffle,
|
||||
text_help: config.text.help_text,
|
||||
add_text: config.text.add_text,
|
||||
fontfile: config.text.fontfile,
|
||||
text_add: config.text.add_text,
|
||||
text_font: config.text.fontfile,
|
||||
text_from_filename: config.text.text_from_filename,
|
||||
style: config.text.style,
|
||||
regex: config.text.regex,
|
||||
text_style: config.text.style,
|
||||
text_regex: config.text.regex,
|
||||
task_help: config.task.help_text,
|
||||
task_enable: config.task.enable,
|
||||
task_path: config.task.path.to_string_lossy().to_string(),
|
||||
|
@ -28,9 +28,9 @@ use ffplayout::{
|
||||
player::controller::{ChannelController, ChannelManager},
|
||||
sse::{broadcast::Broadcaster, routes::*, AuthState},
|
||||
utils::{
|
||||
args_parse::run_args,
|
||||
config::PlayoutConfig,
|
||||
logging::{init_logging, MailQueue},
|
||||
run_args,
|
||||
},
|
||||
ARGS,
|
||||
};
|
||||
@ -69,6 +69,8 @@ async fn validator(
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let mail_queues = Arc::new(Mutex::new(vec![]));
|
||||
|
||||
let pool = db_pool()
|
||||
.await
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
|
||||
@ -77,15 +79,13 @@ async fn main() -> std::io::Result<()> {
|
||||
panic!("{e}");
|
||||
};
|
||||
|
||||
if let Err(c) = run_args().await {
|
||||
exit(c);
|
||||
}
|
||||
|
||||
let mail_queues = Arc::new(Mutex::new(vec![]));
|
||||
|
||||
init_globales(&pool).await;
|
||||
init_logging(mail_queues.clone())?;
|
||||
|
||||
if let Err(c) = run_args(&pool).await {
|
||||
exit(c);
|
||||
}
|
||||
|
||||
let channel_controllers = Arc::new(Mutex::new(ChannelController::new()));
|
||||
|
||||
if let Some(conn) = &ARGS.listen {
|
||||
@ -236,7 +236,7 @@ async fn main() -> std::io::Result<()> {
|
||||
Ok(())
|
||||
} else if let Some(channels) = &ARGS.channels {
|
||||
for (index, channel_id) in channels.iter().enumerate() {
|
||||
let channel = handles::select_channel(&pool, &channel_id).await.unwrap();
|
||||
let channel = handles::select_channel(&pool, channel_id).await.unwrap();
|
||||
let config = PlayoutConfig::new(&pool, *channel_id).await;
|
||||
let manager = ChannelManager::new(Some(pool.clone()), channel.clone(), config.clone());
|
||||
let m_queue = Arc::new(Mutex::new(MailQueue::new(*channel_id, config.mail)));
|
||||
|
@ -28,7 +28,7 @@ pub fn watchman(
|
||||
is_terminated: Arc<AtomicBool>,
|
||||
sources: Arc<Mutex<Vec<Media>>>,
|
||||
) {
|
||||
let path = Path::new(&config.storage.path);
|
||||
let path = Path::new(&config.global.storage_path);
|
||||
|
||||
if !path.exists() {
|
||||
error!("Folder path not exists: '{path:?}'");
|
||||
|
@ -27,7 +27,7 @@ pub fn source_generator(manager: ChannelManager) -> Box<dyn Iterator<Item = Medi
|
||||
info!("Playout in folder mode");
|
||||
debug!(
|
||||
"Monitor folder: <b><magenta>{:?}</></b>",
|
||||
config.storage.path
|
||||
config.global.storage_path
|
||||
);
|
||||
|
||||
let config_clone = config.clone();
|
||||
|
@ -309,11 +309,14 @@ impl CurrentProgram {
|
||||
self.current_node =
|
||||
handle_list_init(&self.config, node_clone, &self.manager, last_index);
|
||||
|
||||
if self
|
||||
.current_node
|
||||
.source
|
||||
.contains(&self.config.storage.path.to_string_lossy().to_string())
|
||||
|| self.current_node.source.contains("color=c=#121212")
|
||||
if self.current_node.source.contains(
|
||||
&self
|
||||
.config
|
||||
.global
|
||||
.storage_path
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
) || self.current_node.source.contains("color=c=#121212")
|
||||
{
|
||||
is_filler = true;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ impl FolderSource {
|
||||
path_list.push(path)
|
||||
}
|
||||
} else {
|
||||
path_list.push(&config.storage.path)
|
||||
path_list.push(&config.global.storage_path)
|
||||
}
|
||||
|
||||
for path in &path_list {
|
||||
|
@ -28,13 +28,13 @@ pub fn import_file(
|
||||
program: vec![],
|
||||
};
|
||||
|
||||
let playlist_root = &config.playlist.path;
|
||||
let playlist_root = &config.global.playlist_path;
|
||||
if !playlist_root.is_dir() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"Playlist folder <b><magenta>{:?}</></b> not exists!",
|
||||
config.playlist.path,
|
||||
config.global.playlist_path,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -99,11 +99,11 @@ pub fn read_json(
|
||||
get_next: bool,
|
||||
) -> JsonPlaylist {
|
||||
let config_clone = config.clone();
|
||||
let mut playlist_path = config.playlist.path.clone();
|
||||
let mut playlist_path = config.global.playlist_path.clone();
|
||||
let start_sec = config.playlist.start_sec.unwrap();
|
||||
let date = get_date(seek, start_sec, get_next);
|
||||
|
||||
if playlist_path.is_dir() || is_remote(&config.playlist.path.to_string_lossy()) {
|
||||
if playlist_path.is_dir() || is_remote(&config.global.playlist_path.to_string_lossy()) {
|
||||
let d: Vec<&str> = date.split('-').collect();
|
||||
playlist_path = playlist_path
|
||||
.join(d[0])
|
||||
|
@ -1,6 +1,17 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{
|
||||
io::{stdin, stdout, Write},
|
||||
path::PathBuf,
|
||||
process::exit,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use log::*;
|
||||
use rpassword::read_password;
|
||||
use sqlx::{Pool, Sqlite};
|
||||
|
||||
use crate::db::{db_pool, handles::insert_user, models::User};
|
||||
use crate::utils::config::PlayoutConfig;
|
||||
use crate::ARGS;
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[clap(version,
|
||||
@ -22,6 +33,16 @@ pub struct Args {
|
||||
)]
|
||||
pub channels: Option<Vec<i32>>,
|
||||
|
||||
#[clap(long, help = "Dump channel configuration to ffplayout_{channel}.toml")]
|
||||
pub dump_config: Option<i32>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "import channel configuration from file. Input must be `{channel id} {path to toml}`",
|
||||
num_args = 2
|
||||
)]
|
||||
pub import_config: Option<Vec<String>>,
|
||||
|
||||
#[clap(long, help = "List available channel ids")]
|
||||
pub list_channels: bool,
|
||||
|
||||
@ -68,3 +89,112 @@ pub struct Args {
|
||||
#[clap(short, long, help = "Admin password")]
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
||||
let mut args = ARGS.clone();
|
||||
|
||||
if args.ask {
|
||||
let mut user = String::new();
|
||||
print!("Username: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
stdin()
|
||||
.read_line(&mut user)
|
||||
.expect("Did not enter a correct name?");
|
||||
if let Some('\n') = user.chars().next_back() {
|
||||
user.pop();
|
||||
}
|
||||
if let Some('\r') = user.chars().next_back() {
|
||||
user.pop();
|
||||
}
|
||||
|
||||
args.username = Some(user);
|
||||
|
||||
print!("Password: ");
|
||||
stdout().flush().unwrap();
|
||||
let password = read_password();
|
||||
|
||||
args.password = password.ok();
|
||||
|
||||
let mut mail = String::new();
|
||||
print!("Mail: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
stdin()
|
||||
.read_line(&mut mail)
|
||||
.expect("Did not enter a correct name?");
|
||||
if let Some('\n') = mail.chars().next_back() {
|
||||
mail.pop();
|
||||
}
|
||||
if let Some('\r') = mail.chars().next_back() {
|
||||
mail.pop();
|
||||
}
|
||||
|
||||
args.mail = Some(mail);
|
||||
}
|
||||
|
||||
if let Some(username) = args.username {
|
||||
if args.mail.is_none() || args.password.is_none() {
|
||||
error!("Mail/password missing!");
|
||||
return Err(1);
|
||||
}
|
||||
|
||||
let user = User {
|
||||
id: 0,
|
||||
mail: Some(args.mail.unwrap()),
|
||||
username: username.clone(),
|
||||
password: args.password.unwrap(),
|
||||
role_id: Some(1),
|
||||
channel_id: Some(1),
|
||||
token: None,
|
||||
};
|
||||
|
||||
match db_pool().await {
|
||||
Ok(conn) => {
|
||||
if let Err(e) = insert_user(&conn, user).await {
|
||||
error!("{e}");
|
||||
return Err(1);
|
||||
};
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
return Err(1);
|
||||
}
|
||||
};
|
||||
|
||||
info!("Create admin user \"{username}\" done...");
|
||||
|
||||
return Err(0);
|
||||
}
|
||||
|
||||
if let Some(id) = ARGS.dump_config {
|
||||
match PlayoutConfig::dump(&pool, id).await {
|
||||
Ok(_) => {
|
||||
info!("Dump config to: ffplayout_{id}.toml");
|
||||
exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Dump config: {e}");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(import) = &ARGS.import_config {
|
||||
match PlayoutConfig::import(&pool, import.clone()).await {
|
||||
Ok(_) => {
|
||||
info!("Import config done...");
|
||||
exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,12 +9,15 @@ use flexi_logger::Level;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shlex::split;
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::db::{handles, models};
|
||||
use crate::utils::{free_tcp_socket, time_to_sec};
|
||||
use crate::vec_strings;
|
||||
use crate::AdvancedConfig;
|
||||
|
||||
use super::errors::ServiceError;
|
||||
|
||||
pub const DUMMY_LEN: f64 = 60.0;
|
||||
pub const IMAGE_FORMAT: [&str; 21] = [
|
||||
"bmp", "dds", "dpx", "exr", "gif", "hdr", "j2k", "jpg", "jpeg", "pcx", "pfm", "pgm", "phm",
|
||||
@ -214,7 +217,7 @@ impl General {
|
||||
help_text: config.general_help.clone(),
|
||||
id: config.id,
|
||||
channel_id: config.channel_id,
|
||||
stop_threshold: config.stop_threshold,
|
||||
stop_threshold: config.general_stop_threshold,
|
||||
generate: None,
|
||||
ffmpeg_filters: vec![],
|
||||
ffmpeg_libs: vec![],
|
||||
@ -235,21 +238,21 @@ pub struct Mail {
|
||||
pub sender_pass: String,
|
||||
pub recipient: String,
|
||||
pub mail_level: Level,
|
||||
pub interval: u64,
|
||||
pub interval: i64,
|
||||
}
|
||||
|
||||
impl Mail {
|
||||
fn new(config: &models::Configuration) -> Self {
|
||||
Self {
|
||||
help_text: config.mail_help.clone(),
|
||||
subject: config.subject.clone(),
|
||||
smtp_server: config.smtp_server.clone(),
|
||||
starttls: config.starttls,
|
||||
sender_addr: config.sender_addr.clone(),
|
||||
sender_pass: config.sender_pass.clone(),
|
||||
recipient: config.recipient.clone(),
|
||||
subject: config.mail_subject.clone(),
|
||||
smtp_server: config.mail_smtp.clone(),
|
||||
starttls: config.mail_starttls,
|
||||
sender_addr: config.mail_addr.clone(),
|
||||
sender_pass: config.mail_pass.clone(),
|
||||
recipient: config.mail_recipient.clone(),
|
||||
mail_level: string_to_log_level(config.mail_level.clone()),
|
||||
interval: config.interval as u64,
|
||||
interval: config.mail_interval,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,7 +268,7 @@ impl Default for Mail {
|
||||
sender_pass: String::default(),
|
||||
recipient: String::default(),
|
||||
mail_level: Level::Debug,
|
||||
interval: u64::default(),
|
||||
interval: i64::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -283,11 +286,11 @@ impl Logging {
|
||||
fn new(config: &models::Configuration) -> Self {
|
||||
Self {
|
||||
help_text: config.logging_help.clone(),
|
||||
ffmpeg_level: config.ffmpeg_level.clone(),
|
||||
ingest_level: config.ingest_level.clone(),
|
||||
detect_silence: config.detect_silence,
|
||||
ffmpeg_level: config.logging_ffmpeg_level.clone(),
|
||||
ingest_level: config.logging_ingest_level.clone(),
|
||||
detect_silence: config.logging_detect_silence,
|
||||
ignore_lines: config
|
||||
.ignore_lines
|
||||
.logging_ignore
|
||||
.split(';')
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
@ -311,7 +314,7 @@ pub struct Processing {
|
||||
pub add_logo: bool,
|
||||
pub logo: String,
|
||||
pub logo_scale: String,
|
||||
pub logo_opacity: f32,
|
||||
pub logo_opacity: f64,
|
||||
pub logo_position: String,
|
||||
pub audio_tracks: i32,
|
||||
pub audio_channels: u8,
|
||||
@ -326,23 +329,23 @@ impl Processing {
|
||||
Self {
|
||||
help_text: config.processing_help.clone(),
|
||||
mode: ProcessMode::new(&config.processing_mode.clone()),
|
||||
audio_only: config.audio_only,
|
||||
audio_track_index: config.audio_track_index,
|
||||
copy_audio: config.copy_audio,
|
||||
copy_video: config.copy_video,
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
aspect: config.aspect,
|
||||
fps: config.fps,
|
||||
add_logo: config.add_logo,
|
||||
logo: config.logo.clone(),
|
||||
logo_scale: config.logo_scale.clone(),
|
||||
logo_opacity: config.logo_opacity,
|
||||
logo_position: config.logo_position.clone(),
|
||||
audio_tracks: config.audio_tracks,
|
||||
audio_channels: config.audio_channels,
|
||||
volume: config.volume,
|
||||
custom_filter: config.decoder_filter.clone(),
|
||||
audio_only: config.processing_audio_only,
|
||||
audio_track_index: config.processing_audio_track_index,
|
||||
copy_audio: config.processing_copy_audio,
|
||||
copy_video: config.processing_copy_video,
|
||||
width: config.processing_width,
|
||||
height: config.processing_height,
|
||||
aspect: config.processing_aspect,
|
||||
fps: config.processing_fps,
|
||||
add_logo: config.processing_add_logo,
|
||||
logo: config.processing_logo.clone(),
|
||||
logo_scale: config.processing_logo_scale.clone(),
|
||||
logo_opacity: config.processing_logo_opacity,
|
||||
logo_position: config.processing_logo_position.clone(),
|
||||
audio_tracks: config.processing_audio_tracks,
|
||||
audio_channels: config.processing_audio_channels,
|
||||
volume: config.processing_volume,
|
||||
custom_filter: config.processing_filter.clone(),
|
||||
cmd: None,
|
||||
}
|
||||
}
|
||||
@ -373,7 +376,6 @@ impl Ingest {
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||
pub struct Playlist {
|
||||
pub help_text: String,
|
||||
pub path: PathBuf,
|
||||
pub day_start: String,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub start_sec: Option<f64>,
|
||||
@ -387,12 +389,11 @@ impl Playlist {
|
||||
fn new(config: &models::Configuration) -> Self {
|
||||
Self {
|
||||
help_text: config.playlist_help.clone(),
|
||||
path: PathBuf::from(config.playlist_path.clone()),
|
||||
day_start: config.day_start.clone(),
|
||||
day_start: config.playlist_day_start.clone(),
|
||||
start_sec: None,
|
||||
length: config.length.clone(),
|
||||
length: config.playlist_length.clone(),
|
||||
length_sec: None,
|
||||
infinit: config.infinit,
|
||||
infinit: config.playlist_infinit,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,7 +401,6 @@ impl Playlist {
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||
pub struct Storage {
|
||||
pub help_text: String,
|
||||
pub path: PathBuf,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub paths: Vec<PathBuf>,
|
||||
pub filler: PathBuf,
|
||||
@ -412,15 +412,14 @@ impl Storage {
|
||||
fn new(config: &models::Configuration) -> Self {
|
||||
Self {
|
||||
help_text: config.storage_help.clone(),
|
||||
path: PathBuf::from(config.storage_path.clone()),
|
||||
paths: vec![],
|
||||
filler: PathBuf::from(config.filler.clone()),
|
||||
filler: PathBuf::from(config.storage_filler.clone()),
|
||||
extensions: config
|
||||
.extensions
|
||||
.storage_extensions
|
||||
.split(';')
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
shuffle: config.shuffle,
|
||||
shuffle: config.storage_shuffle,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -445,14 +444,14 @@ impl Text {
|
||||
fn new(config: &models::Configuration) -> Self {
|
||||
Self {
|
||||
help_text: config.text_help.clone(),
|
||||
add_text: config.add_text,
|
||||
add_text: config.text_add,
|
||||
node_pos: None,
|
||||
zmq_stream_socket: None,
|
||||
zmq_server_socket: None,
|
||||
fontfile: config.fontfile.clone(),
|
||||
fontfile: config.text_font.clone(),
|
||||
text_from_filename: config.text_from_filename,
|
||||
style: config.style.clone(),
|
||||
regex: config.regex.clone(),
|
||||
style: config.text_style.clone(),
|
||||
regex: config.text_regex.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -683,6 +682,34 @@ impl PlayoutConfig {
|
||||
output,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn dump(pool: &Pool<Sqlite>, id: i32) -> Result<(), ServiceError> {
|
||||
let config = Self::new(pool, id).await;
|
||||
|
||||
let toml_string = toml_edit::ser::to_string_pretty(&config)?;
|
||||
tokio::fs::write(&format!("ffplayout_{id}.toml"), toml_string).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn import(pool: &Pool<Sqlite>, import: Vec<String>) -> Result<(), ServiceError> {
|
||||
let id = import[0].parse::<i32>()?;
|
||||
let path = Path::new(&import[1]);
|
||||
|
||||
if path.is_file() {
|
||||
let mut file = tokio::fs::File::open(path).await?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).await?;
|
||||
|
||||
let config: PlayoutConfig = toml_edit::de::from_str(&contents).unwrap();
|
||||
|
||||
handles::update_configuration(pool, id, config).await?;
|
||||
} else {
|
||||
return Err(ServiceError::BadRequest("Path not exists!".to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// impl Default for PlayoutConfig {
|
||||
|
@ -141,12 +141,13 @@ pub async fn browser(
|
||||
let mut extensions = config.storage.extensions.clone();
|
||||
extensions.append(&mut channel_extensions);
|
||||
|
||||
let (path, parent, path_component) = norm_abs_path(&config.storage.path, &path_obj.source)?;
|
||||
let (path, parent, path_component) =
|
||||
norm_abs_path(&config.global.storage_path, &path_obj.source)?;
|
||||
|
||||
let parent_path = if !path_component.is_empty() {
|
||||
path.parent().unwrap()
|
||||
} else {
|
||||
&config.storage.path
|
||||
&config.global.storage_path
|
||||
};
|
||||
|
||||
let mut obj = PathObject::new(path_component, Some(parent));
|
||||
@ -237,7 +238,7 @@ pub async fn create_directory(
|
||||
config: &PlayoutConfig,
|
||||
path_obj: &PathObject,
|
||||
) -> Result<HttpResponse, ServiceError> {
|
||||
let (path, _, _) = norm_abs_path(&config.storage.path, &path_obj.source)?;
|
||||
let (path, _, _) = norm_abs_path(&config.global.storage_path, &path_obj.source)?;
|
||||
|
||||
if let Err(e) = fs::create_dir_all(&path).await {
|
||||
return Err(ServiceError::BadRequest(e.to_string()));
|
||||
@ -306,8 +307,8 @@ pub async fn rename_file(
|
||||
config: &PlayoutConfig,
|
||||
move_object: &MoveObject,
|
||||
) -> Result<MoveObject, ServiceError> {
|
||||
let (source_path, _, _) = norm_abs_path(&config.storage.path, &move_object.source)?;
|
||||
let (mut target_path, _, _) = norm_abs_path(&config.storage.path, &move_object.target)?;
|
||||
let (source_path, _, _) = norm_abs_path(&config.global.storage_path, &move_object.source)?;
|
||||
let (mut target_path, _, _) = norm_abs_path(&config.global.storage_path, &move_object.target)?;
|
||||
|
||||
if !source_path.exists() {
|
||||
return Err(ServiceError::BadRequest("Source file not exist!".into()));
|
||||
@ -339,7 +340,7 @@ pub async fn remove_file_or_folder(
|
||||
config: &PlayoutConfig,
|
||||
source_path: &str,
|
||||
) -> Result<(), ServiceError> {
|
||||
let (source, _, _) = norm_abs_path(&config.storage.path, source_path)?;
|
||||
let (source, _, _) = norm_abs_path(&config.global.storage_path, source_path)?;
|
||||
|
||||
if !source.exists() {
|
||||
return Err(ServiceError::BadRequest("Source does not exists!".into()));
|
||||
@ -371,7 +372,7 @@ pub async fn remove_file_or_folder(
|
||||
}
|
||||
|
||||
async fn valid_path(config: &PlayoutConfig, path: &str) -> Result<PathBuf, ServiceError> {
|
||||
let (test_path, _, _) = norm_abs_path(&config.storage.path, path)?;
|
||||
let (test_path, _, _) = norm_abs_path(&config.global.storage_path, path)?;
|
||||
|
||||
if !test_path.is_dir() {
|
||||
return Err(ServiceError::BadRequest("Target folder not exists!".into()));
|
||||
|
@ -213,7 +213,7 @@ pub fn playlist_generator(manager: &ChannelManager) -> Result<Vec<JsonPlaylist>,
|
||||
}
|
||||
}
|
||||
};
|
||||
let playlist_root = &config.playlist.path;
|
||||
let playlist_root = &config.global.playlist_path;
|
||||
let mut playlists = vec![];
|
||||
let mut date_range = vec![];
|
||||
let mut from_template = false;
|
||||
@ -221,7 +221,7 @@ pub fn playlist_generator(manager: &ChannelManager) -> Result<Vec<JsonPlaylist>,
|
||||
if !playlist_root.is_dir() {
|
||||
error!(
|
||||
"Playlist folder <b><magenta>{:?}</></b> not exists!",
|
||||
config.playlist.path
|
||||
config.global.playlist_path
|
||||
);
|
||||
|
||||
exit(1);
|
||||
|
@ -347,7 +347,7 @@ pub fn mail_queue(mail_queues: Arc<Mutex<Vec<Arc<Mutex<MailQueue>>>>>) {
|
||||
|
||||
// Process mail queues and send emails
|
||||
for queue in queues.iter_mut() {
|
||||
let interval = round_to_nearest_ten(counter);
|
||||
let interval = round_to_nearest_ten(counter as i64);
|
||||
let mut q_lock = queue.lock().unwrap_or_else(|poisoned| {
|
||||
error!("Queue mutex was poisoned");
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
env, fmt,
|
||||
fs::{self, metadata},
|
||||
io::{stdin, stdout, Write},
|
||||
net::TcpListener,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
@ -12,7 +11,7 @@ use log::*;
|
||||
use path_clean::PathClean;
|
||||
use rand::Rng;
|
||||
use regex::Regex;
|
||||
use rpassword::read_password;
|
||||
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserialize, Deserializer, Serialize,
|
||||
@ -31,7 +30,6 @@ pub mod playlist;
|
||||
pub mod system;
|
||||
pub mod task_runner;
|
||||
|
||||
use crate::db::{db_pool, handles::insert_user, models::User};
|
||||
use crate::player::utils::time_to_sec;
|
||||
use crate::utils::{errors::ServiceError, logging::log_file_path};
|
||||
use crate::ARGS;
|
||||
@ -208,87 +206,6 @@ pub fn public_path() -> PathBuf {
|
||||
PathBuf::from("./public/")
|
||||
}
|
||||
|
||||
pub async fn run_args() -> Result<(), i32> {
|
||||
let mut args = ARGS.clone();
|
||||
|
||||
if args.ask {
|
||||
let mut user = String::new();
|
||||
print!("Username: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
stdin()
|
||||
.read_line(&mut user)
|
||||
.expect("Did not enter a correct name?");
|
||||
if let Some('\n') = user.chars().next_back() {
|
||||
user.pop();
|
||||
}
|
||||
if let Some('\r') = user.chars().next_back() {
|
||||
user.pop();
|
||||
}
|
||||
|
||||
args.username = Some(user);
|
||||
|
||||
print!("Password: ");
|
||||
stdout().flush().unwrap();
|
||||
let password = read_password();
|
||||
|
||||
args.password = password.ok();
|
||||
|
||||
let mut mail = String::new();
|
||||
print!("Mail: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
stdin()
|
||||
.read_line(&mut mail)
|
||||
.expect("Did not enter a correct name?");
|
||||
if let Some('\n') = mail.chars().next_back() {
|
||||
mail.pop();
|
||||
}
|
||||
if let Some('\r') = mail.chars().next_back() {
|
||||
mail.pop();
|
||||
}
|
||||
|
||||
args.mail = Some(mail);
|
||||
}
|
||||
|
||||
if let Some(username) = args.username {
|
||||
if args.mail.is_none() || args.password.is_none() {
|
||||
error!("Mail/password missing!");
|
||||
return Err(1);
|
||||
}
|
||||
|
||||
let user = User {
|
||||
id: 0,
|
||||
mail: Some(args.mail.unwrap()),
|
||||
username: username.clone(),
|
||||
password: args.password.unwrap(),
|
||||
role_id: Some(1),
|
||||
channel_id: Some(1),
|
||||
token: None,
|
||||
};
|
||||
|
||||
match db_pool().await {
|
||||
Ok(conn) => {
|
||||
if let Err(e) = insert_user(&conn, user).await {
|
||||
error!("{e}");
|
||||
return Err(1);
|
||||
};
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
return Err(1);
|
||||
}
|
||||
};
|
||||
|
||||
info!("Create admin user \"{username}\" done...");
|
||||
|
||||
return Err(0);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_log_file(channel_id: &i32, date: &str) -> Result<String, ServiceError> {
|
||||
let mut date_str = "".to_string();
|
||||
|
||||
@ -378,7 +295,7 @@ pub fn free_tcp_socket(exclude_socket: String) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn round_to_nearest_ten(num: u64) -> u64 {
|
||||
pub fn round_to_nearest_ten(num: i64) -> i64 {
|
||||
if num % 10 >= 5 {
|
||||
((num / 10) + 1) * 10
|
||||
} else {
|
||||
|
@ -13,7 +13,7 @@ pub async fn read_playlist(
|
||||
config: &PlayoutConfig,
|
||||
date: String,
|
||||
) -> Result<JsonPlaylist, ServiceError> {
|
||||
let (path, _, _) = norm_abs_path(&config.playlist.path, "")?;
|
||||
let (path, _, _) = norm_abs_path(&config.global.playlist_path, "")?;
|
||||
let mut playlist_path = path;
|
||||
let d: Vec<&str> = date.split('-').collect();
|
||||
playlist_path = playlist_path
|
||||
@ -33,7 +33,7 @@ pub async fn write_playlist(
|
||||
json_data: JsonPlaylist,
|
||||
) -> Result<String, ServiceError> {
|
||||
let date = json_data.date.clone();
|
||||
let mut playlist_path = config.playlist.path.clone();
|
||||
let mut playlist_path = config.global.playlist_path.clone();
|
||||
let d: Vec<&str> = date.split('-').collect();
|
||||
|
||||
if !playlist_path
|
||||
@ -93,7 +93,7 @@ pub fn generate_playlist(manager: ChannelManager) -> Result<JsonPlaylist, Servic
|
||||
|
||||
for path in &source.paths {
|
||||
let (safe_path, _, _) =
|
||||
norm_abs_path(&config.storage.path, &path.to_string_lossy())?;
|
||||
norm_abs_path(&config.global.storage_path, &path.to_string_lossy())?;
|
||||
paths.push(safe_path);
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ pub fn generate_playlist(manager: ChannelManager) -> Result<JsonPlaylist, Servic
|
||||
}
|
||||
|
||||
pub async fn delete_playlist(config: &PlayoutConfig, date: &str) -> Result<String, ServiceError> {
|
||||
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
||||
let mut playlist_path = PathBuf::from(&config.global.playlist_path);
|
||||
let d: Vec<&str> = date.split('-').collect();
|
||||
playlist_path = playlist_path
|
||||
.join(d[0])
|
||||
|
@ -118,7 +118,7 @@ pub fn stat(config: PlayoutConfig) -> SystemStat {
|
||||
|
||||
for disk in &*disks {
|
||||
if disk.mount_point().to_string_lossy().len() > 1
|
||||
&& config.storage.path.starts_with(disk.mount_point())
|
||||
&& config.global.storage_path.starts_with(disk.mount_point())
|
||||
{
|
||||
storage.path = disk.name().to_string_lossy().to_string();
|
||||
storage.total = disk.total_space();
|
||||
|
@ -67,58 +67,58 @@ CREATE TABLE
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
channel_id INTEGER NOT NULL DEFAULT 1,
|
||||
general_help TEXT NOT NULL DEFAULT "Sometimes it can happen, that a file is corrupt but still playable, this can produce an streaming error over all following files. The only way in this case is, to stop ffplayout and start it again. Here we only say when it stops, the starting process is in your hand. Best way is a systemd service on linux.\n'stop_threshold' stop ffplayout, if it is async in time above this value. A number below 3 can cause unexpected errors.",
|
||||
stop_threshold REAL NOT NULL DEFAULT 11.0,
|
||||
general_stop_threshold REAL NOT NULL DEFAULT 11.0,
|
||||
mail_help TEXT NOT NULL DEFAULT "Send error messages to email address, like missing playlist; invalid json format; missing clip path. Leave recipient blank, if you don't need this.\n'mail_level' can be INFO, WARNING or ERROR.\n'interval' means seconds until a new mail will be sended, value must be in increments of 10.",
|
||||
subject TEXT NOT NULL DEFAULT "Playout Error",
|
||||
smtp_server TEXT NOT NULL DEFAULT "mail.example.org",
|
||||
sender_addr TEXT NOT NULL DEFAULT "ffplayout@example.org",
|
||||
sender_pass TEXT NOT NULL DEFAULT "",
|
||||
recipient TEXT NOT NULL DEFAULT "",
|
||||
starttls INTEGER NOT NULL DEFAULT 0,
|
||||
mail_subject TEXT NOT NULL DEFAULT "Playout Error",
|
||||
mail_smtp TEXT NOT NULL DEFAULT "mail.example.org",
|
||||
mail_addr TEXT NOT NULL DEFAULT "ffplayout@example.org",
|
||||
mail_pass TEXT NOT NULL DEFAULT "",
|
||||
mail_recipient TEXT NOT NULL DEFAULT "",
|
||||
mail_starttls INTEGER NOT NULL DEFAULT 0,
|
||||
mail_level TEXT NOT NULL DEFAULT "ERROR",
|
||||
interval INTEGER NOT NULL DEFAULT 120,
|
||||
mail_interval INTEGER NOT NULL DEFAULT 120,
|
||||
logging_help TEXT NOT NULL DEFAULT "If 'log_to_file' is true, log to file, when is false log to console. \n'local_time' to false will set log timestamps to UTC. Path to /var/log/ only if you run this program as daemon.\n'level' can be DEBUG, INFO, WARNING, ERROR.\n'ffmpeg_level/ingest_level' can be INFO, WARNING, ERROR.\n'detect_silence' logs an error message if the audio line is silent for 15 seconds during the validation process.\n'ignore_lines' makes logging to ignore strings that contains matched lines, in frontend is a semicolon separated list.",
|
||||
ffmpeg_level TEXT NOT NULL DEFAULT "ERROR",
|
||||
ingest_level TEXT NOT NULL DEFAULT "ERROR",
|
||||
detect_silence INTEGER NOT NULL DEFAULT 1,
|
||||
ignore_lines TEXT NOT NULL DEFAULT "P sub_mb_type 4 out of range at;error while decoding MB;negative number of zero coeffs at;out of range intra chroma pred mode;non-existing SPS 0 referenced in buffering period",
|
||||
logging_ffmpeg_level TEXT NOT NULL DEFAULT "ERROR",
|
||||
logging_ingest_level TEXT NOT NULL DEFAULT "ERROR",
|
||||
logging_detect_silence INTEGER NOT NULL DEFAULT 1,
|
||||
logging_ignore TEXT NOT NULL DEFAULT "P sub_mb_type 4 out of range at;error while decoding MB;negative number of zero coeffs at;out of range intra chroma pred mode;non-existing SPS 0 referenced in buffering period",
|
||||
processing_help TEXT NOT NULL DEFAULT "Default processing for all clips, to have them unique. Mode can be playlist or folder.\n'aspect' must be a float number.'logo' is only used if the path exist, path is relative to your storage folder.\n'logo_scale' scale the logo to target size, leave it blank when no scaling is needed, format is 'width:height', for example '100:-1' for proportional scaling. With 'logo_opacity' logo can become transparent.\nWith 'audio_tracks' it is possible to configure how many audio tracks should be processed.\n'audio_channels' can be use, if audio has more channels then only stereo.\nWith 'logo_position' in format 'x:y' you set the logo position.\nWith 'custom_filter' it is possible, to apply further filters. The filter outputs should end with [c_v_out] for video filter, and [c_a_out] for audio filter.",
|
||||
processing_mode TEXT NOT NULL DEFAULT "playlist",
|
||||
audio_only INTEGER NOT NULL DEFAULT 0,
|
||||
copy_audio INTEGER NOT NULL DEFAULT 0,
|
||||
copy_video INTEGER NOT NULL DEFAULT 0,
|
||||
width INTEGER NOT NULL DEFAULT 1280,
|
||||
height INTEGER NOT NULL DEFAULT 720,
|
||||
aspect REAL NOT NULL DEFAULT 1.778,
|
||||
fps REAL NOT NULL DEFAULT 25.0,
|
||||
add_logo INTEGER NOT NULL DEFAULT 1,
|
||||
logo TEXT NOT NULL DEFAULT "graphics/logo.png",
|
||||
logo_scale TEXT NOT NULL DEFAULT "",
|
||||
logo_opacity REAL NOT NULL DEFAULT 0.7,
|
||||
logo_position TEXT NOT NULL DEFAULT "W-w-12:12",
|
||||
audio_tracks INTEGER NOT NULL DEFAULT 1,
|
||||
audio_track_index INTEGER NOT NULL DEFAULT -1,
|
||||
audio_channels INTEGER NOT NULL DEFAULT 2,
|
||||
volume REAL NOT NULL DEFAULT 1.0,
|
||||
decoder_filter TEXT NOT NULL DEFAULT "",
|
||||
processing_audio_only INTEGER NOT NULL DEFAULT 0,
|
||||
processing_copy_audio INTEGER NOT NULL DEFAULT 0,
|
||||
processing_copy_video INTEGER NOT NULL DEFAULT 0,
|
||||
processing_width INTEGER NOT NULL DEFAULT 1280,
|
||||
processing_height INTEGER NOT NULL DEFAULT 720,
|
||||
processing_aspect REAL NOT NULL DEFAULT 1.778,
|
||||
processing_fps REAL NOT NULL DEFAULT 25.0,
|
||||
processing_add_logo INTEGER NOT NULL DEFAULT 1,
|
||||
processing_logo TEXT NOT NULL DEFAULT "graphics/logo.png",
|
||||
processing_logo_scale TEXT NOT NULL DEFAULT "",
|
||||
processing_logo_opacity REAL NOT NULL DEFAULT 0.7,
|
||||
processing_logo_position TEXT NOT NULL DEFAULT "W-w-12:12",
|
||||
processing_audio_tracks INTEGER NOT NULL DEFAULT 1,
|
||||
processing_audio_track_index INTEGER NOT NULL DEFAULT -1,
|
||||
processing_audio_channels INTEGER NOT NULL DEFAULT 2,
|
||||
processing_volume REAL NOT NULL DEFAULT 1.0,
|
||||
processing_filter TEXT NOT NULL DEFAULT "",
|
||||
ingest_help "Run a server for a ingest stream. This stream will override the normal streaming until is done. There is only a very simple authentication mechanism, which check if the stream name is correct.\n'custom_filter' can be used in the same way then the one in the process section.",
|
||||
ingest_enable INTEGER NOT NULL DEFAULT 0,
|
||||
ingest_param TEXT NOT NULL DEFAULT "-f live_flv -listen 1 -i rtmp://127.0.0.1:1936/live/stream",
|
||||
ingest_filter TEXT NOT NULL DEFAULT "",
|
||||
playlist_help TEXT NOT NULL DEFAULT "'path' can be a path to a single file, or a directory. For directory put only the root folder, for example '/playlists', subdirectories are read by the program. Subdirectories needs this structure '/playlists/2018/01'.\n'day_start' means at which time the playlist should start, leave day_start blank when playlist should always start at the begin. 'length' represent the target length from playlist, when is blank real length will not consider.\n'infinit: true' works with single playlist file and loops it infinitely.",
|
||||
day_start TEXT NOT NULL DEFAULT "05:59:25",
|
||||
length TEXT NOT NULL DEFAULT "24:00:00",
|
||||
infinit INTEGER NOT NULL DEFAULT 0,
|
||||
playlist_day_start TEXT NOT NULL DEFAULT "05:59:25",
|
||||
playlist_length TEXT NOT NULL DEFAULT "24:00:00",
|
||||
playlist_infinit INTEGER NOT NULL DEFAULT 0,
|
||||
storage_help TEXT NOT NULL DEFAULT "'filler' is for playing instead of a missing file or fill the end to reach 24 hours, can be a file or folder, it will loop when is necessary.\n'extensions' search only files with this extension. Set 'shuffle' to 'true' to pick files randomly.",
|
||||
filler TEXT NOT NULL DEFAULT "filler/filler.mp4",
|
||||
extensions TEXT NOT NULL DEFAULT "mp4;mkv;webm",
|
||||
shuffle INTEGER NOT NULL DEFAULT 1,
|
||||
storage_filler TEXT NOT NULL DEFAULT "filler/filler.mp4",
|
||||
storage_extensions TEXT NOT NULL DEFAULT "mp4;mkv;webm",
|
||||
storage_shuffle INTEGER NOT NULL DEFAULT 1,
|
||||
text_help TEXT NOT NULL DEFAULT "Overlay text in combination with libzmq for remote text manipulation. fontfile is a relative path to your storage folder.\n'text_from_filename' activate the extraction from text of a filename. With 'style' you can define the drawtext parameters like position, color, etc. Post Text over API will override this. With 'regex' you can format file names, to get a title from it.",
|
||||
add_text INTEGER NOT NULL DEFAULT 1,
|
||||
text_add INTEGER NOT NULL DEFAULT 1,
|
||||
text_from_filename INTEGER NOT NULL DEFAULT 0,
|
||||
fontfile TEXT NOT NULL DEFAULT "fonts/DejaVuSans.ttf",
|
||||
style TEXT NOT NULL DEFAULT "x=(w-tw)/2:y=(h-line_h)*0.9:fontsize=24:fontcolor=#ffffff:box=1:boxcolor=#000000:boxborderw=4",
|
||||
regex TEXT NOT NULL DEFAULT "^.+[/\\](.*)(.mp4|.mkv|.webm)$",
|
||||
text_font TEXT NOT NULL DEFAULT "fonts/DejaVuSans.ttf",
|
||||
text_style TEXT NOT NULL DEFAULT "x=(w-tw)/2:y=(h-line_h)*0.9:fontsize=24:fontcolor=#ffffff:box=1:boxcolor=#000000:boxborderw=4",
|
||||
text_regex TEXT NOT NULL DEFAULT "^.+[/\\](.*)(.mp4|.mkv|.webm)$",
|
||||
task_help TEXT NOT NULL DEFAULT "Run an external program with a given media object. The media object is in json format and contains all the information about the current clip. The external program can be a script or a binary, but should only run for a short time.",
|
||||
task_enable INTEGER NOT NULL DEFAULT 0,
|
||||
task_path TEXT NOT NULL DEFAULT "",
|
||||
|
@ -90,7 +90,7 @@ fn test_generate_playlist_from_folder() {
|
||||
config.processing.mode = Playlist;
|
||||
config.storage.filler = "assets/".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
|
||||
let playlist = generate_playlist(manager);
|
||||
|
||||
@ -143,7 +143,7 @@ fn test_generate_playlist_from_template() {
|
||||
config.processing.mode = Playlist;
|
||||
config.storage.filler = "assets/".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
|
||||
let playlist = generate_playlist(manager);
|
||||
|
||||
|
@ -49,7 +49,7 @@ fn test_gen_source() {
|
||||
config.playlist.start_sec = Some(0.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".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);
|
||||
@ -104,7 +104,7 @@ fn playlist_missing() {
|
||||
config.playlist.start_sec = Some(0.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
@ -146,7 +146,7 @@ fn playlist_next_missing() {
|
||||
config.playlist.start_sec = Some(0.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
@ -188,7 +188,7 @@ fn playlist_to_short() {
|
||||
config.playlist.start_sec = Some(21600.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
@ -230,7 +230,7 @@ fn playlist_init_after_list_end() {
|
||||
config.playlist.start_sec = Some(21600.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
@ -272,7 +272,7 @@ fn playlist_change_at_midnight() {
|
||||
config.playlist.start_sec = Some(0.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
@ -314,7 +314,7 @@ fn playlist_change_before_midnight() {
|
||||
config.playlist.start_sec = Some(0.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
@ -356,7 +356,7 @@ fn playlist_change_at_six() {
|
||||
config.playlist.start_sec = Some(21600.0);
|
||||
config.playlist.length = "24:00:00".into();
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
config.playlist.path = "assets/playlists".into();
|
||||
config.global.playlist_path = "assets/playlists".into();
|
||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||
config.output.mode = Null;
|
||||
config.output.output_count = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user