use std::{ env, fmt, fs::File, path::{Path, PathBuf}, process, str::FromStr, }; use chrono::NaiveTime; use log::LevelFilter; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use shlex::split; use super::vec_strings; use crate::utils::{free_tcp_socket, home_dir, time_to_sec, OutputMode::*}; 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", "png", "psd", "ppm", "sgi", "svg", "tga", "tif", "webp", ]; // Some well known errors can be safely ignore pub const FFMPEG_IGNORE_ERRORS: [&str; 11] = [ "ac-tex damaged", "codec s302m, is muxed as a private data stream", "corrupt decoded frame in stream", "corrupt input packet in stream", "end mismatch left", "Packet corrupt", "Referenced QT chapter track not found", "skipped MB in I-frame at", "Thread message queue blocking", "Warning MVs not available", "frame size not set", ]; pub const FFMPEG_UNRECOVERABLE_ERRORS: [&str; 5] = [ "Address already in use", "Invalid argument", "Numerical result", "Error initializing complex filters", "Error while decoding stream #0:0: Invalid data found when processing input", ]; #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] #[serde(rename_all = "lowercase")] pub enum OutputMode { Desktop, HLS, Null, Stream, } impl FromStr for OutputMode { type Err = String; fn from_str(input: &str) -> Result { match input { "desktop" => Ok(Self::Desktop), "hls" => Ok(Self::HLS), "null" => Ok(Self::Null), "stream" => Ok(Self::Stream), _ => Err("Use 'desktop', 'hls', 'null' or 'stream'".to_string()), } } } #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] #[serde(rename_all = "lowercase")] pub enum ProcessMode { Folder, Playlist, } impl fmt::Display for ProcessMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ProcessMode::Folder => write!(f, "folder"), ProcessMode::Playlist => write!(f, "playlist"), } } } impl FromStr for ProcessMode { type Err = String; fn from_str(input: &str) -> Result { match input { "folder" => Ok(Self::Folder), "playlist" => Ok(Self::Playlist), _ => Err("Use 'folder' or 'playlist'".to_string()), } } } pub fn string_to_log_level<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; match s.to_lowercase().as_str() { "debug" => Ok(LevelFilter::Debug), "error" => Ok(LevelFilter::Error), "info" => Ok(LevelFilter::Info), "trace" => Ok(LevelFilter::Trace), "warning" => Ok(LevelFilter::Warn), "off" => Ok(LevelFilter::Off), _ => Err(de::Error::custom("Error level not exists!")), } } fn log_level_to_string(l: &LevelFilter, s: S) -> Result where S: Serializer, { match l { LevelFilter::Debug => s.serialize_str("DEBUG"), LevelFilter::Error => s.serialize_str("ERROR"), LevelFilter::Info => s.serialize_str("INFO"), LevelFilter::Trace => s.serialize_str("TRACE"), LevelFilter::Warn => s.serialize_str("WARNING"), LevelFilter::Off => s.serialize_str("OFF"), } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Template { pub sources: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Source { pub start: NaiveTime, pub duration: NaiveTime, pub shuffle: bool, pub paths: Vec, } /// Global Config /// /// This we init ones, when ffplayout is starting and use them globally in the hole program. #[derive(Debug, Serialize, Deserialize, Clone)] pub struct PlayoutConfig { pub general: General, pub rpc_server: RpcServer, pub mail: Mail, pub logging: Logging, pub processing: Processing, pub ingest: Ingest, pub playlist: Playlist, pub storage: Storage, pub text: Text, #[serde(default)] pub task: Task, pub out: Out, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct General { pub help_text: String, pub stop_threshold: f64, #[serde(default, skip_serializing, skip_deserializing)] pub config_path: String, #[serde(default)] pub stat_file: String, #[serde(skip_serializing, skip_deserializing)] pub generate: Option>, #[serde(skip_serializing, skip_deserializing)] pub ffmpeg_filters: Vec, #[serde(skip_serializing, skip_deserializing)] pub ffmpeg_libs: Vec, #[serde(skip_serializing, skip_deserializing)] pub template: Option