run task on clip change, #276
This commit is contained in:
parent
f248e4bf37
commit
5bd1b23513
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
15
ffplayout-engine/src/utils/task_runner.rs
Normal file
15
ffplayout-engine/src/utils/task_runner.rs
Normal 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}");
|
||||||
|
};
|
||||||
|
}
|
@ -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
7
scripts/task-runner.sh
Executable 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')"
|
Loading…
Reference in New Issue
Block a user