rename GlobalConfig and Playlist struct, get playlist config

This commit is contained in:
jb-alvarado 2022-06-14 12:09:31 +02:00
parent 74f968e284
commit b97f30c2b4
27 changed files with 200 additions and 109 deletions

View File

@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
use crate::api::utils::GlobalSettings;
// Token lifetime and Secret key are hardcoded for clarity
const JWT_EXPIRATION_MINUTES: i64 = 60;
// Token lifetime
const JWT_EXPIRATION_DAYS: i64 = 7;
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Claims {
@ -23,7 +23,7 @@ impl Claims {
id,
username,
role,
exp: (Utc::now() + Duration::minutes(JWT_EXPIRATION_MINUTES)).timestamp(),
exp: (Utc::now() + Duration::days(JWT_EXPIRATION_DAYS)).timestamp(),
}
}
}

View File

@ -1,35 +1,23 @@
use std::path::Path;
use argon2::{
password_hash::{rand_core::OsRng, SaltString},
Argon2, PasswordHasher,
};
use faccess::PathExt;
use rand::{distributions::Alphanumeric, Rng};
use simplelog::*;
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool};
use crate::api::models::{Settings, User};
use crate::api::utils::GlobalSettings;
use crate::api::{
models::{Settings, User},
utils::db_path,
};
#[derive(Debug, sqlx::FromRow)]
struct Role {
name: String,
}
pub fn db_path() -> Result<String, Box<dyn std::error::Error>> {
let sys_path = Path::new("/usr/share/ffplayout");
let mut db_path = String::from("./ffplayout.db");
if sys_path.is_dir() && sys_path.writable() {
db_path = String::from("/usr/share/ffplayout/ffplayout.db");
} else if Path::new("./assets").is_dir() {
db_path = String::from("./assets/ffplayout.db");
}
Ok(db_path)
}
async fn create_schema() -> Result<SqliteQueryResult, sqlx::Error> {
let conn = db_connection().await?;
let query = "PRAGMA foreign_keys = ON;

View File

@ -1,5 +1,5 @@
use actix_web::{get, http::StatusCode, patch, post, put, web, Responder};
use actix_web_grants::proc_macro::has_any_role;
use actix_web_grants::{permissions::AuthDetails, proc_macro::has_any_role};
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, SaltString},
Argon2, PasswordHasher, PasswordVerifier,
@ -14,9 +14,11 @@ use crate::api::{
db_add_user, db_get_settings, db_login, db_role, db_update_settings, db_update_user,
},
models::{LoginUser, Settings, User},
utils::Role,
utils::{read_playout_config, Role},
};
use crate::utils::playout_config;
#[derive(Serialize)]
struct ResponseObj<T> {
message: String,
@ -24,6 +26,37 @@ struct ResponseObj<T> {
data: Option<T>,
}
#[derive(Debug, Serialize, Clone)]
struct ResponsePlayoutConfig {
general: Option<playout_config::General>,
rpc_server: Option<playout_config::RpcServer>,
mail: Option<playout_config::Mail>,
logging: Option<playout_config::Logging>,
processing: Option<playout_config::Processing>,
ingest: Option<playout_config::Ingest>,
playlist: Option<playout_config::Playlist>,
storage: Option<playout_config::Storage>,
text: Option<playout_config::Text>,
out: Option<playout_config::Out>,
}
impl ResponsePlayoutConfig {
fn new() -> Self {
Self {
general: None,
rpc_server: None,
mail: None,
logging: None,
processing: None,
ingest: None,
playlist: None,
storage: None,
text: None,
out: None,
}
}
}
/// curl -X GET http://127.0.0.1:8080/api/settings/1 -H "Authorization: Bearer <TOKEN>"
#[get("/settings/{id}")]
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
@ -56,13 +89,35 @@ async fn patch_settings(
Err(ServiceError::InternalServerError)
}
/// curl -X GET http://localhost:8080/api/playout/config/1 --header 'Authorization: <TOKEN>'
#[get("/playout/config/{id}")]
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
async fn get_playout_config(id: web::Path<i64>) -> Result<impl Responder, ServiceError> {
async fn get_playout_config(
id: web::Path<i64>,
details: AuthDetails<Role>,
) -> Result<impl Responder, ServiceError> {
if let Ok(settings) = db_get_settings(&id).await {
println!("{:?}", settings.config_path);
if let Ok(config) = read_playout_config(&settings.config_path) {
let mut playout_cfg = ResponsePlayoutConfig::new();
return Ok("settings");
playout_cfg.playlist = Some(config.playlist);
playout_cfg.storage = Some(config.storage);
playout_cfg.text = Some(config.text);
if details.has_role(&Role::Admin) {
playout_cfg.general = Some(config.general);
playout_cfg.rpc_server = Some(config.rpc_server);
playout_cfg.mail = Some(config.mail);
playout_cfg.logging = Some(config.logging);
playout_cfg.processing = Some(config.processing);
playout_cfg.ingest = Some(config.ingest);
playout_cfg.out = Some(config.out);
return Ok(web::Json(playout_cfg));
}
return Ok(web::Json(playout_cfg));
}
};
Err(ServiceError::InternalServerError)

View File

@ -1,3 +1,6 @@
use std::{error::Error, fs::File, path::Path};
use faccess::PathExt;
use once_cell::sync::OnceCell;
use simplelog::*;
@ -6,6 +9,7 @@ use crate::api::{
handles::{db_add_user, db_global, db_init},
models::User,
};
use crate::utils::PlayoutConfig;
#[derive(PartialEq, Clone)]
pub enum Role {
@ -53,6 +57,19 @@ pub async fn init_config() {
INSTANCE.set(config).unwrap();
}
pub fn db_path() -> Result<String, Box<dyn std::error::Error>> {
let sys_path = Path::new("/usr/share/ffplayout");
let mut db_path = String::from("./ffplayout.db");
if sys_path.is_dir() && sys_path.writable() {
db_path = String::from("/usr/share/ffplayout/ffplayout.db");
} else if Path::new("./assets").is_dir() {
db_path = String::from("./assets/ffplayout.db");
}
Ok(db_path)
}
pub async fn run_args(args: Args) -> Result<(), i32> {
if !args.init && args.listen.is_none() && args.username.is_none() {
error!("Wrong number of arguments! Run ffpapi --help for more information.");
@ -96,3 +113,10 @@ pub async fn run_args(args: Args) -> Result<(), i32> {
Ok(())
}
pub fn read_playout_config(path: &str) -> Result<PlayoutConfig, Box<dyn Error>> {
let file = File::open(path)?;
let config: PlayoutConfig = serde_yaml::from_reader(file)?;
Ok(config)
}

View File

@ -1,4 +1,4 @@
use std::process::exit;
use std::{path::Path, process::exit};
use actix_web::{dev::ServiceRequest, middleware, web, App, Error, HttpMessage, HttpServer};
use actix_web_grants::permissions::AttachPermissions;
@ -14,9 +14,9 @@ use ffplayout_engine::{
auth,
models::LoginUser,
routes::{add_user, get_playout_config, get_settings, login, patch_settings, update_user},
utils::{init_config, run_args, Role},
utils::{db_path, init_config, run_args, Role},
},
utils::{init_logging, GlobalConfig},
utils::{init_logging, PlayoutConfig},
};
async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> {
@ -35,7 +35,7 @@ async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<Servi
async fn main() -> std::io::Result<()> {
let args = Args::parse();
let mut config = GlobalConfig::new(None);
let mut config = PlayoutConfig::new(None);
config.mail.recipient = String::new();
config.logging.log_to_file = false;
config.logging.timestamp = false;
@ -48,6 +48,12 @@ async fn main() -> std::io::Result<()> {
}
if let Some(conn) = args.listen {
if let Ok(p) = db_path() {
if !Path::new(&p).is_file() {
error!("Database is not initialized! Init DB first and add admin user.");
exit(1);
}
}
init_config().await;
let ip_port = conn.split(':').collect::<Vec<&str>>();
let addr = ip_port[0];

View File

@ -1,9 +1,9 @@
use crate::utils::GlobalConfig;
use crate::utils::PlayoutConfig;
/// Loudnorm Audio Filter
///
/// Add loudness normalization.
pub fn filter_node(config: &GlobalConfig) -> String {
pub fn filter_node(config: &PlayoutConfig) -> String {
format!(
"loudnorm=I={}:TP={}:LRA={}",
config.processing.loud_i, config.processing.loud_tp, config.processing.loud_lra

View File

@ -1,10 +1,10 @@
use crate::filter::{a_loudnorm, v_overlay};
use crate::utils::GlobalConfig;
use crate::utils::PlayoutConfig;
/// Audio Filter
///
/// If needed we add audio filters to the server instance.
fn audio_filter(config: &GlobalConfig) -> String {
fn audio_filter(config: &PlayoutConfig) -> String {
let mut audio_chain = ";[0:a]afade=in:st=0:d=0.5".to_string();
if config.processing.loudnorm_ingest {
@ -22,7 +22,7 @@ fn audio_filter(config: &GlobalConfig) -> String {
}
/// Create filter nodes for ingest live stream.
pub fn filter_cmd(config: &GlobalConfig) -> Vec<String> {
pub fn filter_cmd(config: &PlayoutConfig) -> Vec<String> {
let mut filter = format!(
"[0:v]fps={},scale={}:{},setdar=dar={},fade=in:st=0:d=0.5",
config.processing.fps,

View File

@ -7,7 +7,7 @@ pub mod ingest_filter;
pub mod v_drawtext;
pub mod v_overlay;
use crate::utils::{get_delta, is_close, GlobalConfig, Media};
use crate::utils::{get_delta, is_close, Media, PlayoutConfig};
#[derive(Debug, Clone)]
struct Filters {
@ -72,7 +72,7 @@ fn deinterlace(field_order: &Option<String>, chain: &mut Filters) {
}
}
fn pad(aspect: f64, chain: &mut Filters, config: &GlobalConfig) {
fn pad(aspect: f64, chain: &mut Filters, config: &PlayoutConfig) {
if !is_close(aspect, config.processing.aspect, 0.03) {
chain.add_filter(
&format!(
@ -84,13 +84,13 @@ fn pad(aspect: f64, chain: &mut Filters, config: &GlobalConfig) {
}
}
fn fps(fps: f64, chain: &mut Filters, config: &GlobalConfig) {
fn fps(fps: f64, chain: &mut Filters, config: &PlayoutConfig) {
if fps != config.processing.fps {
chain.add_filter(&format!("fps={}", config.processing.fps), "video")
}
}
fn scale(v_stream: &ffprobe::Stream, aspect: f64, chain: &mut Filters, config: &GlobalConfig) {
fn scale(v_stream: &ffprobe::Stream, aspect: f64, chain: &mut Filters, config: &PlayoutConfig) {
// width: i64, height: i64
if let (Some(w), Some(h)) = (v_stream.width, v_stream.height) {
if w != config.processing.width || h != config.processing.height {
@ -137,7 +137,7 @@ fn fade(node: &mut Media, chain: &mut Filters, codec_type: &str) {
}
}
fn overlay(node: &mut Media, chain: &mut Filters, config: &GlobalConfig) {
fn overlay(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
if config.processing.add_logo
&& Path::new(&config.processing.logo).is_file()
&& &node.category.clone().unwrap_or_default() != "advertisement"
@ -183,7 +183,7 @@ fn extend_video(node: &mut Media, chain: &mut Filters) {
}
/// add drawtext filter for lower thirds messages
fn add_text(node: &mut Media, chain: &mut Filters, config: &GlobalConfig) {
fn add_text(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
if config.text.add_text && config.text.over_pre {
let filter = v_drawtext::filter_node(config, node);
@ -233,7 +233,7 @@ fn extend_audio(node: &mut Media, chain: &mut Filters) {
}
/// Add single pass loudnorm filter to audio line.
fn add_loudnorm(node: &mut Media, chain: &mut Filters, config: &GlobalConfig) {
fn add_loudnorm(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
if config.processing.add_loudnorm
&& !node
.probe
@ -247,13 +247,13 @@ fn add_loudnorm(node: &mut Media, chain: &mut Filters, config: &GlobalConfig) {
}
}
fn audio_volume(chain: &mut Filters, config: &GlobalConfig) {
fn audio_volume(chain: &mut Filters, config: &PlayoutConfig) {
if config.processing.volume != 1.0 {
chain.add_filter(&format!("volume={}", config.processing.volume), "audio")
}
}
fn aspect_calc(aspect_string: &Option<String>, config: &GlobalConfig) -> f64 {
fn aspect_calc(aspect_string: &Option<String>, config: &PlayoutConfig) -> f64 {
let mut source_aspect = config.processing.aspect;
if let Some(aspect) = aspect_string {
@ -276,7 +276,12 @@ fn fps_calc(r_frame_rate: &str) -> f64 {
}
/// This realtime filter is important for HLS output to stay in sync.
fn realtime_filter(node: &mut Media, chain: &mut Filters, config: &GlobalConfig, codec_type: &str) {
fn realtime_filter(
node: &mut Media,
chain: &mut Filters,
config: &PlayoutConfig,
codec_type: &str,
) {
let mut t = "";
if codec_type == "audio" {
@ -300,7 +305,7 @@ fn realtime_filter(node: &mut Media, chain: &mut Filters, config: &GlobalConfig,
}
}
pub fn filter_chains(config: &GlobalConfig, node: &mut Media) -> Vec<String> {
pub fn filter_chains(config: &PlayoutConfig, node: &mut Media) -> Vec<String> {
let mut filters = Filters::new();
if let Some(probe) = node.probe.as_ref() {

View File

@ -2,9 +2,9 @@ use std::path::Path;
use regex::Regex;
use crate::utils::{GlobalConfig, Media};
use crate::utils::{Media, PlayoutConfig};
pub fn filter_node(config: &GlobalConfig, node: &mut Media) -> String {
pub fn filter_node(config: &PlayoutConfig, node: &mut Media) -> String {
let mut filter = String::new();
let mut font = String::new();

View File

@ -1,11 +1,11 @@
use std::path::Path;
use crate::utils::GlobalConfig;
use crate::utils::PlayoutConfig;
/// Overlay Filter
///
/// When a logo is set, we create here the filter for the server.
pub fn filter_node(config: &GlobalConfig, add_tail: bool) -> String {
pub fn filter_node(config: &PlayoutConfig, add_tail: bool) -> String {
let mut logo_chain = String::new();
if config.processing.add_logo && Path::new(&config.processing.logo).is_file() {

View File

@ -19,14 +19,14 @@ use rand::{seq::SliceRandom, thread_rng};
use simplelog::*;
use walkdir::WalkDir;
use crate::utils::{get_sec, GlobalConfig, Media};
use crate::utils::{get_sec, Media, PlayoutConfig};
/// Folder Sources
///
/// Like playlist source, we create here a folder list for iterate over it.
#[derive(Debug, Clone)]
pub struct FolderSource {
config: GlobalConfig,
config: PlayoutConfig,
pub nodes: Arc<Mutex<Vec<Media>>>,
current_node: Media,
index: Arc<AtomicUsize>,
@ -34,7 +34,7 @@ pub struct FolderSource {
impl FolderSource {
pub fn new(
config: &GlobalConfig,
config: &PlayoutConfig,
current_list: Arc<Mutex<Vec<Media>>>,
global_index: Arc<AtomicUsize>,
) -> Self {
@ -163,7 +163,7 @@ fn file_extension(filename: &Path) -> Option<&str> {
/// When a change is register, update the current file list.
/// This makes it possible, to play infinitely and and always new files to it.
pub fn watchman(
config: GlobalConfig,
config: PlayoutConfig,
is_terminated: Arc<AtomicBool>,
sources: Arc<Mutex<Vec<Media>>>,
) {

View File

@ -9,7 +9,7 @@ use crossbeam_channel::Sender;
use simplelog::*;
use crate::filter::ingest_filter::filter_cmd;
use crate::utils::{format_log_line, GlobalConfig, Ingest, ProcessControl};
use crate::utils::{format_log_line, Ingest, PlayoutConfig, ProcessControl};
use crate::vec_strings;
pub fn log_line(line: String, level: &str) {
@ -55,6 +55,10 @@ fn server_monitor(
);
}
if line.contains("Address already in use") {
proc_ctl.kill_all();
}
log_line(line, level);
}
@ -65,7 +69,7 @@ fn server_monitor(
///
/// Start ffmpeg in listen mode, and wait for input.
pub fn ingest_server(
config: GlobalConfig,
config: PlayoutConfig,
ingest_sender: Sender<(usize, [u8; 65088])>,
mut proc_control: ProcessControl,
) -> Result<(), Error> {

View File

@ -9,7 +9,7 @@ use std::{
use simplelog::*;
use crate::utils::{GlobalConfig, Media, PlayoutStatus};
use crate::utils::{Media, PlayoutConfig, PlayoutStatus};
pub mod folder;
pub mod ingest;
@ -21,7 +21,7 @@ pub use playlist::CurrentProgram;
/// Create a source iterator from playlist, or from folder.
pub fn source_generator(
config: GlobalConfig,
config: PlayoutConfig,
current_list: Arc<Mutex<Vec<Media>>>,
index: Arc<AtomicUsize>,
playout_stat: PlayoutStatus,

View File

@ -12,7 +12,7 @@ use simplelog::*;
use crate::utils::{
check_sync, gen_dummy, get_delta, get_sec, is_close, is_remote, json_serializer::read_json,
modified_time, seek_and_length, valid_source, GlobalConfig, Media, PlayoutStatus, DUMMY_LEN,
modified_time, seek_and_length, valid_source, Media, PlayoutConfig, PlayoutStatus, DUMMY_LEN,
};
/// Struct for current playlist.
@ -20,7 +20,7 @@ use crate::utils::{
/// Here we prepare the init clip and build a iterator where we pull our clips.
#[derive(Debug)]
pub struct CurrentProgram {
config: GlobalConfig,
config: PlayoutConfig,
start_sec: f64,
json_mod: Option<String>,
json_path: Option<String>,
@ -34,7 +34,7 @@ pub struct CurrentProgram {
impl CurrentProgram {
pub fn new(
config: &GlobalConfig,
config: &PlayoutConfig,
playout_stat: PlayoutStatus,
is_terminated: Arc<AtomicBool>,
current_list: Arc<Mutex<Vec<Media>>>,
@ -390,7 +390,7 @@ impl Iterator for CurrentProgram {
/// - return clip only if we are in 24 hours time range
fn timed_source(
node: Media,
config: &GlobalConfig,
config: &PlayoutConfig,
last: bool,
playout_stat: &PlayoutStatus,
) -> Media {
@ -440,7 +440,7 @@ fn timed_source(
}
/// Generate the source CMD, or when clip not exist, get a dummy.
fn gen_source(config: &GlobalConfig, mut node: Media) -> Media {
fn gen_source(config: &PlayoutConfig, mut node: Media) -> Media {
if valid_source(&node.source) {
node.add_probe();
node.cmd = Some(seek_and_length(
@ -470,7 +470,7 @@ fn gen_source(config: &GlobalConfig, mut node: Media) -> Media {
/// Handle init clip, but this clip can be the last one in playlist,
/// this we have to figure out and calculate the right length.
fn handle_list_init(config: &GlobalConfig, mut node: Media) -> Media {
fn handle_list_init(config: &PlayoutConfig, mut node: Media) -> Media {
debug!("Playlist init");
let (_, total_delta) = get_delta(config, &node.begin.unwrap());
let mut out = node.out;

View File

@ -14,8 +14,8 @@ use ffplayout_engine::{
output::{player, write_hls},
rpc::json_rpc_server,
utils::{
generate_playlist, get_args, init_logging, send_mail, validate_ffmpeg, GlobalConfig,
PlayerControl, PlayoutStatus, ProcessControl,
generate_playlist, get_args, init_logging, send_mail, validate_ffmpeg, PlayerControl,
PlayoutConfig, PlayoutStatus, ProcessControl,
},
};
@ -57,7 +57,7 @@ fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) {
fn main() {
let args = get_args();
let config = GlobalConfig::new(Some(args));
let config = PlayoutConfig::new(Some(args));
let config_clone = config.clone();
let play_control = PlayerControl::new();
let playout_stat = PlayoutStatus::new();

View File

@ -3,13 +3,13 @@ use std::process::{self, Command, Stdio};
use simplelog::*;
use crate::filter::v_drawtext;
use crate::utils::{GlobalConfig, Media};
use crate::utils::{Media, PlayoutConfig};
use crate::vec_strings;
/// Desktop Output
///
/// Instead of streaming, we run a ffplay instance and play on desktop.
pub fn output(config: &GlobalConfig, log_format: &str) -> process::Child {
pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
let mut enc_filter: Vec<String> = vec![];
let mut enc_cmd = vec_strings!["-hide_banner", "-nostats", "-v", log_format, "-i", "pipe:0"];

View File

@ -30,14 +30,14 @@ use simplelog::*;
use crate::filter::ingest_filter::filter_cmd;
use crate::input::{ingest::log_line, source_generator};
use crate::utils::{
prepare_output_cmd, sec_to_time, stderr_reader, Decoder, GlobalConfig, Ingest, PlayerControl,
prepare_output_cmd, sec_to_time, stderr_reader, Decoder, Ingest, PlayerControl, PlayoutConfig,
PlayoutStatus, ProcessControl,
};
use crate::vec_strings;
/// Ingest Server for HLS
fn ingest_to_hls_server(
config: GlobalConfig,
config: PlayoutConfig,
playout_stat: PlayoutStatus,
mut proc_control: ProcessControl,
) -> Result<(), Error> {
@ -131,7 +131,7 @@ fn ingest_to_hls_server(
///
/// Write with single ffmpeg instance directly to a HLS playlist.
pub fn write_hls(
config: &GlobalConfig,
config: &PlayoutConfig,
play_control: PlayerControl,
playout_stat: PlayoutStatus,
mut proc_control: ProcessControl,

View File

@ -17,7 +17,8 @@ pub use hls::write_hls;
use crate::input::{ingest_server, source_generator};
use crate::utils::{
sec_to_time, stderr_reader, Decoder, GlobalConfig, PlayerControl, PlayoutStatus, ProcessControl,
sec_to_time, stderr_reader, Decoder, PlayerControl, PlayoutConfig, PlayoutStatus,
ProcessControl,
};
use crate::vec_strings;
@ -31,7 +32,7 @@ use crate::vec_strings;
/// When a live ingest arrive, it stops the current playing and switch to the live source.
/// When ingest stops, it switch back to playlist/folder mode.
pub fn player(
config: &GlobalConfig,
config: &PlayoutConfig,
play_control: PlayerControl,
playout_stat: PlayoutStatus,
mut proc_control: ProcessControl,

View File

@ -3,13 +3,13 @@ use std::process::{self, Command, Stdio};
use simplelog::*;
use crate::filter::v_drawtext;
use crate::utils::{prepare_output_cmd, GlobalConfig, Media};
use crate::utils::{prepare_output_cmd, Media, PlayoutConfig};
use crate::vec_strings;
/// Streaming Output
///
/// Prepare the ffmpeg command for streaming output
pub fn output(config: &GlobalConfig, log_format: &str) -> process::Child {
pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
let mut enc_cmd = vec![];
let mut enc_filter = vec![];
let mut preview_cmd = config.out.preview_cmd.as_ref().unwrap().clone();

View File

@ -9,7 +9,7 @@ use serde_json::{json, Map};
use simplelog::*;
use crate::utils::{
get_delta, get_sec, sec_to_time, write_status, GlobalConfig, Media, PlayerControl,
get_delta, get_sec, sec_to_time, write_status, Media, PlayerControl, PlayoutConfig,
PlayoutStatus, ProcessControl,
};
@ -25,7 +25,7 @@ fn get_media_map(media: Media) -> Value {
}
/// prepare json object for response
fn get_data_map(config: &GlobalConfig, media: Media) -> Map<String, Value> {
fn get_data_map(config: &PlayoutConfig, media: Media) -> Map<String, Value> {
let mut data_map = Map::new();
let begin = media.begin.unwrap_or(0.0);
@ -56,7 +56,7 @@ fn get_data_map(config: &GlobalConfig, media: Media) -> Map<String, Value> {
/// - get last clip
/// - reset player state to original clip
pub fn json_rpc_server(
config: GlobalConfig,
config: PlayoutConfig,
play_control: PlayerControl,
playout_stat: PlayoutStatus,
proc_control: ProcessControl,

View File

@ -21,7 +21,7 @@ fn timed_kill(sec: u64, mut proc_ctl: ProcessControl) {
#[test]
#[ignore]
fn playlist_change_at_midnight() {
let mut config = GlobalConfig::new(None);
let mut config = PlayoutConfig::new(None);
config.mail.recipient = "".into();
config.processing.mode = "playlist".into();
config.playlist.day_start = "00:00:00".into();
@ -46,7 +46,7 @@ fn playlist_change_at_midnight() {
#[test]
#[ignore]
fn playlist_change_at_six() {
let mut config = GlobalConfig::new(None);
let mut config = PlayoutConfig::new(None);
config.mail.recipient = "".into();
config.processing.mode = "playlist".into();
config.playlist.day_start = "06:00:00".into();

View File

@ -39,7 +39,7 @@ fn get_date_tomorrow() {
#[test]
fn test_delta() {
let mut config = GlobalConfig::new(None);
let mut config = PlayoutConfig::new(None);
config.mail.recipient = "".into();
config.processing.mode = "playlist".into();
config.playlist.day_start = "00:00:00".into();

View File

@ -15,7 +15,7 @@ use crate::vec_strings;
///
/// This we init ones, when ffplayout is starting and use them globally in the hole program.
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GlobalConfig {
pub struct PlayoutConfig {
pub general: General,
pub rpc_server: RpcServer,
pub mail: Mail,
@ -134,7 +134,7 @@ pub struct Out {
pub output_cmd: Option<Vec<String>>,
}
impl GlobalConfig {
impl PlayoutConfig {
/// Read config from YAML file, and set some extra config values.
pub fn new(args: Option<Args>) -> Self {
let mut config_path = PathBuf::from("/etc/ffplayout/ffplayout.yml");
@ -161,7 +161,7 @@ impl GlobalConfig {
}
};
let mut config: GlobalConfig =
let mut config: PlayoutConfig =
serde_yaml::from_reader(f).expect("Could not read config file.");
config.general.generate = None;
config.general.stat_file = env::temp_dir()
@ -275,7 +275,7 @@ impl GlobalConfig {
}
}
impl Default for GlobalConfig {
impl Default for PlayoutConfig {
fn default() -> Self {
Self::new(None)
}

View File

@ -17,7 +17,7 @@ use chrono::{Duration, NaiveDate};
use simplelog::*;
use crate::input::FolderSource;
use crate::utils::{json_serializer::Playlist, GlobalConfig, Media};
use crate::utils::{json_serializer::JsonPlaylist, Media, PlayoutConfig};
/// Generate a vector with dates, from given range.
fn get_date_range(date_range: &[String]) -> Vec<String> {
@ -50,7 +50,7 @@ fn get_date_range(date_range: &[String]) -> Vec<String> {
}
/// Generate playlists
pub fn generate_playlist(config: &GlobalConfig, mut date_range: Vec<String>) {
pub fn generate_playlist(config: &PlayoutConfig, mut date_range: Vec<String>) {
let total_length = config.playlist.length_sec.unwrap();
let current_list = Arc::new(Mutex::new(vec![Media::new(0, "".to_string(), false)]));
let index = Arc::new(AtomicUsize::new(0));
@ -103,7 +103,7 @@ pub fn generate_playlist(config: &GlobalConfig, mut date_range: Vec<String>) {
let mut length = 0.0;
let mut round = 0;
let mut playlist = Playlist {
let mut playlist = JsonPlaylist {
date,
current_file: None,
start_sec: None,

View File

@ -9,14 +9,14 @@ use std::{
use simplelog::*;
use crate::utils::{
get_date, is_remote, modified_time, time_from_header, validate_playlist, GlobalConfig, Media,
get_date, is_remote, modified_time, time_from_header, validate_playlist, Media, PlayoutConfig,
};
pub const DUMMY_LEN: f64 = 60.0;
/// This is our main playlist object, it holds all necessary information for the current day.
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Playlist {
pub struct JsonPlaylist {
pub date: String,
#[serde(skip_serializing, skip_deserializing)]
@ -31,7 +31,7 @@ pub struct Playlist {
pub program: Vec<Media>,
}
impl Playlist {
impl JsonPlaylist {
fn new(date: String, start: f64) -> Self {
let mut media = Media::new(0, String::new(), false);
media.begin = Some(start);
@ -47,7 +47,11 @@ impl Playlist {
}
}
fn set_defaults(mut playlist: Playlist, current_file: String, mut start_sec: f64) -> Playlist {
fn set_defaults(
mut playlist: JsonPlaylist,
current_file: String,
mut start_sec: f64,
) -> JsonPlaylist {
playlist.current_file = Some(current_file);
playlist.start_sec = Some(start_sec);
@ -66,15 +70,15 @@ fn set_defaults(mut playlist: Playlist, current_file: String, mut start_sec: f64
playlist
}
/// Read json playlist file, fills Playlist struct and set some extra values,
/// Read json playlist file, fills JsonPlaylist struct and set some extra values,
/// which we need to process.
pub fn read_json(
config: &GlobalConfig,
config: &PlayoutConfig,
path: Option<String>,
is_terminated: Arc<AtomicBool>,
seek: bool,
next_start: f64,
) -> Playlist {
) -> JsonPlaylist {
let config_clone = config.clone();
let mut playlist_path = Path::new(&config.playlist.path).to_owned();
let start_sec = config.playlist.start_sec.unwrap();
@ -104,7 +108,7 @@ pub fn read_json(
let headers = resp.headers().clone();
if let Ok(body) = resp.text() {
let mut playlist: Playlist =
let mut playlist: JsonPlaylist =
serde_json::from_str(&body).expect("Could't read remote json playlist.");
if let Some(time) = time_from_header(&headers) {
@ -127,7 +131,7 @@ pub fn read_json(
.write(false)
.open(&current_file)
.expect("Could not open json playlist file.");
let mut playlist: Playlist =
let mut playlist: JsonPlaylist =
serde_json::from_reader(f).expect("Could't read json playlist file.");
playlist.modified = modified_time(&current_file);
@ -140,5 +144,5 @@ pub fn read_json(
error!("Read playlist error, on: <b><magenta>{current_file}</></b>!");
Playlist::new(date, start_sec)
JsonPlaylist::new(date, start_sec)
}

View File

@ -5,7 +5,7 @@ use std::sync::{
use simplelog::*;
use crate::utils::{sec_to_time, valid_source, GlobalConfig, MediaProbe, Playlist};
use crate::utils::{sec_to_time, valid_source, JsonPlaylist, MediaProbe, PlayoutConfig};
/// Validate a given playlist, to check if:
///
@ -14,7 +14,11 @@ use crate::utils::{sec_to_time, valid_source, GlobalConfig, MediaProbe, Playlist
/// - total playtime fits target length from config
///
/// This function we run in a thread, to don't block the main function.
pub fn validate_playlist(playlist: Playlist, is_terminated: Arc<AtomicBool>, config: GlobalConfig) {
pub fn validate_playlist(
playlist: JsonPlaylist,
is_terminated: Arc<AtomicBool>,
config: PlayoutConfig,
) {
let date = playlist.date;
let mut length = config.playlist.length_sec.unwrap();
let mut begin = config.playlist.start_sec.unwrap();

View File

@ -16,7 +16,7 @@ use serde_json::json;
use simplelog::*;
mod arg_parse;
mod config;
pub mod config;
pub mod controller;
mod generator;
pub mod json_serializer;
@ -24,10 +24,10 @@ mod json_validate;
mod logging;
pub use arg_parse::{get_args, Args};
pub use config::GlobalConfig;
pub use config::{self as playout_config, PlayoutConfig};
pub use controller::{PlayerControl, PlayoutStatus, ProcessControl, ProcessUnit::*};
pub use generator::generate_playlist;
pub use json_serializer::{read_json, Playlist, DUMMY_LEN};
pub use json_serializer::{read_json, JsonPlaylist, DUMMY_LEN};
pub use json_validate::validate_playlist;
pub use logging::{init_logging, send_mail};
@ -123,7 +123,7 @@ impl Media {
}
}
pub fn add_filter(&mut self, config: &GlobalConfig) {
pub fn add_filter(&mut self, config: &PlayoutConfig) {
let mut node = self.clone();
self.filter = Some(filter_chains(config, &mut node))
}
@ -191,7 +191,7 @@ impl MediaProbe {
/// Write current status to status file in temp folder.
///
/// The status file is init in main function and mostly modified in RPC server.
pub fn write_status(config: &GlobalConfig, date: &str, shift: f64) {
pub fn write_status(config: &PlayoutConfig, date: &str, shift: f64) {
let data = json!({
"time_shift": shift,
"date": date,
@ -308,7 +308,7 @@ pub fn is_close(a: f64, b: f64, to: f64) -> bool {
/// if we still in sync.
///
/// We also get here the global delta between clip start and time when a new playlist should start.
pub fn get_delta(config: &GlobalConfig, begin: &f64) -> (f64, f64) {
pub fn get_delta(config: &PlayoutConfig, begin: &f64) -> (f64, f64) {
let mut current_time = get_sec();
let start = config.playlist.start_sec.unwrap();
let length = time_to_sec(&config.playlist.length);
@ -339,7 +339,7 @@ pub fn get_delta(config: &GlobalConfig, begin: &f64) -> (f64, f64) {
}
/// Check if clip in playlist is in sync with global time.
pub fn check_sync(config: &GlobalConfig, delta: f64) -> bool {
pub fn check_sync(config: &PlayoutConfig, delta: f64) -> bool {
if delta.abs() > config.general.stop_threshold && config.general.stop_threshold > 0.0 {
error!("Clip begin out of sync for <yellow>{delta:.3}</> seconds. Stop playout!");
return false;
@ -349,7 +349,7 @@ pub fn check_sync(config: &GlobalConfig, delta: f64) -> bool {
}
/// Create a dummy clip as a placeholder for missing video files.
pub fn gen_dummy(config: &GlobalConfig, duration: f64) -> (String, Vec<String>) {
pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec<String>) {
let color = "#121212";
let source = format!(
"color=c={color}:s={}x{}:d={duration}",
@ -567,7 +567,7 @@ fn ffmpeg_libs_and_filter() -> (Vec<String>, Vec<String>) {
/// Validate ffmpeg/ffprobe/ffplay.
///
/// Check if they are in system and has all filters and codecs we need.
pub fn validate_ffmpeg(config: &GlobalConfig) {
pub fn validate_ffmpeg(config: &PlayoutConfig) {
is_in_system("ffmpeg");
is_in_system("ffprobe");