work on new infinit mode
This commit is contained in:
parent
46c3a05dc6
commit
00ae975a81
@ -80,7 +80,7 @@ impl CurrentProgram {
|
||||
|
||||
if get_current {
|
||||
let json = read_json(
|
||||
&self.config,
|
||||
&mut self.config,
|
||||
&self.player_control,
|
||||
self.json_path.clone(),
|
||||
self.is_terminated.clone(),
|
||||
@ -137,15 +137,16 @@ impl CurrentProgram {
|
||||
trace!("next_start: {next_start}, end_sec: {}", self.end_sec);
|
||||
|
||||
// Check if we over the target length or we are close to it, if so we load the next playlist.
|
||||
if next_start >= self.end_sec
|
||||
|| is_close(total_delta, 0.0, 2.0)
|
||||
|| is_close(total_delta, self.end_sec, 2.0)
|
||||
if !self.config.playlist.infinit
|
||||
&& (next_start >= self.end_sec
|
||||
|| is_close(total_delta, 0.0, 2.0)
|
||||
|| is_close(total_delta, self.end_sec, 2.0))
|
||||
{
|
||||
trace!("get next day");
|
||||
next = true;
|
||||
|
||||
let json = read_json(
|
||||
&self.config,
|
||||
&mut self.config,
|
||||
&self.player_control,
|
||||
None,
|
||||
self.is_terminated.clone(),
|
||||
@ -212,7 +213,7 @@ impl CurrentProgram {
|
||||
let mut time_sec = time_in_seconds();
|
||||
|
||||
if time_sec < self.start_sec {
|
||||
time_sec += self.config.playlist.length_sec.unwrap()
|
||||
time_sec += 86400.0 // self.config.playlist.length_sec.unwrap();
|
||||
}
|
||||
|
||||
time_sec
|
||||
@ -222,6 +223,7 @@ impl CurrentProgram {
|
||||
fn get_current_clip(&mut self) {
|
||||
let mut time_sec = self.get_current_time();
|
||||
let shift = self.playout_stat.time_shift.lock().unwrap();
|
||||
let p_length = self.config.playlist.length_sec.unwrap();
|
||||
|
||||
if *self.playout_stat.current_date.lock().unwrap()
|
||||
== *self.playout_stat.date.lock().unwrap()
|
||||
@ -231,6 +233,10 @@ impl CurrentProgram {
|
||||
time_sec += *shift;
|
||||
}
|
||||
|
||||
if self.config.playlist.infinit && p_length < 86400.0 {
|
||||
time_sec -= p_length;
|
||||
}
|
||||
|
||||
for (i, item) in self
|
||||
.player_control
|
||||
.current_list
|
||||
@ -255,7 +261,8 @@ impl CurrentProgram {
|
||||
let mut is_filler = false;
|
||||
|
||||
if !self.playout_stat.list_init.load(Ordering::SeqCst) {
|
||||
let time_sec = self.get_current_time();
|
||||
let mut time_sec = self.get_current_time();
|
||||
let p_length = self.config.playlist.length_sec.unwrap();
|
||||
let index = self
|
||||
.player_control
|
||||
.current_index
|
||||
@ -268,6 +275,10 @@ impl CurrentProgram {
|
||||
|
||||
trace!("Clip from init: {}", node_clone.source);
|
||||
|
||||
if self.config.playlist.infinit && p_length < 86400.0 {
|
||||
time_sec -= p_length;
|
||||
}
|
||||
|
||||
node_clone.seek += time_sec
|
||||
- (node_clone.begin.unwrap() - *self.playout_stat.time_shift.lock().unwrap());
|
||||
|
||||
@ -488,7 +499,8 @@ fn timed_source(
|
||||
debug!("Delta: <yellow>{shifted_delta:.3}</>");
|
||||
}
|
||||
|
||||
if config.general.stop_threshold > 0.0
|
||||
if !config.playlist.infinit
|
||||
&& config.general.stop_threshold > 0.0
|
||||
&& shifted_delta.abs() > config.general.stop_threshold
|
||||
{
|
||||
error!("Clip begin out of sync for <yellow>{delta:.3}</> seconds.");
|
||||
@ -502,6 +514,7 @@ fn timed_source(
|
||||
if (total_delta > node.out - node.seek && !last)
|
||||
|| node.index.unwrap() < 2
|
||||
|| !config.playlist.length.contains(':')
|
||||
|| config.playlist.infinit
|
||||
{
|
||||
// when we are in the 24 hour range, get the clip
|
||||
new_node.process = Some(true);
|
||||
@ -742,7 +755,7 @@ fn handle_list_init(
|
||||
debug!("Playlist init");
|
||||
let (_, total_delta) = get_delta(config, &node.begin.unwrap());
|
||||
|
||||
if node.out - node.seek > total_delta {
|
||||
if !config.playlist.infinit && node.out - node.seek > total_delta {
|
||||
node.out = total_delta + node.seek;
|
||||
}
|
||||
|
||||
|
@ -107,8 +107,18 @@ pub fn player(
|
||||
continue;
|
||||
}
|
||||
|
||||
let c_index = if cfg!(debug_assertions) {
|
||||
format!(
|
||||
" ({}/{})",
|
||||
node.index.unwrap() + 1,
|
||||
play_control.current_list.lock().unwrap().len()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
info!(
|
||||
"Play for <yellow>{}</>: <b><magenta>{} {}</></b>",
|
||||
"Play for <yellow>{}</>{c_index}: <b><magenta>{} {}</></b>",
|
||||
sec_to_time(node.out - node.seek),
|
||||
node.source,
|
||||
node.audio
|
||||
|
@ -9,8 +9,8 @@ use std::{
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::{
|
||||
controller::ProcessUnit::*, get_date, is_remote, modified_time, time_from_header,
|
||||
validate_playlist, Media, PlayerControl, PlayoutConfig, DUMMY_LEN,
|
||||
get_date, is_remote, modified_time, sec_to_time, time_from_header, validate_playlist, Media,
|
||||
PlayerControl, PlayoutConfig, DUMMY_LEN,
|
||||
};
|
||||
|
||||
/// This is our main playlist object, it holds all necessary information for the current day.
|
||||
@ -65,9 +65,10 @@ fn set_defaults(
|
||||
mut playlist: JsonPlaylist,
|
||||
current_file: String,
|
||||
mut start_sec: f64,
|
||||
) -> JsonPlaylist {
|
||||
) -> (JsonPlaylist, f64) {
|
||||
playlist.current_file = Some(current_file);
|
||||
playlist.start_sec = Some(start_sec);
|
||||
let mut length = 0.0;
|
||||
|
||||
// Add extra values to every media clip
|
||||
for (i, item) in playlist.program.iter_mut().enumerate() {
|
||||
@ -78,69 +79,18 @@ fn set_defaults(
|
||||
item.process = Some(true);
|
||||
item.filter = None;
|
||||
|
||||
start_sec += item.out - item.seek;
|
||||
let dur = item.out - item.seek;
|
||||
start_sec += dur;
|
||||
length += dur;
|
||||
}
|
||||
|
||||
playlist
|
||||
}
|
||||
|
||||
fn loop_playlist(
|
||||
config: &PlayoutConfig,
|
||||
current_file: String,
|
||||
mut playlist: JsonPlaylist,
|
||||
) -> JsonPlaylist {
|
||||
let start_sec = config.playlist.start_sec.unwrap();
|
||||
let mut begin = start_sec;
|
||||
let length = config.playlist.length_sec.unwrap();
|
||||
let mut program_list = vec![];
|
||||
let mut index = 0;
|
||||
|
||||
playlist.current_file = Some(current_file);
|
||||
playlist.start_sec = Some(start_sec);
|
||||
|
||||
'program_looper: loop {
|
||||
for item in playlist.program.iter() {
|
||||
let media = Media {
|
||||
index: Some(index),
|
||||
begin: Some(begin),
|
||||
seek: item.seek,
|
||||
out: item.out,
|
||||
duration: item.duration,
|
||||
duration_audio: item.duration_audio,
|
||||
category: item.category.clone(),
|
||||
source: item.source.clone(),
|
||||
audio: item.audio.clone(),
|
||||
cmd: item.cmd.clone(),
|
||||
probe: item.probe.clone(),
|
||||
probe_audio: item.probe_audio.clone(),
|
||||
process: Some(true),
|
||||
unit: Decoder,
|
||||
last_ad: false,
|
||||
next_ad: false,
|
||||
filter: None,
|
||||
custom_filter: String::new(),
|
||||
};
|
||||
|
||||
if begin < start_sec + length {
|
||||
program_list.push(media);
|
||||
} else {
|
||||
break 'program_looper;
|
||||
}
|
||||
|
||||
begin += item.out - item.seek;
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
playlist.program = program_list;
|
||||
|
||||
playlist
|
||||
(playlist, length)
|
||||
}
|
||||
|
||||
/// Read json playlist file, fills JsonPlaylist struct and set some extra values,
|
||||
/// which we need to process.
|
||||
pub fn read_json(
|
||||
config: &PlayoutConfig,
|
||||
config: &mut PlayoutConfig,
|
||||
player_control: &PlayerControl,
|
||||
path: Option<String>,
|
||||
is_terminated: Arc<AtomicBool>,
|
||||
@ -197,10 +147,14 @@ pub fn read_json(
|
||||
});
|
||||
}
|
||||
|
||||
match config.playlist.infinit {
|
||||
true => return loop_playlist(config, current_file, playlist),
|
||||
false => return set_defaults(playlist, current_file, start_sec),
|
||||
let (playlist, duration) = set_defaults(playlist, current_file, start_sec);
|
||||
|
||||
if config.playlist.infinit || config.playlist.length.is_empty() {
|
||||
config.playlist.length = sec_to_time(duration);
|
||||
config.playlist.length_sec = Some(duration);
|
||||
}
|
||||
|
||||
return playlist;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -235,10 +189,14 @@ pub fn read_json(
|
||||
});
|
||||
}
|
||||
|
||||
match config.playlist.infinit {
|
||||
true => return loop_playlist(config, current_file, playlist),
|
||||
false => return set_defaults(playlist, current_file, start_sec),
|
||||
let (playlist, duration) = set_defaults(playlist, current_file, start_sec);
|
||||
|
||||
if config.playlist.infinit || config.playlist.length.is_empty() {
|
||||
config.playlist.length = sec_to_time(duration);
|
||||
config.playlist.length_sec = Some(duration);
|
||||
}
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
error!("Playlist <b><magenta>{current_file}</></b> not exist!");
|
||||
|
@ -236,5 +236,12 @@ pub fn validate_playlist(
|
||||
);
|
||||
}
|
||||
|
||||
if config.general.validate {
|
||||
info!(
|
||||
"[Validation] Playlist length: <yellow>{}</>",
|
||||
sec_to_time(begin - config.playlist.start_sec.unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
debug!("Validation done, in {:.3?} ...", timer.elapsed(),);
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::{exit, ChildStderr, Command, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
time::{self, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@ -427,11 +426,12 @@ pub fn time_to_sec(time_str: &str) -> f64 {
|
||||
|
||||
/// Convert floating number (seconds) to a formatted time string.
|
||||
pub fn sec_to_time(sec: f64) -> String {
|
||||
let d = UNIX_EPOCH + time::Duration::from_millis((sec * 1000.0) as u64);
|
||||
// Create DateTime from SystemTime
|
||||
let date_time = DateTime::<Utc>::from(d);
|
||||
|
||||
date_time.format("%H:%M:%S%.3f").to_string()
|
||||
format!(
|
||||
"{:0>2}:{:0>2}:{:06.3}",
|
||||
(sec / 60.0 / 60.0) as i32,
|
||||
(sec / 60.0 % 60.0) as i32,
|
||||
(sec % 60.0),
|
||||
)
|
||||
}
|
||||
|
||||
/// get file extension
|
||||
@ -461,18 +461,20 @@ pub fn sum_durations(clip_list: &Vec<Media>) -> f64 {
|
||||
///
|
||||
/// We also get here the global delta between clip start and time when a new playlist should start.
|
||||
pub fn get_delta(config: &PlayoutConfig, begin: &f64) -> (f64, f64) {
|
||||
let time_sec = time_in_seconds();
|
||||
let mut current_time = time_in_seconds();
|
||||
let start = config.playlist.start_sec.unwrap();
|
||||
let length = time_to_sec(&config.playlist.length);
|
||||
let length = config.playlist.length_sec.unwrap_or(86400.0);
|
||||
let mut target_length = 86400.0;
|
||||
|
||||
if length > 0.0 && length != target_length {
|
||||
target_length = length
|
||||
}
|
||||
|
||||
if begin == &start && start == 0.0 && 86400.0 - current_time < 4.0 {
|
||||
current_time -= target_length
|
||||
} else if start >= current_time && begin != &start {
|
||||
current_time += target_length
|
||||
current_time += 86400.0
|
||||
}
|
||||
|
||||
let mut current_delta = begin - current_time;
|
||||
@ -487,6 +489,14 @@ pub fn get_delta(config: &PlayoutConfig, begin: &f64) -> (f64, f64) {
|
||||
target_length + start - current_time
|
||||
};
|
||||
|
||||
if config.playlist.infinit && length < 86400.0 {
|
||||
current_delta += length;
|
||||
|
||||
if time_sec > start {
|
||||
current_delta -= 86400.0;
|
||||
}
|
||||
}
|
||||
|
||||
(current_delta, total_delta)
|
||||
}
|
||||
|
||||
|
288
tests/assets/playlists/2024/03/2024-03-19.json
Normal file
288
tests/assets/playlists/2024/03/2024-03-19.json
Normal file
@ -0,0 +1,288 @@
|
||||
{
|
||||
"channel": "Test 1",
|
||||
"date": "2024-02-01",
|
||||
"program": [
|
||||
{
|
||||
"in": 0,
|
||||
"out": 10.0,
|
||||
"duration": 10.0,
|
||||
"source": "tests/assets/media_sorted/DarkGray_00-00-10.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Olive_00-00-30.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 15.0,
|
||||
"duration": 15.0,
|
||||
"source": "tests/assets/media_sorted/Indigo_00-00-15.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 25.0,
|
||||
"duration": 25.0,
|
||||
"source": "tests/assets/media_sorted/DarkOrchid_00-00-25.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 45.0,
|
||||
"duration": 45.0,
|
||||
"source": "tests/assets/media_sorted/Orange_00-00-45.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 20.0,
|
||||
"duration": 20.0,
|
||||
"source": "tests/assets/media_sorted/LightGoldenRodYellow_00-00-20.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Cyan_00-00-30.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 50.0,
|
||||
"duration": 50.0,
|
||||
"source": "tests/assets/media_sorted/Cornsilk_00-00-50.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 15.0,
|
||||
"duration": 15.0,
|
||||
"source": "tests/assets/media_sorted/LightSeaGreen_00-00-15.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Yellow_00-00-30.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Aqua_00-00-30.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 1500.0,
|
||||
"duration": 1500.0,
|
||||
"source": "tests/assets/media_sorted/MediumSeaGreen_00-25-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 1800.0,
|
||||
"duration": 1800.0,
|
||||
"source": "tests/assets/media_sorted/MediumOrchid_00-30-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/Plum_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 3600.0,
|
||||
"duration": 3600.0,
|
||||
"source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 1800.0,
|
||||
"duration": 1800.0,
|
||||
"source": "tests/assets/media_sorted/MediumOrchid_00-30-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 1500.0,
|
||||
"duration": 1500.0,
|
||||
"source": "tests/assets/media_sorted/MediumSeaGreen_00-25-00.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 10.0,
|
||||
"duration": 10.0,
|
||||
"source": "tests/assets/media_sorted/DarkGray_00-00-10.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Olive_00-00-30.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 15.0,
|
||||
"duration": 15.0,
|
||||
"source": "tests/assets/media_sorted/Indigo_00-00-15.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 25.0,
|
||||
"duration": 25.0,
|
||||
"source": "tests/assets/media_sorted/DarkOrchid_00-00-25.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 45.0,
|
||||
"duration": 45.0,
|
||||
"source": "tests/assets/media_sorted/Orange_00-00-45.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 20.0,
|
||||
"duration": 20.0,
|
||||
"source": "tests/assets/media_sorted/LightGoldenRodYellow_00-00-20.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Cyan_00-00-30.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 50.0,
|
||||
"duration": 50.0,
|
||||
"source": "tests/assets/media_sorted/Cornsilk_00-00-50.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 15.0,
|
||||
"duration": 15.0,
|
||||
"source": "tests/assets/media_sorted/LightSeaGreen_00-00-15.mp4"
|
||||
},
|
||||
{
|
||||
"in": 0,
|
||||
"out": 30.0,
|
||||
"duration": 30.0,
|
||||
"source": "tests/assets/media_sorted/Yellow_00-00-30.mp4"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user