parse complete yaml config, add arg parser, get current time in seconds and timestamp

This commit is contained in:
jb-alvarado 2022-02-13 21:26:41 +01:00
parent 0ba502634b
commit fd1e4f7cf4
7 changed files with 562 additions and 35 deletions

249
Cargo.lock generated
View File

@ -2,16 +2,88 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]]
name = "clap"
version = "3.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ffplayout-engine-rs"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"regex",
"serde",
"serde_yaml",
]
@ -22,6 +94,21 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.8.0"
@ -32,12 +119,82 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
@ -56,6 +213,23 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ryu"
version = "1.0.9"
@ -94,6 +268,12 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.86"
@ -105,12 +285,81 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@ -8,6 +8,9 @@ edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
regex = "1"
chrono = "0.4"
clap = { version = "3.0.14", features = ["derive"] }
[[bin]]
name = "ffplayout"

View File

@ -11,10 +11,138 @@ mail:
helptext: Send error messages to email address, like missing playlist; invalid
json format; missing clip path. Leave recipient blank, if you don't need this.
'mail_level' can be WARNING or ERROR.
subject: Playout Error
smtp_server: mail.example.org
subject: "Playout Error"
smtp_server: "mail.example.org"
smtp_port: 587
sender_addr: ffplayout@example.org
sender_pass: abc123
recipient: null
mail_level: ERROR
sender_addr: "ffplayout@example.org"
sender_pass: "abc123"
recipient:
mail_level: "ERROR"
logging:
helptext: Logging to file, if 'log_to_file' False log to console. 'backup_count'
says how long log files will be saved in days. Path to /var/log/ only if you
run this program as daemon. 'log_level' can be DEBUG, INFO, WARNING,
ERROR. 'ffmpeg_level' can be INFO, WARNING, ERROR.
log_to_file: true
backup_count: 7
log_path: "/var/log/ffplayout/"
log_level: "DEBUG"
ffmpeg_level: "ERROR"
processing:
helptext: Set playing mode, like playlist; folder, or you own custom one.
Default processing, for all clips that they get prepared in that way,
so the output is unique. 'aspect' must be a float number. 'logo' is only used
if the path exist. 'logo_scale' scale the logo to target size, leave it blank
when no scaling is needed, format is 'number:number', for example '100:-1'
for proportional scaling. With 'logo_opacity' logo can become transparent.
With 'logo_filter' 'overlay=W-w-12:12' you can modify the logo position.
With 'use_loudnorm' you can activate single pass EBU R128 loudness normalization.
'loud_*' can adjust the loudnorm filter. 'output_count' sets the outputs for
the filtering, > 1 gives the option to use the same filters for multiple outputs.
This outputs can be taken in 'stream_param', names will be vout2, vout3;
aout2, aout2 etc.
mode: playlist
width: 1024
height: 576
aspect: 1.778
fps: 25
add_logo: true
logo: "docs/logo.png"
logo_scale:
logo_opacity: 0.7
logo_filter: "overlay=W-w-12:12"
add_loudnorm: false
loud_i: -18
loud_tp: -1.5
loud_lra: 11
output_count: 1
ingest:
helptext: Works not with direct hls output, it always needs full processing! Run a server
for a ingest stream. This stream will override the normal streaming until is done.
There is no authentication, this is up to you. The recommend way is to set address to localhost, stream to a local server with authentication and from there stream to this app.
enable: false
stream_input: [-f, live_flv, -listen, 1, -i, rtmp://localhost:1936/live/stream]
playlist:
helptext: >
'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
script. Subdirectories needs this structure '/playlists/2018/01'. '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. 'loop true' works with
single playlist file and loops it infinitely.
path: "/playlists"
day_start: "5:59:25"
length: "24:00:00"
loop: false
storage:
helptext: Play ordered or randomly files from path. 'filler_clip' is for fill
the end to reach 24 hours, it will loop when is necessary. 'extensions' search
only files with this extension. Set 'shuffle' to 'True' to pick files randomly.
path: "/mediaStorage"
filler_clip: "/mediaStorage/filler/filler.mp4"
extensions:
- ".mp4"
- ".mkv"
shuffle: true
text:
helptext: Overlay text in combination with libzmq for remote text manipulation.
On windows fontfile path need to be like this 'C\:/WINDOWS/fonts/DejaVuSans.ttf'.
In a standard environment the filter drawtext node is Parsed_drawtext_2.
'over_pre' if True text will be overlay in pre processing. Continue same text
over multiple files is in that mode not possible. '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: false
over_pre: false
bind_address: "127.0.0.1:5555"
fontfile: "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
text_from_filename: false
style: "x=(w-tw)/2:y=(h-line_h)*0.9:fontsize=24:fontcolor=#ffffff:box=1:boxcolor=#000000:boxborderw=4"
regex: "^(.*)_"
out:
helptext: The final playout compression. Set the settings to your needs.
'mode' has the standard options 'desktop', 'hls', 'live_switch', 'stream'. Self made
outputs can be define, by adding script in output folder with an 'output' function
inside. 'preview' works only in streaming output and creates a separate preview stream.
mode: 'stream'
preview: false
preview_param:
[-s, 512x288,
-c:v, libx264,
-crf, 24,
-x264-params, keyint=50:min-keyint=25:scenecut=-1,
-maxrate, 800k,
-bufsize, 1600k,
-preset, ultrafast,
-tune, zerolatency,
-profile:v, Main,
-level, 3.1,
-c:a, aac,
-ar, 44100,
-b:a, 128k,
-flags, +global_header,
-f, flv, rtmp://preview.local/live/stream]
stream_param:
[-c:v, libx264,
-crf, 23,
-x264-params, keyint=50:min-keyint=25:scenecut=-1,
-maxrate, 1300k,
-bufsize, 2600k,
-preset, faster,
-tune, zerolatency,
-profile:v, Main,
-level, 3.1,
-c:a, aac,
-ar, 44100,
-b:a, 128k,
-flags, +global_header,
-f, flv, rtmp://localhost/live/stream]

39
src/arg_parse.rs Normal file
View File

@ -0,0 +1,39 @@
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(version, about = "ffplayout, the rust playout solution", long_about = None)]
pub struct Args {
#[clap(short, long, help = "file path to ffplayout.conf")]
pub config: Option<String>,
#[clap(short, long, help = "play folder content")]
pub folder: Option<String>,
#[clap(short, long, help = "file path for logfile")]
pub log: Option<String>,
#[clap(short = 'i', long, help = "loop playlist infinitely")]
pub r#loop: bool,
#[clap(short, long,
help = "set output mode: desktop, hls, stream")]
pub output: Option<String>,
#[clap(short, long, help = "path from playlist")]
pub playlist: Option<String>,
#[clap(short, long, help = "start time in 'hh:mm:ss', 'now' for start with first")]
pub start: Option<String>,
#[clap(short = 't', long, help = "set length in 'hh:mm:ss', 'none' for no length check")]
pub length: Option<String>,
#[clap(long, help = "playing mode: folder, playlist, custom...")]
pub play_mode: Option<String>,
}
pub fn get_args() -> Args {
let args = Args::parse();
args
}

114
src/config_reader.rs Normal file
View File

@ -0,0 +1,114 @@
use serde::{Deserialize, Serialize};
use serde_yaml::{self};
use std::path::Path;
// use regex::Regex;
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub general: General,
pub mail: Mail,
pub logging: Logging,
pub processing: Processing,
pub ingest: Ingest,
pub playlist: Playlist,
pub storage: Storage,
pub text: Text,
pub out: Out,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct General {
pub stop_threshold: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Mail {
pub subject: String,
pub smtp_server: String,
pub smtp_port: u32,
pub sender_addr: String,
pub sender_pass: String,
pub recipient: String,
pub mail_level: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Logging {
pub log_to_file: bool,
pub backup_count: u32,
pub log_path: String,
pub log_level: String,
pub ffmpeg_level: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Processing {
pub mode: String,
pub width: u32,
pub height: u32,
pub aspect: f32,
pub fps: u32,
pub add_logo: bool,
pub logo: String,
pub logo_scale: String,
pub logo_opacity: f32,
pub logo_filter: String,
pub add_loudnorm: bool,
pub loud_i: f32,
pub loud_tp: f32,
pub loud_lra: f32,
pub output_count: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Ingest {
pub enable: bool,
pub stream_input: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Playlist {
pub path: String,
pub day_start: String,
pub length: String,
pub r#loop: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Storage {
pub path: String,
pub filler_clip: String,
pub extensions: Vec<String>,
pub shuffle: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Text {
pub add_text: bool,
pub over_pre: bool,
pub bind_address: String,
pub text_from_filename: bool,
pub style: String,
pub regex: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Out {
pub mode: String,
pub preview: bool,
pub preview_param: Vec<String>,
pub stream_param: Vec<String>,
}
pub fn read_yaml() -> Config {
let mut config_path: String = "ffplayout.yml".to_string();
if Path::new("/etc/ffplayout/ffplayout.yml").exists() {
config_path = "/etc/ffplayout/ffplayout.yml".to_string();
}
let f = std::fs::File::open(config_path).expect("Could not open file.");
let config: Config = serde_yaml::from_reader(f).expect("Could not read config file.");
config
}

View File

@ -1,7 +1,17 @@
mod arg_parse;
mod config_reader;
mod utils;
fn main() {
let config = utils::read_yaml();
//println!("{:#?}", utils::Mail());
let config = config_reader::read_yaml();
let args = arg_parse::get_args();
println!("{:#?}", config);
println!("{:#?}", args);
println!("{:#?}", args.config.is_some());
println!("{:#?}", args.config.unwrap());
//println!("{:?}", config.general.stop_threshold);
println!("{:#?}", utils::get_sec());
println!("{:#?}", utils::get_timestamp());
}

View File

@ -1,33 +1,17 @@
use serde::{Deserialize, Serialize};
use serde_yaml::{self};
use chrono::prelude::*;
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub general: General,
pub mail: Mail,
pub fn get_sec() -> f64 {
let local: DateTime<Local> = Local::now();
let sec = (
local.hour() * 3600 + local.minute() * 60 + local.second()
) as f64 + (local.nanosecond() as f64 / 1000000000.0);
sec
}
#[derive(Debug, Serialize, Deserialize)]
pub struct General {
pub helptext: String,
pub stop_threshold: u32,
}
pub fn get_timestamp() -> i64 {
let local: DateTime<Local> = Local::now();
#[derive(Debug, Serialize, Deserialize)]
pub struct Mail {
pub helptext: String,
pub subject: String,
pub smtp_server: String,
pub smtp_port: u32,
pub sender_addr: String,
pub sender_pass: String,
pub recipient: String,
pub mail_level: String,
}
pub fn read_yaml() -> Config {
let f = std::fs::File::open("ffplayout.yml").expect("Could not open file.");
let config: Config = serde_yaml::from_reader(f).expect("Could not read values.");
config
local.timestamp_millis() as i64
}