Merge pull request #133 from pybt/remote-playlist

read remote playlist if playlist path start with http or https
This commit is contained in:
jb-alvarado 2022-06-02 15:32:39 +02:00 committed by GitHub
commit f778140b9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 402 additions and 62 deletions

263
Cargo.lock generated
View File

@ -55,6 +55,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "bumpalo"
version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.1.0" version = "1.1.0"
@ -184,6 +190,15 @@ dependencies = [
"base64", "base64",
] ]
[[package]]
name = "encoding_rs"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
"cfg-if 1.0.0",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.7.0" version = "1.7.0"
@ -209,6 +224,7 @@ dependencies = [
"openssl", "openssl",
"rand", "rand",
"regex", "regex",
"reqwest",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
@ -280,6 +296,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]] [[package]]
name = "fsevent" name = "fsevent"
version = "0.4.0" version = "0.4.0"
@ -428,6 +454,25 @@ dependencies = [
"regex", "regex",
] ]
[[package]]
name = "h2"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util 0.7.2",
"tracing",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.11.2"
@ -504,6 +549,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
@ -517,6 +563,19 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.3" version = "0.2.3"
@ -576,12 +635,27 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ipnet"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "js-sys"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "jsonrpc-core" name = "jsonrpc-core"
version = "18.0.0" version = "18.0.0"
@ -627,7 +701,7 @@ dependencies = [
"log", "log",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util 0.6.10",
"unicase", "unicase",
] ]
@ -992,6 +1066,12 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.9" version = "0.2.9"
@ -1129,6 +1209,42 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "reqwest"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"lazy_static",
"log",
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.10" version = "1.0.10"
@ -1214,6 +1330,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.8.24" version = "0.8.24"
@ -1373,6 +1501,16 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.8" version = "0.1.8"
@ -1398,6 +1536,20 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-util"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.1" version = "0.3.1"
@ -1412,9 +1564,21 @@ checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-attributes"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.26" version = "0.1.26"
@ -1460,6 +1624,18 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@ -1505,6 +1681,82 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
[[package]]
name = "web-sys"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.8" version = "0.2.8"
@ -1591,6 +1843,15 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "ws2_32-sys" name = "ws2_32-sys"
version = "0.2.1" version = "0.2.1"

View File

@ -20,6 +20,7 @@ log = "0.4"
notify = "4.0" notify = "4.0"
rand = "0.8" rand = "0.8"
regex = "1" regex = "1"
reqwest = { version = "0.11", features = ["blocking"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
serde_yaml = "0.8" serde_yaml = "0.8"

View File

@ -11,8 +11,8 @@ use serde_json::json;
use simplelog::*; use simplelog::*;
use crate::utils::{ use crate::utils::{
check_sync, gen_dummy, get_delta, get_sec, is_close, json_serializer::read_json, modified_time, check_sync, gen_dummy, get_delta, get_sec, is_close, is_remote, json_serializer::read_json,
seek_and_length, valid_source, GlobalConfig, Media, PlayoutStatus, DUMMY_LEN, modified_time, seek_and_length, valid_source, GlobalConfig, Media, PlayoutStatus, DUMMY_LEN,
}; };
/// Struct for current playlist. /// Struct for current playlist.
@ -77,50 +77,87 @@ impl CurrentProgram {
self.json_path = json.current_file; self.json_path = json.current_file;
self.json_mod = json.modified; self.json_mod = json.modified;
*self.nodes.lock().unwrap() = json.program; *self.nodes.lock().unwrap() = json.program;
} else if Path::new(&self.json_path.clone().unwrap()).is_file() { } else if Path::new(&self.json_path.clone().unwrap()).is_file()
let mod_time = modified_time(&self.json_path.clone().unwrap()); || is_remote(&self.json_path.clone().unwrap())
{
let mut is_playlist_changed = false;
if let Some(m) = mod_time { if is_remote(&self.json_path.clone().unwrap()) {
if !m.to_string().eq(&self.json_mod.clone().unwrap()) { let resp = reqwest::blocking::Client::new()
// when playlist has changed, reload it .head(self.json_path.clone().unwrap())
info!( .send();
"Reload playlist <b><magenta>{}</></b>", match resp {
self.json_path.clone().unwrap() Ok(resp) => {
); if resp.status().is_success() {
match resp.headers().get(reqwest::header::LAST_MODIFIED) {
Some(last_modified) => {
if !last_modified
.to_str()
.unwrap()
.eq(&self.json_mod.clone().unwrap())
{
is_playlist_changed = true
}
}
None => {}
}
}
}
Err(_) => self.on_check_update_error(),
};
} else {
let mod_time = modified_time(&self.json_path.clone().unwrap());
let json = read_json( if let Some(m) = mod_time {
&self.config, if !m.to_string().eq(&self.json_mod.clone().unwrap()) {
self.json_path.clone(), is_playlist_changed = true;
self.is_terminated.clone(), }
false,
0.0,
);
self.json_mod = json.modified;
*self.nodes.lock().unwrap() = json.program;
self.get_current_clip();
self.index.fetch_add(1, Ordering::SeqCst);
} }
} }
} else {
error!(
"Playlist <b><magenta>{}</></b> not exists!",
self.json_path.clone().unwrap()
);
let mut media = Media::new(0, String::new(), false);
media.begin = Some(get_sec());
media.duration = DUMMY_LEN;
media.out = DUMMY_LEN;
self.json_path = None; if is_playlist_changed {
*self.nodes.lock().unwrap() = vec![media.clone()]; // when playlist has changed, reload it
self.current_node = media; info!(
self.playout_stat.list_init.store(true, Ordering::SeqCst); "Reload playlist <b><magenta>{}</></b>",
self.index.store(0, Ordering::SeqCst); self.json_path.clone().unwrap()
);
let json = read_json(
&self.config,
self.json_path.clone(),
self.is_terminated.clone(),
false,
0.0,
);
self.json_mod = json.modified;
*self.nodes.lock().unwrap() = json.program;
self.get_current_clip();
self.index.fetch_add(1, Ordering::SeqCst);
}
} else {
self.on_check_update_error();
} }
} }
fn on_check_update_error(&mut self) {
error!(
"Playlist <b><magenta>{}</></b> not exists!",
self.json_path.clone().unwrap()
);
let mut media = Media::new(0, String::new(), false);
media.begin = Some(get_sec());
media.duration = DUMMY_LEN;
media.out = DUMMY_LEN;
self.json_path = None;
*self.nodes.lock().unwrap() = vec![media.clone()];
self.current_node = media;
self.playout_stat.list_init.store(true, Ordering::SeqCst);
self.index.store(0, Ordering::SeqCst);
}
// Check if day is past and it is time for a new playlist. // Check if day is past and it is time for a new playlist.
fn check_for_next_playlist(&mut self) { fn check_for_next_playlist(&mut self) {
let current_time = get_sec(); let current_time = get_sec();

View File

@ -8,7 +8,7 @@ use std::{
use simplelog::*; use simplelog::*;
use crate::utils::{get_date, modified_time, validate_playlist, GlobalConfig, Media}; use crate::utils::{get_date, is_remote, modified_time, validate_playlist, GlobalConfig, Media};
pub const DUMMY_LEN: f64 = 60.0; pub const DUMMY_LEN: f64 = 60.0;
@ -75,29 +75,68 @@ pub fn read_json(
current_file = p current_file = p
} }
if !playlist_path.is_file() { let mut playlist: Playlist;
error!("Playlist <b><magenta>{current_file}</></b> not exists!");
return Playlist::new(date, start_sec); if is_remote(&current_file) {
let resp = reqwest::blocking::Client::new().get(&current_file).send();
match resp {
Ok(resp) => {
if resp.status().is_success() {
info!("Read Remote Playlist: <b><magenta>{current_file}</></b>");
let headers = resp.headers().clone();
let body = resp.text().unwrap();
playlist =
serde_json::from_str(&body).expect("Could not read json playlist str.");
match headers.get(reqwest::header::LAST_MODIFIED) {
Some(t) => {
playlist.modified = Some(t.to_str().unwrap().to_string());
}
None => {}
}
} else {
error!(
"Get Remote Playlist <b><magenta>{current_file}</></b> not success!: {}",
resp.text().unwrap()
);
return Playlist::new(date, start_sec);
}
}
Err(e) => {
error!("Remote Playlist <b><magenta>{current_file}</></b>: {}", e);
return Playlist::new(date, start_sec);
}
};
} else {
if !playlist_path.is_file() {
error!("Playlist <b><magenta>{current_file}</></b> not exists!");
return Playlist::new(date, start_sec);
}
info!("Read Playlist: <b><magenta>{current_file}</></b>");
let f = File::options()
.read(true)
.write(false)
.open(&current_file)
.expect("Could not open json playlist file.");
playlist = serde_json::from_reader(f).expect("Could not read json playlist file.");
let modify = modified_time(&current_file);
if let Some(modi) = modify {
playlist.modified = Some(modi.to_string());
}
} }
info!("Read Playlist: <b><magenta>{current_file}</></b>"); playlist.current_file = Some(current_file);
let f = File::options()
.read(true)
.write(false)
.open(&current_file)
.expect("Could not open json playlist file.");
let mut playlist: Playlist =
serde_json::from_reader(f).expect("Could not read json playlist file.");
playlist.current_file = Some(current_file.clone());
playlist.start_sec = Some(start_sec); playlist.start_sec = Some(start_sec);
let modify = modified_time(&current_file);
if let Some(modi) = modify {
playlist.modified = Some(modi.to_string());
}
// Add extra values to every media clip // Add extra values to every media clip
for (i, item) in playlist.program.iter_mut().enumerate() { for (i, item) in playlist.program.iter_mut().enumerate() {

View File

@ -429,13 +429,15 @@ pub fn prepare_output_cmd(
cmd cmd
} }
pub fn is_remote(path: &str) -> bool {
Regex::new(r"^https?://.*").unwrap().is_match(path)
}
/// Validate input /// Validate input
/// ///
/// Check if input is a remote source, or from storage and see if it exists. /// Check if input is a remote source, or from storage and see if it exists.
pub fn valid_source(source: &str) -> bool { pub fn valid_source(source: &str) -> bool {
let re = Regex::new(r"^https?://.*").unwrap(); if is_remote(source) && MediaProbe::new(source).video_streams.is_some() {
if re.is_match(source) && MediaProbe::new(source).video_streams.is_some() {
return true; return true;
} }