don't log missing source when playlist is to short

add validate playlist option
This commit is contained in:
jb-alvarado 2023-07-06 11:33:09 +02:00
parent cd4c8727bd
commit 83432ef673
10 changed files with 154 additions and 58 deletions

70
Cargo.lock generated
View File

@ -485,7 +485,7 @@ dependencies = [
"log",
"parking",
"polling",
"rustix 0.37.22",
"rustix 0.37.23",
"slab",
"socket2",
"waker-fn",
@ -535,9 +535,9 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
[[package]]
name = "async-trait"
version = "0.1.69"
version = "0.1.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7"
checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
dependencies = [
"proc-macro2",
"quote",
@ -745,9 +745,9 @@ checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
[[package]]
name = "clap"
version = "4.3.10"
version = "4.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a"
checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d"
dependencies = [
"clap_builder",
"clap_derive",
@ -756,9 +756,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.3.10"
version = "4.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d"
checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b"
dependencies = [
"anstream",
"anstyle",
@ -840,9 +840,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "cpufeatures"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c"
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
dependencies = [
"libc",
]
@ -1452,9 +1452,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "hex"
@ -1693,7 +1693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb"
dependencies = [
"hermit-abi",
"rustix 0.38.2",
"rustix 0.38.3",
"windows-sys 0.48.0",
]
@ -2392,9 +2392,21 @@ dependencies = [
[[package]]
name = "regex"
version = "1.8.4"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56"
dependencies = [
"aho-corasick",
"memchr",
@ -2403,9 +2415,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
[[package]]
name = "relative-path"
@ -2516,9 +2528,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.37.22"
version = "0.37.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8818fa822adcc98b18fedbb3632a6a33213c070556b5aa7c4c8cc21cff565c4c"
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
dependencies = [
"bitflags 1.3.2",
"errno",
@ -2530,9 +2542,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.2"
version = "0.38.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabcb0461ebd01d6b79945797c27f8529082226cb630a9865a71870ff63532a4"
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
dependencies = [
"bitflags 2.3.3",
"errno",
@ -2632,9 +2644,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.99"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c"
dependencies = [
"itoa",
"ryu",
@ -2773,9 +2785,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "socket2"
@ -3071,7 +3083,7 @@ dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
"rustix 0.37.22",
"rustix 0.37.23",
"windows-sys 0.48.0",
]
@ -3111,18 +3123,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.40"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59"
dependencies = [
"proc-macro2",
"quote",

View File

@ -250,13 +250,19 @@ impl CurrentProgram {
if !self.playout_stat.list_init.load(Ordering::SeqCst) {
let time_sec = self.get_current_time();
let index = self.index.fetch_add(1, Ordering::SeqCst);
let nodes = self.nodes.lock().unwrap();
let last_index = nodes.len() - 1;
// de-instance node to preserve original values in list
let mut node_clone = self.nodes.lock().unwrap()[index].clone();
let mut node_clone = nodes[index].clone();
node_clone.seek = time_sec - node_clone.begin.unwrap();
self.current_node =
handle_list_init(&self.config, node_clone, &self.playout_stat.chain);
self.current_node = handle_list_init(
&self.config,
node_clone,
&self.playout_stat.chain,
last_index,
);
}
}
}
@ -269,6 +275,7 @@ impl Iterator for CurrentProgram {
self.check_update(self.playout_stat.list_init.load(Ordering::SeqCst));
if self.playout_stat.list_init.load(Ordering::SeqCst) {
trace!("Init playlist, from next iterator");
if self.json_path.is_some() {
self.init_clip();
}
@ -281,6 +288,7 @@ impl Iterator for CurrentProgram {
self.current_node = self.nodes.lock().unwrap()[last_index].clone();
let new_node = self.nodes.lock().unwrap()[last_index].clone();
let new_length = new_node.begin.unwrap() + new_node.duration;
trace!("Init playlist after playlist end");
self.check_for_next_playlist();
@ -304,13 +312,17 @@ impl Iterator for CurrentProgram {
current_time += self.config.playlist.length_sec.unwrap() + 1.0;
}
let mut media = Media::new(0, "", false);
let mut nodes = self.nodes.lock().unwrap();
let index = nodes.len();
let mut media = Media::new(index, "", false);
media.begin = Some(current_time);
media.duration = duration;
media.out = duration;
self.current_node = gen_source(&self.config, media, &self.playout_stat.chain);
let mut nodes = self.nodes.lock().unwrap();
self.current_node =
gen_source(&self.config, media, &self.playout_stat.chain, last_index);
nodes.push(self.current_node.clone());
self.index.store(nodes.len(), Ordering::SeqCst);
}
@ -326,8 +338,9 @@ impl Iterator for CurrentProgram {
let mut is_last = false;
let index = self.index.load(Ordering::SeqCst);
let nodes = self.nodes.lock().unwrap();
let last_index = nodes.len() - 1;
if index == nodes.len() - 1 {
if index == last_index {
is_last = true
}
@ -336,6 +349,7 @@ impl Iterator for CurrentProgram {
&self.config,
is_last,
&self.playout_stat,
last_index,
);
drop(nodes);
@ -370,6 +384,7 @@ impl Iterator for CurrentProgram {
&self.config,
self.current_node.clone(),
&self.playout_stat.chain,
0,
);
self.nodes.lock().unwrap().push(self.current_node.clone());
self.last_next_ad();
@ -385,10 +400,12 @@ impl Iterator for CurrentProgram {
// Get first clip from next playlist.
self.index.store(0, Ordering::SeqCst);
let last_index = self.nodes.lock().unwrap().len() - 1;
self.current_node = gen_source(
&self.config,
self.nodes.lock().unwrap()[0].clone(),
&self.playout_stat.chain,
last_index,
);
self.last_next_ad();
self.current_node.last_ad = last_ad;
@ -409,12 +426,15 @@ fn timed_source(
config: &PlayoutConfig,
last: bool,
playout_stat: &PlayoutStatus,
last_index: usize,
) -> Media {
let (delta, total_delta) = get_delta(config, &node.begin.unwrap());
let mut shifted_delta = delta;
let mut new_node = node.clone();
new_node.process = Some(false);
trace!("timed source ist last: {last}");
if config.playlist.length.contains(':') {
let time_shift = playout_stat.time_shift.lock().unwrap();
@ -443,11 +463,11 @@ fn timed_source(
{
// when we are in the 24 hour range, get the clip
new_node.process = Some(true);
new_node = gen_source(config, node, &playout_stat.chain);
new_node = gen_source(config, node, &playout_stat.chain, last_index);
} else if total_delta <= 0.0 {
info!("Begin is over play time, skip: {}", node.source);
} else if total_delta < node.duration - node.seek || last {
new_node = handle_list_end(config, node, total_delta, &playout_stat.chain);
new_node = handle_list_end(config, node, total_delta, &playout_stat.chain, last_index);
}
new_node
@ -458,6 +478,7 @@ pub fn gen_source(
config: &PlayoutConfig,
mut node: Media,
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
last_index: usize,
) -> Media {
let duration = node.out - node.seek;
@ -476,7 +497,16 @@ pub fn gen_source(
node.cmd = Some(seek_and_length(&node));
}
} else {
error!("Source not found: <b><magenta>\"{}\"</></b>", node.source);
trace!(
"clip index: {:?} | last index: {:?}",
node.index.unwrap_or_default(),
last_index
);
if node.index.unwrap_or_default() < last_index {
error!("Source not found: <b><magenta>\"{}\"</></b>", node.source);
}
warn!("Generate filler with <yellow>{duration:.2}</> seconds length!");
let probe = MediaProbe::new(&config.storage.filler_clip);
@ -532,6 +562,7 @@ fn handle_list_init(
config: &PlayoutConfig,
mut node: Media,
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
last_index: usize,
) -> Media {
debug!("Playlist init");
let (_, total_delta) = get_delta(config, &node.begin.unwrap());
@ -542,7 +573,8 @@ fn handle_list_init(
}
node.out = out;
gen_source(config, node, filter_chain)
gen_source(config, node, filter_chain, last_index)
}
/// when we come to last clip in playlist,
@ -553,6 +585,7 @@ fn handle_list_end(
mut node: Media,
total_delta: f64,
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
last_index: usize,
) -> Media {
debug!("Playlist end");
@ -580,5 +613,5 @@ fn handle_list_end(
node.process = Some(true);
gen_source(config, node, filter_chain)
gen_source(config, node, filter_chain, last_index)
}

View File

@ -2,7 +2,7 @@ use std::{
fs::{self, File},
path::{Path, PathBuf},
process::exit,
sync::{Arc, Mutex},
sync::{atomic::AtomicBool, Arc, Mutex},
thread,
};
@ -19,8 +19,9 @@ use ffplayout::{
};
use ffplayout_lib::utils::{
generate_playlist, import::import_file, init_logging, send_mail, validate_ffmpeg,
OutputMode::*, PlayerControl, PlayoutStatus, ProcessControl,
generate_playlist, get_date, import::import_file, init_logging, is_remote, send_mail,
validate_ffmpeg, validate_playlist, JsonPlaylist, OutputMode::*, PlayerControl, PlayoutStatus,
ProcessControl,
};
#[cfg(debug_assertions)]
@ -159,6 +160,39 @@ fn main() {
}
}
if args.validate {
let mut playlist_path = Path::new(&config.playlist.path).to_owned();
let start_sec = config.playlist.start_sec.unwrap();
let date = get_date(false, start_sec, 0.0);
if playlist_path.is_dir() || is_remote(&config.playlist.path) {
let d: Vec<&str> = date.split('-').collect();
playlist_path = playlist_path
.join(d[0])
.join(d[1])
.join(date.clone())
.with_extension("json");
}
let f = File::options()
.read(true)
.write(false)
.open(&playlist_path)
.expect("Could not open json playlist file.");
let playlist: JsonPlaylist = match serde_json::from_reader(f) {
Ok(p) => p,
Err(e) => {
error!("{e:?}");
exit(1)
}
};
validate_playlist(playlist, Arc::new(AtomicBool::new(false)), config);
exit(0);
}
if config.rpc_server.enable {
// If RPC server is enable we also fire up a JSON RPC server.
thread::spawn(move || run_server(config_clone, play_ctl, play_stat, proc_ctl2));

View File

@ -41,7 +41,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
}) {
enc_cmd.append(&mut cmd);
} else {
warn!("Given output parameter a skipped, they are not supported by ffplay!");
warn!("Given output parameters are skipped, they are not supported by ffplay!");
}
}

View File

@ -78,6 +78,9 @@ pub struct Args {
#[cfg(debug_assertions)]
#[clap(long, help = "fake date time, for debugging")]
pub fake_time: Option<String>,
#[clap(long, help = "validate given playlist")]
pub validate: bool,
}
/// Get arguments from command line, and return them.

View File

@ -39,6 +39,10 @@ pub fn get_config(args: Args) -> PlayoutConfig {
config.general.generate = Some(gen);
}
if args.validate {
config.general.validate = true;
}
if let Some(paths) = args.paths {
config.storage.paths = paths;
}

View File

@ -157,6 +157,9 @@ pub struct General {
#[serde(skip_serializing, skip_deserializing)]
pub ffmpeg_libs: Vec<String>,
#[serde(default, skip_serializing, skip_deserializing)]
pub validate: bool,
}
#[derive(Debug, Serialize, Deserialize, Clone)]

View File

@ -147,10 +147,16 @@ pub fn validate_playlist(
if valid_source(&item.source) {
if let Err(e) = check_media(item.clone(), pos, begin, &config) {
error!("{e}");
} else if config.general.validate {
debug!(
"Source at <yellow>{}</>, seems fine: <b><magenta>{}</></b>",
sec_to_time(begin),
item.source
)
};
} else {
error!(
"Source on position <yellow>{pos}</> {} not exists: <b><magenta>\"{}\"</></b>",
"Source on position <yellow>{pos}</> {} not exists: <b><magenta>{}</></b>",
sec_to_time(begin),
item.source
);

View File

@ -238,6 +238,7 @@ pub fn init_logging(
} else {
let term_config = log_config
.clone()
.set_level_color(Level::Trace, Some(Color::Ansi256(11)))
.set_level_color(Level::Debug, Some(Color::Ansi256(12)))
.set_level_color(Level::Info, Some(Color::Ansi256(10)))
.set_level_color(Level::Warn, Some(Color::Ansi256(208)))

View File

@ -15,7 +15,7 @@ fn video_audio_input() {
config.processing.logo = logo_path.to_string_lossy().to_string();
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd =
vec_strings![
@ -41,7 +41,7 @@ fn video_audio_custom_filter1_input() {
config.processing.custom_filter = "[0:v]gblur=2[c_v_out];[0:a]volume=0.2[c_a_out]".to_string();
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd = vec_strings![
"-filter_complex",
@ -68,7 +68,7 @@ fn video_audio_custom_filter2_input() {
.to_string();
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd = vec_strings![
"-filter_complex",
@ -94,7 +94,7 @@ fn video_audio_custom_filter3_input() {
"[v_in];movie=logo.png[l];[v_in][l]overlay[c_v_out];[0:a]volume=0.2[c_a_out]".to_string();
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd = vec_strings![
"-filter_complex",
@ -119,7 +119,7 @@ fn dual_audio_aevalsrc_input() {
config.processing.add_logo = false;
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd =
vec_strings![
@ -145,7 +145,7 @@ fn dual_audio_input() {
config.processing.add_logo = false;
let media_obj = Media::new(0, "./assets/dual_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd = vec_strings![
"-filter_complex",
@ -171,7 +171,7 @@ fn video_separate_audio_input() {
let mut media_obj = Media::new(0, "./assets/no_audio.mp4", true);
media_obj.audio = "./assets/audio.mp3".to_string();
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let test_filter_cmd = vec_strings![
"-filter_complex",
@ -1333,7 +1333,7 @@ fn video_audio_hls() {
]);
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let enc_prefix = vec_strings![
"-hide_banner",
@ -1422,7 +1422,7 @@ fn video_audio_sub_meta_hls() {
]);
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let enc_prefix = vec_strings![
"-hide_banner",
@ -1512,7 +1512,7 @@ fn video_multi_audio_hls() {
]);
let media_obj = Media::new(0, "./assets/dual_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let enc_prefix = vec_strings![
"-hide_banner",
@ -1617,7 +1617,7 @@ fn multi_video_audio_hls() {
]);
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let enc_prefix = vec_strings![
"-hide_banner",
@ -1733,7 +1733,7 @@ fn multi_video_multi_audio_hls() {
]);
let media_obj = Media::new(0, "./assets/dual_audio.mp4", true);
let media = gen_source(&config, media_obj, &None);
let media = gen_source(&config, media_obj, &None, 1);
let enc_prefix = vec_strings![
"-hide_banner",