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"
|
||||
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:
|
||||
help_text: The final playout compression. Set the settings to your needs. 'mode'
|
||||
has the options 'desktop', 'hls', 'null', 'stream'. Use 'stream' and adjust
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
io::{prelude::*, BufReader, BufWriter, Read},
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
sync::atomic::Ordering,
|
||||
thread::{self, sleep},
|
||||
@ -17,6 +18,8 @@ mod stream;
|
||||
pub use hls::write_hls;
|
||||
|
||||
use crate::input::{ingest_server, source_generator};
|
||||
use crate::utils::task_runner;
|
||||
|
||||
use ffplayout_lib::utils::{
|
||||
sec_to_time, stderr_reader, OutputMode::*, PlayerControl, PlayoutConfig, PlayoutStatus,
|
||||
ProcessControl, ProcessUnit::*,
|
||||
@ -83,11 +86,6 @@ pub fn player(
|
||||
'source_iter: for node in get_source {
|
||||
*play_control.current_media.lock().unwrap() = Some(node.clone());
|
||||
|
||||
let mut cmd = match node.cmd {
|
||||
Some(cmd) => cmd,
|
||||
None => break,
|
||||
};
|
||||
|
||||
if !node.process.unwrap() {
|
||||
continue;
|
||||
}
|
||||
@ -99,6 +97,23 @@ pub fn player(
|
||||
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];
|
||||
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 crate::rpc::zmq_send;
|
||||
use crate::utils::{get_data_map, get_media_map};
|
||||
use ffplayout_lib::utils::{
|
||||
get_delta, get_sec, sec_to_time, write_status, Ingest, Media, OutputMode::*, PlayerControl,
|
||||
PlayoutConfig, PlayoutStatus, ProcessControl,
|
||||
get_delta, write_status, Ingest, OutputMode::*, PlayerControl, PlayoutConfig,
|
||||
PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
#[derive(Default, Deserialize, Clone, Debug)]
|
||||
@ -129,45 +130,6 @@ fn filter_from_json(raw_text: serde_json::Value) -> 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)]
|
||||
struct ResponseData {
|
||||
message: String,
|
||||
|
@ -4,14 +4,18 @@ use std::{
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
use serde_json::{json, Map, Value};
|
||||
use simplelog::*;
|
||||
|
||||
pub mod arg_parse;
|
||||
pub mod task_runner;
|
||||
|
||||
pub use arg_parse::Args;
|
||||
use ffplayout_lib::{
|
||||
filter::Filters,
|
||||
utils::{time_to_sec, OutputMode::*, PlayoutConfig, ProcessMode::*},
|
||||
utils::{
|
||||
get_sec, sec_to_time, time_to_sec, Media, OutputMode::*, PlayoutConfig, ProcessMode::*,
|
||||
},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
@ -207,3 +211,42 @@ pub fn prepare_output_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 storage: Storage,
|
||||
pub text: Text,
|
||||
#[serde(default)]
|
||||
pub task: Task,
|
||||
pub out: Out,
|
||||
}
|
||||
|
||||
@ -292,6 +294,12 @@ pub struct Text {
|
||||
pub regex: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct Task {
|
||||
pub enable: bool,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Out {
|
||||
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