WIP Playlist error handling. Filtering is broken.

This commit is contained in:
jb-alvarado 2022-03-02 18:14:21 +01:00
parent bc69300a1c
commit 934fd289cb
5 changed files with 291 additions and 120 deletions

View File

@ -4,15 +4,15 @@ use std::path::Path;
use crate::utils::{is_close, Config, Media}; use crate::utils::{is_close, Config, Media};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
struct Filters { pub struct Filters {
audio_chain: Option<String>, pub audio_chain: Option<String>,
video_chain: Option<String>, pub video_chain: Option<String>,
audio_map: Option<String>, pub audio_map: Option<String>,
video_map: Option<String>, pub video_map: Option<String>,
} }
impl Filters { impl Filters {
fn new() -> Self { pub fn new() -> Self {
Filters { Filters {
audio_chain: None, audio_chain: None,
video_chain: None, video_chain: None,
@ -58,13 +58,26 @@ impl Filters {
} }
} }
fn deinterlace(field_order: Option<String>, chain: &mut Filters) { fn deinterlace(node: Media, chain: &mut Filters) {
if field_order.is_some() && field_order.unwrap() != "progressive".to_string() { // if node.probe.is_none() {
// return;
// }
let v_stream = &node.probe.unwrap().video_streams.unwrap()[0];
if v_stream.field_order.is_some() && v_stream.field_order.unwrap() != "progressive".to_string()
{
chain.add_filter("yadif=0:-1:0".into(), "video".into()) chain.add_filter("yadif=0:-1:0".into(), "video".into())
} }
} }
fn pad(aspect: f64, chain: &mut Filters, config: &Config) { fn pad(node: &Media, chain: &mut Filters, config: &Config) {
if node.probe.is_none() {
return;
}
let aspect = aspect_calc(node);
if !is_close(aspect, config.processing.aspect, 0.03) { if !is_close(aspect, config.processing.aspect, 0.03) {
if aspect < config.processing.aspect { if aspect < config.processing.aspect {
chain.add_filter( chain.add_filter(
@ -88,7 +101,13 @@ fn pad(aspect: f64, chain: &mut Filters, config: &Config) {
} }
} }
fn fps(fps: f64, chain: &mut Filters, config: &Config) { fn fps(node: &Media, chain: &mut Filters, config: &Config) {
if node.probe.is_none() {
return;
}
let fps = fps_calc(node);
if fps != config.processing.fps { if fps != config.processing.fps {
chain.add_filter( chain.add_filter(
format!("fps={}", config.processing.fps).into(), format!("fps={}", config.processing.fps).into(),
@ -97,7 +116,17 @@ fn fps(fps: f64, chain: &mut Filters, config: &Config) {
} }
} }
fn scale(width: i64, height: i64, aspect: f64, chain: &mut Filters, config: &Config) { fn scale(node: &Media, chain: &mut Filters, config: &Config) {
if node.probe.is_none() {
return;
}
let v_stream = node.probe.unwrap().video_streams.unwrap()[0];
let aspect = aspect_calc(node);
let width = v_stream.width.unwrap();
let height = v_stream.height.unwrap();
if width != config.processing.width || height != config.processing.height { if width != config.processing.width || height != config.processing.height {
chain.add_filter( chain.add_filter(
format!( format!(
@ -136,7 +165,7 @@ fn fade(node: &mut Media, chain: &mut Filters, codec_type: String) {
} }
} }
fn overlay(node: &mut Media, chain: &mut Filters, config: &Config) { pub fn overlay(node: &mut Media, chain: &mut Filters, config: &Config) {
if config.processing.add_logo if config.processing.add_logo
&& Path::new(&config.processing.logo).is_file() && Path::new(&config.processing.logo).is_file()
&& node.category != "advertisement".to_string() && node.category != "advertisement".to_string()
@ -146,7 +175,10 @@ fn overlay(node: &mut Media, chain: &mut Filters, config: &Config) {
config.processing.logo_opacity config.processing.logo_opacity
); );
let logo_loop = "loop=loop=-1:size=1:start=0"; let logo_loop = "loop=loop=-1:size=1:start=0";
let mut logo_chain = format!("null[v];movie={},{logo_loop},{opacity}", config.processing.logo); let mut logo_chain = format!(
"null[v];movie={},{logo_loop},{opacity}",
config.processing.logo
);
if node.last_ad.unwrap() { if node.last_ad.unwrap() {
logo_chain.push_str(",fade=in:st=0:d=1.0:alpha=1") logo_chain.push_str(",fade=in:st=0:d=1.0:alpha=1")
@ -174,7 +206,13 @@ fn extend_video(node: &mut Media, chain: &mut Filters) {
let duration_float = video_duration.clone().unwrap().parse::<f64>().unwrap(); let duration_float = video_duration.clone().unwrap().parse::<f64>().unwrap();
if node.out - node.seek > duration_float - node.seek + 0.1 { if node.out - node.seek > duration_float - node.seek + 0.1 {
chain.add_filter(format!("tpad=stop_mode=add:stop_duration={}", (node.out - node.seek) - (duration_float - node.seek)) , "video".into()) chain.add_filter(
format!(
"tpad=stop_mode=add:stop_duration={}",
(node.out - node.seek) - (duration_float - node.seek)
),
"video".into(),
)
} }
} }
} }
@ -200,8 +238,11 @@ fn extend_audio(node: &mut Media, chain: &mut Filters) {
if audio_duration.is_some() { if audio_duration.is_some() {
let duration_float = audio_duration.clone().unwrap().parse::<f64>().unwrap(); let duration_float = audio_duration.clone().unwrap().parse::<f64>().unwrap();
if node.out - node.seek > duration_float- node.seek + 0.1 { if node.out - node.seek > duration_float - node.seek + 0.1 {
chain.add_filter(format!("apad=whole_dur={}", node.out - node.seek), "audio".into()) chain.add_filter(
format!("apad=whole_dur={}", node.out - node.seek),
"audio".into(),
)
} }
} }
} }
@ -209,53 +250,49 @@ fn extend_audio(node: &mut Media, chain: &mut Filters) {
fn audio_volume(chain: &mut Filters, config: &Config) { fn audio_volume(chain: &mut Filters, config: &Config) {
if config.processing.volume != 1.0 { if config.processing.volume != 1.0 {
chain.add_filter(format!("volume={}", config.processing.volume), "audio".into()) chain.add_filter(
format!("volume={}", config.processing.volume),
"audio".into(),
)
} }
} }
fn aspect_calc(node: &Media) -> f64 {
let v_stream = node.probe.unwrap().video_streams.unwrap()[0];
let aspect_string = v_stream.display_aspect_ratio.clone().unwrap();
let aspect_vec: Vec<&str> = aspect_string.split(':').collect();
let w: f64 = aspect_vec[0].parse().unwrap();
let h: f64 = aspect_vec[1].parse().unwrap();
let source_aspect: f64 = w as f64 / h as f64;
source_aspect
}
fn fps_calc(node: &Media) -> f64 {
let v_stream = node.probe.unwrap().video_streams.unwrap()[0];
let frame_rate_vec: Vec<&str> = v_stream.r_frame_rate.split('/').collect();
let rate: f64 = frame_rate_vec[0].parse().unwrap();
let factor: f64 = frame_rate_vec[1].parse().unwrap();
let fps: f64 = rate / factor;
fps
}
pub fn filter_chains(node: &mut Media, config: &Config) -> Vec<String> { pub fn filter_chains(node: &mut Media, config: &Config) -> Vec<String> {
let mut filters = Filters::new(); let mut filters = Filters::new();
let probe = node.probe.clone();
match probe { deinterlace(node.clone(), &mut filters);
Some(p) => { pad(&node, &mut filters, &config);
// let a_stream = &p.audio_streams.unwrap()[0]; fps(&node, &mut filters, &config);
if p.video_streams.is_some() { scale(&node, &mut filters, &config);
let v_stream = &p.video_streams.unwrap()[0]; extend_video(node, &mut filters);
let aspect_string = v_stream.display_aspect_ratio.clone().unwrap(); fade(node, &mut filters, "video".into());
let aspect_vec: Vec<&str> = aspect_string.split(':').collect(); overlay(node, &mut filters, &config);
let w: f64 = aspect_vec[0].parse().unwrap();
let h: f64 = aspect_vec[1].parse().unwrap();
let source_aspect: f64 = w as f64 / h as f64;
let frame_rate_vec: Vec<&str> = v_stream.r_frame_rate.split('/').collect();
let rate: f64 = frame_rate_vec[0].parse().unwrap();
let factor: f64 = frame_rate_vec[1].parse().unwrap();
let frames_per_second: f64 = rate / factor;
deinterlace(v_stream.field_order.clone(), &mut filters); add_audio(node, &mut filters);
pad(source_aspect, &mut filters, &config); extend_audio(node, &mut filters);
fps(frames_per_second, &mut filters, &config); fade(node, &mut filters, "audio".into());
scale( audio_volume(&mut filters, &config);
v_stream.width.unwrap(),
v_stream.height.unwrap(),
source_aspect,
&mut filters,
&config,
);
extend_video(node, &mut filters);
fade(node, &mut filters, "video".into());
overlay(node, &mut filters, &config);
add_audio(node, &mut filters);
extend_audio(node, &mut filters);
fade(node, &mut filters, "audio".into());
audio_volume(&mut filters, &config);
}
}
None => {
println!("Clip has no media probe object. No filter applied!")
}
}
let mut filter_cmd = vec![]; let mut filter_cmd = vec![];
let mut filter_str: String = "".to_string(); let mut filter_str: String = "".to_string();

View File

@ -111,6 +111,10 @@ pub fn play(config: Config) {
None => break None => break
}; };
if !node.process.unwrap() {
continue
}
info!( info!(
"Play for <yellow>{}</>: <b><magenta>{}</></b>", "Play for <yellow>{}</>: <b><magenta>{}</></b>",
sec_to_time(node.out - node.seek), sec_to_time(node.out - node.seek),
@ -168,6 +172,8 @@ pub fn play(config: Config) {
if let Err(e) = dec_proc.wait() { if let Err(e) = dec_proc.wait() {
panic!("Decoder error: {:?}", e) panic!("Decoder error: {:?}", e)
}; };
sleep(Duration::from_secs(1));
} }
sleep(Duration::from_secs(1)); sleep(Duration::from_secs(1));

View File

@ -57,25 +57,14 @@ pub fn read_json(config: &Config, seek: bool) -> Playlist {
time_sec += length_sec time_sec += length_sec
} }
let cloned_program = playlist.program.clone();
for (i, item) in playlist.program.iter_mut().enumerate() { for (i, item) in playlist.program.iter_mut().enumerate() {
item.begin = Some(start_sec); item.begin = Some(start_sec);
item.index = Some(i); item.index = Some(i);
item.last_ad = Some(false); item.last_ad = Some(false);
item.next_ad = Some(false); item.next_ad = Some(false);
item.process = Some(true);
let mut source_cmd: Vec<String> = vec![]; let mut source_cmd: Vec<String> = vec![];
if i > 0 && cloned_program[i - 1].category == "advertisement".to_string() {
item.last_ad = Some(true);
}
if i + 1 < cloned_program.len() {
if cloned_program[i + 1].category == "advertisement".to_string() {
item.next_ad = Some(true);
}
}
if seek_first { if seek_first {
let tmp_length = item.out - item.seek; let tmp_length = item.out - item.seek;

View File

@ -2,7 +2,7 @@ use chrono::prelude::*;
use chrono::Duration; use chrono::Duration;
use ffprobe::{ffprobe, Format, Stream}; use ffprobe::{ffprobe, Format, Stream};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fs::metadata, time, time::UNIX_EPOCH}; use std::{fs::metadata, path::Path, time, time::UNIX_EPOCH};
use simplelog::*; use simplelog::*;
@ -37,16 +37,22 @@ pub struct Media {
pub probe: Option<MediaProbe>, pub probe: Option<MediaProbe>,
pub last_ad: Option<bool>, pub last_ad: Option<bool>,
pub next_ad: Option<bool>, pub next_ad: Option<bool>,
pub process: Option<bool>,
} }
impl Media { impl Media {
fn new(index: usize, src: String) -> Self { fn new(index: usize, src: String) -> Self {
let probe = MediaProbe::new(src.clone()); let mut duration: f64 = 0.0;
let mut probe = None;
let duration: f64 = match &probe.clone().format.unwrap().duration { if Path::new("src").is_file() {
Some(dur) => dur.parse().unwrap(), probe = Some(MediaProbe::new(src.clone()));
None => 0.0,
}; duration = match probe.clone().unwrap().format.unwrap().duration {
Some(dur) => dur.parse().unwrap(),
None => 0.0,
};
}
Self { Self {
begin: None, begin: None,
@ -58,9 +64,10 @@ impl Media {
source: src.clone(), source: src.clone(),
cmd: Some(vec!["-i".to_string(), src]), cmd: Some(vec!["-i".to_string(), src]),
filter: Some(vec![]), filter: Some(vec![]),
probe: Some(probe), probe: probe,
last_ad: Some(false), last_ad: Some(false),
next_ad: Some(false), next_ad: Some(false),
process: Some(true),
} }
} }
@ -70,7 +77,7 @@ impl Media {
fn add_filter(&mut self, config: &Config) { fn add_filter(&mut self, config: &Config) {
let mut node = self.clone(); let mut node = self.clone();
self.filter = Some(filter_chains(&mut node, &config)); self.filter = Some(filter_chains(&mut node, &config))
} }
} }
@ -235,7 +242,7 @@ pub fn get_delta(begin: &f64, config: &Config) -> (f64, f64) {
pub fn check_sync(delta: f64, config: &Config) -> bool { pub fn check_sync(delta: f64, config: &Config) -> bool {
if delta.abs() > config.general.stop_threshold && config.general.stop_threshold > 0.0 { if delta.abs() > config.general.stop_threshold && config.general.stop_threshold > 0.0 {
error!("Start time out of sync for <yellow>{}</> seconds", delta); error!("Start time out of sync for <yellow>{}</> seconds", delta);
return false return false;
} }
true true

View File

@ -3,16 +3,19 @@ use std::path::Path;
use simplelog::*; use simplelog::*;
use crate::utils::{ use crate::utils::{
check_sync, gen_dummy, get_delta, json_reader::read_json, modified_time, time_to_sec, Config, check_sync, gen_dummy, get_delta, get_sec, json_reader::read_json, modified_time, time_to_sec,
Media, Config, Media,
}; };
use crate::filter::{overlay, Filters};
#[derive(Debug)] #[derive(Debug)]
pub struct CurrentProgram { pub struct CurrentProgram {
config: Config, config: Config,
json_mod: String, json_mod: String,
json_path: String, json_path: String,
nodes: Vec<Media>, nodes: Vec<Media>,
current_node: Media,
init: bool, init: bool,
idx: usize, idx: usize,
} }
@ -26,6 +29,7 @@ impl CurrentProgram {
json_mod: json.modified.unwrap(), json_mod: json.modified.unwrap(),
json_path: json.current_file.unwrap(), json_path: json.current_file.unwrap(),
nodes: json.program.into(), nodes: json.program.into(),
current_node: Media::new(json.start_index.unwrap(), "".to_string()),
init: true, init: true,
idx: json.start_index.unwrap(), idx: json.start_index.unwrap(),
} }
@ -43,24 +47,47 @@ impl CurrentProgram {
} }
} }
fn check_for_next_playlist(&mut self, node: &Media, last: bool) { fn check_for_next_playlist(&mut self, last: bool) -> f64 {
let mut out = node.out.clone(); let mut out = self.current_node.out.clone();
let start_sec = time_to_sec(&self.config.playlist.day_start); let start_sec = time_to_sec(&self.config.playlist.day_start);
let mut delta = 0.0; let mut delta = 0.0;
if node.duration > node.out { if self.current_node.duration > self.current_node.out {
out = node.duration.clone() out = self.current_node.duration.clone()
} }
if last { if last {
let seek = if node.seek > 0.0 { node.seek } else { 0.0 }; let seek = if self.current_node.seek > 0.0 {
(delta, _) = get_delta(&node.begin.unwrap().clone(), &self.config); self.current_node.seek
} else {
0.0
};
(delta, _) = get_delta(&self.current_node.begin.unwrap().clone(), &self.config);
delta += seek + self.config.general.stop_threshold; delta += seek + self.config.general.stop_threshold;
} }
let next_start = node.begin.unwrap() - start_sec + out + delta; let next_start = self.current_node.begin.unwrap() - start_sec + out + delta;
println!("{}", next_start); // println!("{}", next_start);
next_start
}
fn is_ad(&mut self, i: usize, next: bool) -> Option<bool> {
if next {
if i + 1 < self.nodes.len() && self.nodes[i + 1].category == "advertisement".to_string()
{
return Some(true);
} else {
return Some(false);
}
} else {
if i > 0 && i - 1 < self.nodes.len() && self.nodes[i - 1].category == "advertisement".to_string() {
return Some(true);
} else {
return Some(false);
}
}
} }
} }
@ -70,72 +97,167 @@ impl Iterator for CurrentProgram {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.nodes.len() { if self.idx < self.nodes.len() {
self.check_update(); self.check_update();
let mut current;
if self.init { if self.init {
current = handle_list_init(self.nodes[self.idx].clone(), &self.config); self.current_node = handle_list_init(self.nodes[self.idx].clone(), &self.config);
self.init = false; self.init = false;
} else { } else {
current = self.nodes[self.idx].clone(); let new_source = timed_source(self.nodes[self.idx].clone(), &self.config, false);
self.current_node = match new_source {
Some(src) => src,
None => {
let mut media = Media::new(self.idx, "".to_string());
media.process = Some(false);
let (delta, _) = get_delta(&current.begin.unwrap(), &self.config); media
}
};
let (delta, _) = get_delta(&self.current_node.begin.unwrap(), &self.config);
debug!("Delta: <yellow>{delta}</>"); debug!("Delta: <yellow>{delta}</>");
let sync = check_sync(delta, &self.config); let sync = check_sync(delta, &self.config);
if !sync { if !sync {
current.cmd = None; self.current_node.cmd = None;
return Some(current); return Some(self.current_node.clone());
} }
} }
self.idx += 1; self.current_node.last_ad = self.is_ad(self.idx, false);
current = gen_source(current, &self.config); self.current_node.next_ad = self.is_ad(self.idx, false);
self.check_for_next_playlist(&current, true); self.idx += 1;
Some(current)
self.check_for_next_playlist(false);
Some(self.current_node.clone())
} else { } else {
let mut current; let (_, time_diff) = get_delta(&get_sec(), &self.config);
let pl_time_diff = self.check_for_next_playlist(true);
let pl_delta = time_to_sec(&self.config.playlist.length) - pl_time_diff.abs();
let mut last_ad = self.is_ad(self.idx, false);
if time_diff.abs() > pl_delta && time_diff.abs() > self.config.general.stop_threshold {
self.current_node = Media::new(self.idx + 1, "".to_string());
self.current_node.begin = Some(get_sec());
let mut duration = time_diff.abs();
if duration > 60.0 {
duration = 60.0;
}
self.current_node.duration = duration;
self.current_node.out = duration;
self.current_node = gen_source(self.current_node.clone(), &self.config);
self.nodes.push(self.current_node.clone());
last_ad = self.is_ad(self.idx + 1, false);
self.current_node.last_ad = last_ad;
return Some(self.current_node.clone());
}
let json = read_json(&self.config, false); let json = read_json(&self.config, false);
self.json_mod = json.modified.unwrap(); self.json_mod = json.modified.unwrap();
self.json_path = json.current_file.unwrap(); self.json_path = json.current_file.unwrap();
self.nodes = json.program.into(); self.nodes = json.program.into();
self.idx = 1;
if self.init { if self.init {
current = handle_list_init(self.nodes[0].clone(), &self.config); self.current_node = handle_list_init(self.nodes[0].clone(), &self.config);
self.init = false; self.init = false;
} else { } else {
current = self.nodes[0].clone(); let new_source = timed_source(self.nodes[0].clone(), &self.config, false);
self.current_node = match new_source {
Some(src) => src,
None => {
let mut media = Media::new(self.idx, "".to_string());
media.process = Some(false);
let (delta, _) = get_delta(&current.begin.unwrap(), &self.config); media
}
};
let (delta, _) = get_delta(&self.current_node.begin.unwrap(), &self.config);
debug!("Delta: <yellow>{delta}</>"); debug!("Delta: <yellow>{delta}</>");
let sync = check_sync(delta, &self.config); let sync = check_sync(delta, &self.config);
if !sync { if !sync {
current.cmd = None; self.current_node.cmd = None;
return Some(current); return Some(self.current_node.clone());
} }
} }
current = gen_source(current, &self.config); self.current_node.last_ad = last_ad;
self.current_node.next_ad = self.is_ad(0, false);
Some(current) self.idx = 1;
Some(self.current_node.clone())
} }
} }
} }
fn timed_source(node: Media, config: &Config, last: bool) -> Option<Media> {
// prepare input clip
// check begin and length from clip
// return clip only if we are in 24 hours time range
let (delta, total_delta) = get_delta(&node.begin.unwrap(), &config);
let mut new_node = None;
if config.playlist.day_start.contains(":") && config.playlist.length.contains(":") {
debug!("Delta: <yellow>{delta}</>");
check_sync(delta, &config);
}
if (total_delta > node.out - node.seek && !last) || !config.playlist.length.contains(":") {
// when we are in the 24 hour range, get the clip
new_node = Some(gen_source(node, &config));
} else if total_delta <= 0.0 {
info!("Begin is over play time, skip: {}", node.source);
} else if total_delta < node.duration - node.seek || last {
println!("handle list end");
new_node = handle_list_end(node, total_delta);
}
return new_node;
}
fn gen_source(mut node: Media, config: &Config) -> Media { fn gen_source(mut node: Media, config: &Config) -> Media {
if Path::new(&node.source).is_file() { if Path::new(&node.source).is_file() {
node.add_probe(); node.add_probe();
node.add_filter(&config); node.add_filter(&config);
} else { } else {
error!("File not found: {}", node.source); if node.source.chars().count() == 0 {
let dummy = gen_dummy(node.out - node.seek, &config); warn!(
node.source = dummy.0; "Generate filler with <yellow>{}</> seconds length!",
node.cmd = Some(dummy.1); node.out - node.seek
node.filter = Some(vec![]); );
} else {
error!("File not found: {}", node.source);
}
let (source, cmd) = gen_dummy(node.out - node.seek, &config);
node.source = source;
node.cmd = Some(cmd);
let mut filters = Filters::new();
let mut chain: Vec<String> = vec![];
overlay(&mut node, &mut filters, &config);
if filters.video_chain.is_some() {
let mut filter_str: String = "".to_string();
filter_str.push_str(filters.video_chain.unwrap().as_str());
filter_str.push_str(filters.video_map.clone().unwrap().as_str());
chain.push("-filter_complex".to_string());
chain.push(filter_str);
chain.push("-map".to_string());
chain.push(filters.video_map.unwrap());
chain.push("-map".to_string());
chain.push("1:a".to_string());
}
node.filter = Some(chain);
} }
node node
@ -146,19 +268,18 @@ fn handle_list_init(mut node: Media, config: &Config) -> Media {
// this we have to figure out and calculate the right length // this we have to figure out and calculate the right length
debug!("Playlist init"); debug!("Playlist init");
println!("{:?}", node);
let (_, total_delta) = get_delta(&node.begin.unwrap(), config); let (_, total_delta) = get_delta(&node.begin.unwrap(), config);
let mut out = node.out; let mut out = node.out;
if node.out - node.seek > total_delta { if node.out - node.seek > total_delta {
out = total_delta + node.seek out = total_delta + node.seek;
} }
node.out = out; node.out = out;
let new_node = gen_source(node, &config);
node new_node
} }
fn handle_list_end(mut node: Media, total_delta: f64) -> Option<Media> { fn handle_list_end(mut node: Media, total_delta: f64) -> Option<Media> {
@ -168,22 +289,33 @@ fn handle_list_end(mut node: Media, total_delta: f64) -> Option<Media> {
debug!("Playlist end"); debug!("Playlist end");
let mut out = if node.seek > 0.0 {node.seek + total_delta} else {total_delta}; let mut out = if node.seek > 0.0 {
node.seek + total_delta
} else {
total_delta
};
// prevent looping // prevent looping
if out > node.duration { if out > node.duration {
out = node.duration out = node.duration
} else { } else {
warn!("Clip length is not in time, new duration is: <yellow>{:.2}</>", total_delta) warn!(
"Clip length is not in time, new duration is: <yellow>{:.2}</>",
total_delta
)
} }
if node.duration > total_delta && total_delta > 1.0 && node.duration - node.seek >= total_delta { if node.duration > total_delta && total_delta > 1.0 && node.duration - node.seek >= total_delta
{
node.out = out; node.out = out;
} else if node.duration > total_delta && total_delta < 1.0 { } else if node.duration > total_delta && total_delta < 1.0 {
warn!("Last clip less then 1 second long, skip: {}", node.source); warn!("Last clip less then 1 second long, skip: {}", node.source);
return None return None;
} else { } else {
error!("Playlist is not long enough: <yellow>{:.2}</> seconds needed", total_delta); error!(
"Playlist is not long enough: <yellow>{:.2}</> seconds needed",
total_delta
);
} }
Some(node) Some(node)