diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 988b53b0..20f1cab4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -6,19 +6,12 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] runs-on: ${{ matrix.os }} -# runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: rustup update stable - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} -# key: ubuntu-latest-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - run: rustup component add rustfmt - run: rustup component add clippy - - run: cargo build --all-features - run: cargo test --all-features - run: cargo fmt --all -- --check - run: cargo clippy --all-features --all-targets -- --deny warnings + - run: cargo build --all-features diff --git a/Cargo.lock b/Cargo.lock index 11dd2afa..d8935839 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + [[package]] name = "bytes" version = "1.1.0" @@ -184,6 +190,15 @@ dependencies = [ "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]] name = "fastrand" version = "1.7.0" @@ -209,6 +224,7 @@ dependencies = [ "openssl", "rand", "regex", + "reqwest", "serde", "serde_json", "serde_yaml", @@ -280,6 +296,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "fsevent" version = "0.4.0" @@ -428,6 +454,25 @@ dependencies = [ "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]] name = "hashbrown" version = "0.11.2" @@ -504,6 +549,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", @@ -517,6 +563,19 @@ dependencies = [ "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]] name = "idna" version = "0.2.3" @@ -576,12 +635,27 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itoa" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "jsonrpc-core" version = "18.0.0" @@ -627,7 +701,7 @@ dependencies = [ "log", "tokio", "tokio-stream", - "tokio-util", + "tokio-util 0.6.10", "unicase", ] @@ -992,6 +1066,12 @@ dependencies = [ "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]] name = "pin-project-lite" version = "0.2.9" @@ -1129,6 +1209,42 @@ dependencies = [ "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]] name = "ryu" version = "1.0.10" @@ -1214,6 +1330,18 @@ dependencies = [ "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]] name = "serde_yaml" version = "0.8.24" @@ -1373,6 +1501,16 @@ dependencies = [ "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]] name = "tokio-stream" version = "0.1.8" @@ -1398,6 +1536,20 @@ dependencies = [ "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]] name = "tower-service" version = "0.3.1" @@ -1412,9 +1564,21 @@ checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", + "tracing-attributes", "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]] name = "tracing-core" version = "0.1.26" @@ -1460,6 +1624,18 @@ dependencies = [ "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]] name = "vcpkg" 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" 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]] name = "winapi" version = "0.2.8" @@ -1591,6 +1843,15 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 168e4292..6bcee844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ log = "0.4" notify = "4.0" rand = "0.8" regex = "1" +reqwest = { version = "0.11", features = ["blocking"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.8" diff --git a/src/input/playlist.rs b/src/input/playlist.rs index bc497d66..e7ba909c 100644 --- a/src/input/playlist.rs +++ b/src/input/playlist.rs @@ -11,8 +11,8 @@ use serde_json::json; use simplelog::*; use crate::utils::{ - check_sync, gen_dummy, get_delta, get_sec, is_close, json_serializer::read_json, modified_time, - seek_and_length, valid_source, GlobalConfig, Media, PlayoutStatus, DUMMY_LEN, + check_sync, gen_dummy, get_delta, get_sec, is_close, is_remote, json_serializer::read_json, + modified_time, seek_and_length, valid_source, GlobalConfig, Media, PlayoutStatus, DUMMY_LEN, }; /// Struct for current playlist. @@ -77,50 +77,87 @@ impl CurrentProgram { self.json_path = json.current_file; self.json_mod = json.modified; *self.nodes.lock().unwrap() = json.program; - } else if Path::new(&self.json_path.clone().unwrap()).is_file() { - let mod_time = modified_time(&self.json_path.clone().unwrap()); + } else if Path::new(&self.json_path.clone().unwrap()).is_file() + || is_remote(&self.json_path.clone().unwrap()) + { + let mut is_playlist_changed = false; - if let Some(m) = mod_time { - if !m.to_string().eq(&self.json_mod.clone().unwrap()) { - // when playlist has changed, reload it - info!( - "Reload playlist {}", - self.json_path.clone().unwrap() - ); + if is_remote(&self.json_path.clone().unwrap()) { + let resp = reqwest::blocking::Client::new() + .head(self.json_path.clone().unwrap()) + .send(); + match resp { + 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( - &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); + if let Some(m) = mod_time { + if !m.to_string().eq(&self.json_mod.clone().unwrap()) { + is_playlist_changed = true; + } } } - } else { - error!( - "Playlist {} 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); + if is_playlist_changed { + // when playlist has changed, reload it + info!( + "Reload playlist {}", + 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 {} 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. fn check_for_next_playlist(&mut self) { let current_time = get_sec(); diff --git a/src/utils/json_serializer.rs b/src/utils/json_serializer.rs index 36e504a2..369d82b1 100644 --- a/src/utils/json_serializer.rs +++ b/src/utils/json_serializer.rs @@ -8,7 +8,7 @@ use std::{ 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; @@ -75,29 +75,68 @@ pub fn read_json( current_file = p } - if !playlist_path.is_file() { - error!("Playlist {current_file} not exists!"); + let mut playlist: Playlist; - return Playlist::new(date, start_sec); + if is_remote(¤t_file) { + let resp = reqwest::blocking::Client::new().get(¤t_file).send(); + + match resp { + Ok(resp) => { + if resp.status().is_success() { + info!("Read Remote Playlist: {current_file}"); + + 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 {current_file} not success!: {}", + resp.text().unwrap() + ); + + return Playlist::new(date, start_sec); + } + } + Err(e) => { + error!("Remote Playlist {current_file}: {}", e); + + return Playlist::new(date, start_sec); + } + }; + } else { + if !playlist_path.is_file() { + error!("Playlist {current_file} not exists!"); + + return Playlist::new(date, start_sec); + } + + info!("Read Playlist: {current_file}"); + + let f = File::options() + .read(true) + .write(false) + .open(¤t_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(¤t_file); + + if let Some(modi) = modify { + playlist.modified = Some(modi.to_string()); + } } - info!("Read Playlist: {current_file}"); - - let f = File::options() - .read(true) - .write(false) - .open(¤t_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.current_file = Some(current_file); playlist.start_sec = Some(start_sec); - let modify = modified_time(¤t_file); - - if let Some(modi) = modify { - playlist.modified = Some(modi.to_string()); - } // Add extra values to every media clip for (i, item) in playlist.program.iter_mut().enumerate() { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 36362424..c7a2b8a1 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -429,13 +429,15 @@ pub fn prepare_output_cmd( cmd } +pub fn is_remote(path: &str) -> bool { + Regex::new(r"^https?://.*").unwrap().is_match(path) +} + /// Validate input /// /// Check if input is a remote source, or from storage and see if it exists. pub fn valid_source(source: &str) -> bool { - let re = Regex::new(r"^https?://.*").unwrap(); - - if re.is_match(source) && MediaProbe::new(source).video_streams.is_some() { + if is_remote(source) && MediaProbe::new(source).video_streams.is_some() { return true; }