commit
d0e83564de
874
Cargo.lock
generated
874
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
simplelog = { version = "^0.12", features = ["paris"] }
|
||||
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite"] }
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
|
||||
[[bin]]
|
||||
|
@ -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 {
|
||||
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)
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 75515da508578700c82cd7ed916b9d3f2e4f1fdc
|
||||
Subproject commit cf96eee7e3a233a0c58f833ff9443d869fa062b8
|
@ -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)]
|
||||
|
@ -50,9 +50,11 @@ pub fn import_file(
|
||||
if !line.starts_with('#') {
|
||||
let item = Media::new(0, &line, true);
|
||||
|
||||
if item.duration > 0.0 {
|
||||
playlist.program.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut file_exists = false;
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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)))
|
||||
|
@ -113,7 +113,7 @@ impl Media {
|
||||
let mut duration = 0.0;
|
||||
let mut probe = None;
|
||||
|
||||
if do_probe && Path::new(src).is_file() {
|
||||
if do_probe && (is_remote(src) || Path::new(src).is_file()) {
|
||||
probe = Some(MediaProbe::new(src));
|
||||
|
||||
if let Some(dur) = probe
|
||||
|
@ -6,6 +6,10 @@ target=$1
|
||||
echo "build frontend"
|
||||
echo
|
||||
|
||||
if [ ! -f 'ffplayout-frontend/package.json' ]; then
|
||||
git submodule update --init
|
||||
fi
|
||||
|
||||
yes | rm -rf public
|
||||
cd ffplayout-frontend
|
||||
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user