run task on clip change, #276

This commit is contained in:
jb-alvarado 2023-07-19 21:45:15 +02:00
parent f248e4bf37
commit 5bd1b23513
7 changed files with 104 additions and 47 deletions

View File

@ -117,6 +117,13 @@ text:
style: "x=(w-tw)/2:y=(h-line_h)*0.9:fontsize=24:fontcolor=#ffffff:box=1:boxcolor=#000000:boxborderw=4" style: "x=(w-tw)/2:y=(h-line_h)*0.9:fontsize=24:fontcolor=#ffffff:box=1:boxcolor=#000000:boxborderw=4"
regex: ^.+[/\\](.*)(.mp4|.mkv)$ regex: ^.+[/\\](.*)(.mp4|.mkv)$
task:
help_text: Run an external program with a given media object. The media object is in json format
and contains all the information about the current clip. The external program can be a script
or a binary, but should only run for a short time.
enable: false
path:
out: out:
help_text: The final playout compression. Set the settings to your needs. 'mode' help_text: The final playout compression. Set the settings to your needs. 'mode'
has the options 'desktop', 'hls', 'null', 'stream'. Use 'stream' and adjust has the options 'desktop', 'hls', 'null', 'stream'. Use 'stream' and adjust

View File

@ -1,5 +1,6 @@
use std::{ use std::{
io::{prelude::*, BufReader, BufWriter, Read}, io::{prelude::*, BufReader, BufWriter, Read},
path::Path,
process::{Command, Stdio}, process::{Command, Stdio},
sync::atomic::Ordering, sync::atomic::Ordering,
thread::{self, sleep}, thread::{self, sleep},
@ -17,6 +18,8 @@ mod stream;
pub use hls::write_hls; pub use hls::write_hls;
use crate::input::{ingest_server, source_generator}; use crate::input::{ingest_server, source_generator};
use crate::utils::task_runner;
use ffplayout_lib::utils::{ use ffplayout_lib::utils::{
sec_to_time, stderr_reader, OutputMode::*, PlayerControl, PlayoutConfig, PlayoutStatus, sec_to_time, stderr_reader, OutputMode::*, PlayerControl, PlayoutConfig, PlayoutStatus,
ProcessControl, ProcessUnit::*, ProcessControl, ProcessUnit::*,
@ -83,11 +86,6 @@ pub fn player(
'source_iter: for node in get_source { 'source_iter: for node in get_source {
*play_control.current_media.lock().unwrap() = Some(node.clone()); *play_control.current_media.lock().unwrap() = Some(node.clone());
let mut cmd = match node.cmd {
Some(cmd) => cmd,
None => break,
};
if !node.process.unwrap() { if !node.process.unwrap() {
continue; continue;
} }
@ -99,6 +97,23 @@ pub fn player(
node.audio node.audio
); );
if config.task.enable {
let task_config = config.clone();
let task_node = node.clone();
let server_running = proc_control.server_is_running.load(Ordering::SeqCst);
if Path::new(&config.task.path).is_file() {
thread::spawn(move || task_runner::run(task_config, task_node, server_running));
} else {
error!("<bright-blue>{}</> executable not exists!", config.task.path);
}
}
let mut cmd = match node.cmd {
Some(cmd) => cmd,
None => break,
};
let mut dec_cmd = vec_strings!["-hide_banner", "-nostats", "-v", &ff_log_format]; let mut dec_cmd = vec_strings!["-hide_banner", "-nostats", "-v", &ff_log_format];
dec_cmd.append(&mut cmd); dec_cmd.append(&mut cmd);

View File

@ -13,9 +13,10 @@ use std::io::{Cursor, Error as IoError};
use tiny_http::{Header, Method, Request, Response, Server}; use tiny_http::{Header, Method, Request, Response, Server};
use crate::rpc::zmq_send; use crate::rpc::zmq_send;
use crate::utils::{get_data_map, get_media_map};
use ffplayout_lib::utils::{ use ffplayout_lib::utils::{
get_delta, get_sec, sec_to_time, write_status, Ingest, Media, OutputMode::*, PlayerControl, get_delta, write_status, Ingest, OutputMode::*, PlayerControl, PlayoutConfig,
PlayoutConfig, PlayoutStatus, ProcessControl, PlayoutStatus, ProcessControl,
}; };
#[derive(Default, Deserialize, Clone, Debug)] #[derive(Default, Deserialize, Clone, Debug)]
@ -129,45 +130,6 @@ fn filter_from_json(raw_text: serde_json::Value) -> String {
filter.to_string() filter.to_string()
} }
/// map media struct to json object
fn get_media_map(media: Media) -> Value {
json!({
"seek": media.seek,
"out": media.out,
"duration": media.duration,
"category": media.category,
"source": media.source,
})
}
/// prepare json object for response
fn get_data_map(
config: &PlayoutConfig,
media: Media,
server_is_running: bool,
) -> Map<String, Value> {
let mut data_map = Map::new();
let begin = media.begin.unwrap_or(0.0);
data_map.insert("play_mode".to_string(), json!(config.processing.mode));
data_map.insert("ingest_runs".to_string(), json!(server_is_running));
data_map.insert("index".to_string(), json!(media.index));
data_map.insert("start_sec".to_string(), json!(begin));
if begin > 0.0 {
let played_time = get_sec() - begin;
let remaining_time = media.out - played_time;
data_map.insert("start_time".to_string(), json!(sec_to_time(begin)));
data_map.insert("played_sec".to_string(), json!(played_time));
data_map.insert("remaining_sec".to_string(), json!(remaining_time));
}
data_map.insert("current_media".to_string(), get_media_map(media));
data_map
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct ResponseData { struct ResponseData {
message: String, message: String,

View File

@ -4,14 +4,18 @@ use std::{
}; };
use regex::Regex; use regex::Regex;
use serde_json::{json, Map, Value};
use simplelog::*; use simplelog::*;
pub mod arg_parse; pub mod arg_parse;
pub mod task_runner;
pub use arg_parse::Args; pub use arg_parse::Args;
use ffplayout_lib::{ use ffplayout_lib::{
filter::Filters, filter::Filters,
utils::{time_to_sec, OutputMode::*, PlayoutConfig, ProcessMode::*}, utils::{
get_sec, sec_to_time, time_to_sec, Media, OutputMode::*, PlayoutConfig, ProcessMode::*,
},
vec_strings, vec_strings,
}; };
@ -207,3 +211,42 @@ pub fn prepare_output_cmd(
cmd cmd
} }
/// map media struct to json object
pub fn get_media_map(media: Media) -> Value {
json!({
"seek": media.seek,
"out": media.out,
"duration": media.duration,
"category": media.category,
"source": media.source,
})
}
/// prepare json object for response
pub fn get_data_map(
config: &PlayoutConfig,
media: Media,
server_is_running: bool,
) -> Map<String, Value> {
let mut data_map = Map::new();
let begin = media.begin.unwrap_or(0.0);
data_map.insert("play_mode".to_string(), json!(config.processing.mode));
data_map.insert("ingest_runs".to_string(), json!(server_is_running));
data_map.insert("index".to_string(), json!(media.index));
data_map.insert("start_sec".to_string(), json!(begin));
if begin > 0.0 {
let played_time = get_sec() - begin;
let remaining_time = media.out - played_time;
data_map.insert("start_time".to_string(), json!(sec_to_time(begin)));
data_map.insert("played_sec".to_string(), json!(played_time));
data_map.insert("remaining_sec".to_string(), json!(remaining_time));
}
data_map.insert("current_media".to_string(), get_media_map(media));
data_map
}

View File

@ -0,0 +1,15 @@
use std::process::Command;
use simplelog::*;
use crate::utils::get_data_map;
use ffplayout_lib::utils::{config::PlayoutConfig, Media};
pub fn run(config: PlayoutConfig, node: Media, server_running: bool) {
let obj = serde_json::to_string(&get_data_map(&config, node, server_running)).unwrap();
trace!("Run task: {obj}");
if let Err(e) = Command::new(config.task.path).arg(obj).spawn() {
error!("Couldn't spawn task runner: {e}");
};
}

View File

@ -138,6 +138,8 @@ pub struct PlayoutConfig {
pub playlist: Playlist, pub playlist: Playlist,
pub storage: Storage, pub storage: Storage,
pub text: Text, pub text: Text,
#[serde(default)]
pub task: Task,
pub out: Out, pub out: Out,
} }
@ -292,6 +294,12 @@ pub struct Text {
pub regex: String, pub regex: String,
} }
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct Task {
pub enable: bool,
pub path: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Out { pub struct Out {
pub help_text: String, pub help_text: String,

7
scripts/task-runner.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/bash
# media object
mObj=$1
# perform a meaningful task
notify-send -u normal "ffplayout" -t 2 -e "Play: $(echo $mObj | jq -r '.current_media.source')"