refractor code

This commit is contained in:
jb-alvarado 2022-06-21 23:10:38 +02:00
parent 529de7bba8
commit 07b1bd30ae
54 changed files with 603 additions and 388 deletions

91
Cargo.lock generated
View File

@ -315,9 +315,9 @@ dependencies = [
[[package]]
name = "async-global-executor"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880"
checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940"
dependencies = [
"async-channel",
"async-executor",
@ -325,6 +325,7 @@ dependencies = [
"async-lock",
"blocking",
"futures-lite",
"num_cpus",
"once_cell",
]
@ -588,6 +589,7 @@ dependencies = [
"libc",
"num-integer",
"num-traits",
"time 0.1.44",
"winapi 0.3.9",
]
@ -605,9 +607,9 @@ dependencies = [
[[package]]
name = "clap"
version = "3.2.5"
version = "3.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
checksum = "9f1fe12880bae935d142c8702d500c63a4e8634b6c3c57ad72bf978fc7b6249a"
dependencies = [
"atty",
"bitflags",
@ -622,9 +624,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "3.2.5"
version = "3.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
checksum = "ed6db9e867166a43a53f7199b5e4d1f522a1e5bd626654be263c999ce59df39a"
dependencies = [
"heck",
"proc-macro-error",
@ -635,9 +637,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
checksum = "87eba3c8c7f42ef17f6c659fc7416d0f4758cd3e58861ee63c5fa4a4dde649e4"
dependencies = [
"os_str_bytes",
]
@ -977,26 +979,21 @@ dependencies = [
]
[[package]]
name = "ffplayout-engine"
version = "0.9.9"
name = "ffplayout-api"
version = "0.3.0"
dependencies = [
"actix-web",
"actix-web-grants",
"actix-web-httpauth",
"argon2",
"chrono 0.4.19 (git+https://github.com/sbrocket/chrono?branch=parse-error-kind-public)",
"chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)",
"clap",
"crossbeam-channel 0.5.5",
"derive_more",
"faccess",
"ffplayout-lib",
"ffprobe",
"file-rotate",
"futures",
"jsonrpc-http-server",
"jsonwebtoken",
"lettre",
"log",
"notify",
"once_cell",
"openssl",
"rand 0.8.5",
@ -1007,14 +1004,68 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"shlex",
"simplelog",
"sqlx",
]
[[package]]
name = "ffplayout-engine"
version = "0.9.9"
dependencies = [
"chrono 0.4.19 (git+https://github.com/sbrocket/chrono?branch=parse-error-kind-public)",
"clap",
"crossbeam-channel 0.5.5",
"faccess",
"ffplayout-lib",
"ffprobe",
"file-rotate",
"futures",
"jsonrpc-http-server",
"lettre",
"log",
"notify",
"openssl",
"rand 0.8.5",
"regex",
"reqwest",
"serde",
"serde_json",
"serde_yaml",
"shlex",
"simplelog",
"time 0.3.10",
"walkdir",
"zeromq",
]
[[package]]
name = "ffplayout-lib"
version = "0.9.9"
dependencies = [
"chrono 0.4.19 (git+https://github.com/sbrocket/chrono?branch=parse-error-kind-public)",
"crossbeam-channel 0.5.5",
"faccess",
"ffprobe",
"file-rotate",
"futures",
"jsonrpc-http-server",
"lettre",
"log",
"notify",
"once_cell",
"openssl",
"rand 0.8.5",
"regex",
"reqwest",
"serde",
"serde_json",
"serde_yaml",
"shlex",
"simplelog",
"time 0.3.10",
"walkdir",
]
[[package]]
name = "ffprobe"
version = "0.3.2"
@ -1474,9 +1525,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.9.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown 0.12.1",

View File

@ -1,108 +1,12 @@
[package]
name = "ffplayout-engine"
description = "24/7 playout based on rust and ffmpeg"
license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md"
version = "0.9.9"
edition = "2021"
default-run = "ffplayout"
[workspace]
[dependencies]
actix-web = "4"
actix-web-grants = "3"
actix-web-httpauth = "0.6"
argon2 = "0.4"
chrono = { git = "https://github.com/sbrocket/chrono", branch = "parse-error-kind-public" }
clap = { version = "3.2", features = ["derive"] }
crossbeam-channel = "0.5"
derive_more = "0.99"
faccess = "0.2"
ffprobe = "0.3"
file-rotate = { git = "https://github.com/Ploppz/file-rotate.git", branch = "timestamp-parse-fix" }
futures = "0.3"
jsonrpc-http-server = "18.0"
jsonwebtoken = "8"
lettre = "0.10.0-rc.7"
log = "0.4"
notify = "4.0"
once_cell = "1.10"
rand = "0.8"
rand_core = { version = "0.6", features = ["std"] }
relative-path = "1.6"
regex = "1"
reqwest = { version = "0.11", features = ["blocking", "json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
shlex = "1.1"
simplelog = { version = "^0.12", features = ["paris"] }
sqlx = { version = "0.5", features = [
"chrono",
"runtime-actix-native-tls",
"sqlite"
] }
time = { version = "0.3", features = ["formatting", "macros"] }
walkdir = "2"
zeromq = { git = "https://github.com/zeromq/zmq.rs.git", default-features = false, features = [
"async-std-runtime",
"tcp-transport"
] }
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
[[bin]]
name = "ffplayout"
path = "src/main.rs"
[[bin]]
name = "ffpapi"
path = "src/bin/ffpapi.rs"
members = [
"ffplayout-api",
"ffplayout-engine",
"lib",
]
[profile.release]
opt-level = 3
strip = true
lto = true
# DEBIAN DEB PACKAGE
[package.metadata.deb]
name = "ffplayout-engine"
priority = "optional"
section = "net"
license-file = ["LICENSE", "0"]
depends = ""
suggests = "ffmpeg"
copyright = "Copyright (c) 2022, Jonathan Baecker. All rights reserved."
conf-files = ["/etc/ffplayout/ffplayout.yml"]
assets = [
[
"target/x86_64-unknown-linux-musl/release/ffplayout",
"/usr/bin/ffplayout",
"755"
],
[
"target/x86_64-unknown-linux-musl/release/ffpapi",
"/usr/bin/ffpapi",
"755"
],
["assets/ffplayout.yml", "/etc/ffplayout/ffplayout.yml", "644"],
["assets/logo.png", "/usr/share/ffplayout/logo.png", "644"],
["README.md", "/usr/share/doc/ffplayout-engine/README", "644"],
]
maintainer-scripts = "debian/"
systemd-units = { enable = false, unit-scripts = "assets" }
# REHL RPM PACKAGE
[package.metadata.generate-rpm]
name = "ffplayout-engine"
license = "GPL-3.0"
assets = [
{ source = "target/x86_64-unknown-linux-musl/release/ffplayout", dest = "/usr/bin/ffplayout", mode = "755" },
{ source = "target/x86_64-unknown-linux-musl/release/ffpapi", dest = "/usr/bin/ffpapi", mode = "755" },
{ source = "assets/ffplayout.yml", dest = "/etc/ffplayout/ffplayout.yml", mode = "644", config = true },
{ source = "assets/ffplayout-engine.service", dest = "/lib/systemd/system/ffplayout-engine.service", mode = "644" },
{ source = "README.md", dest = "/usr/share/doc/ffplayout-engine/README", mode = "644", doc = true },
{ source = "LICENSE", dest = "/usr/share/doc/ffplayout-engine/LICENSE", mode = "644" },
{ source = "assets/logo.png", dest = "/usr/share/ffplayout/logo.png", mode = "644" },
]

100
build_all.sh Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/bash
targets=("x86_64-unknown-linux-musl" "x86_64-pc-windows-gnu" "x86_64-apple-darwin" "aarch64-apple-darwin")
IFS="= "
while read -r name value; do
if [[ $name == "version" ]]; then
version=${value//\"/}
fi
done < ffplayout-engine/Cargo.toml
echo "Compile ffplayout-engine version is: \"$version\""
echo ""
for target in "${targets[@]}"; do
echo "compile static for $target"
echo ""
cargo build --release --target=$target --bin ffplayout
if [[ $target == "x86_64-pc-windows-gnu" ]]; then
if [[ -f "ffplayout-engine-v${version}_${target}.zip" ]]; then
rm -f "ffplayout-engine-v${version}_${target}.zip"
fi
cp ./target/${target}/release/ffplayout.exe .
zip -r "ffplayout-engine-v${version}_${target}.zip" assets docs LICENSE README.md ffplayout.exe -x *.db
rm -f ffplayout.exe
elif [[ $target == "x86_64-apple-darwin" ]] || [[ $target == "aarch64-apple-darwin" ]]; then
if [[ -f "ffplayout-engine-v${version}_${target}.tar.gz" ]]; then
rm -f "ffplayout-engine-v${version}_${target}.tar.gz"
fi
cp ./target/${target}/release/ffplayout .
tar -czvf "ffplayout-engine-v${version}_${target}.tar.gz" --exclude='*.db' assets docs LICENSE README.md ffplayout
rm -f ffplayout
else
if [[ -f "ffplayout-engine-v${version}_${target}.tar.gz" ]]; then
rm -f "ffplayout-engine-v${version}_${target}.tar.gz"
fi
cp ./target/${target}/release/ffplayout .
tar -czvf "ffplayout-engine-v${version}_${target}.tar.gz" --exclude='*.db' assets docs LICENSE README.md ffplayout
rm -f ffplayout
fi
echo ""
done
cargo deb --target=x86_64-unknown-linux-musl -p ffplayout-engine
mv ./target/x86_64-unknown-linux-musl/debian/ffplayout-engine_${version}_amd64.deb .
cargo generate-rpm --target=x86_64-unknown-linux-musl -p ffplayout-engine
mv ./target/x86_64-unknown-linux-musl/generate-rpm/ffplayout-engine-${version}-1.x86_64.rpm .
IFS="= "
while read -r name value; do
if [[ $name == "version" ]]; then
version=${value//\"/}
fi
done < ffplayout-api/Cargo.toml
echo "Compile ffplayout-api version is: \"$version\""
echo ""
for target in "${targets[@]}"; do
echo "compile static for $target"
echo ""
if [[ $target == "x86_64-pc-windows-gnu" ]]; then
if [[ -f "ffplayout-api-v${version}_${target}.zip" ]]; then
rm -f "ffplayout-api-v${version}_${target}.zip"
fi
cargo build --release --target=$target --bin ffpapi
cp ./target/${target}/release/ffpapi.exe .
zip -r "ffplayout-api-v${version}_${target}.zip" assets docs LICENSE README.md ffpapi.exe -x *.db
rm -f ffpapi.exe
elif [[ $target == "x86_64-unknown-linux-musl" ]]; then
if [[ -f "ffplayout-api-v${version}_${target}.tar.gz" ]]; then
rm -f "ffplayout-api-v${version}_${target}.tar.gz"
fi
cargo build --release --target=$target --bin ffpapi
cp ./target/${target}/release/ffpapi .
tar -czvf "ffplayout-api-v${version}_${target}.tar.gz" --exclude='*.db' assets docs LICENSE README.md ffpapi
rm -f ffpapi
fi
echo ""
done
cargo deb --target=x86_64-unknown-linux-musl -p ffplayout-api
mv ./target/x86_64-unknown-linux-musl/debian/ffplayout-api_${version}_amd64.deb .
cargo generate-rpm --target=x86_64-unknown-linux-musl -p ffplayout-api
mv ./target/x86_64-unknown-linux-musl/generate-rpm/ffplayout-api-${version}-1.x86_64.rpm .

View File

@ -1,56 +0,0 @@
#!/usr/bin/bash
targets=("x86_64-unknown-linux-musl" "x86_64-pc-windows-gnu" "x86_64-apple-darwin" "aarch64-apple-darwin")
IFS="= "
while read -r name value; do
if [[ $name == "version" ]]; then
version=${value//\"/}
fi
done < Cargo.toml
echo "Compile ffplayout-engine version is: \"$version\""
echo ""
for target in "${targets[@]}"; do
echo "compile static for $target"
echo ""
cargo build --release --target=$target
if [[ $target == "x86_64-pc-windows-gnu" ]]; then
if [[ -f "ffplayout-engine-v${version}_${target}.zip" ]]; then
rm -f "ffplayout-engine-v${version}_${target}.zip"
fi
cp ./target/${target}/release/ffplayout.exe .
cp ./target/${target}/release/ffpapi.exe .
zip -r "ffplayout-engine-v${version}_${target}.zip" assets docs LICENSE README.md ffplayout.exe ffpapi.exe -x *.db
rm -f ffplayout.exe ffpapi.exe
else
if [[ -f "ffplayout-engine-v${version}_${target}.tar.gz" ]]; then
rm -f "ffplayout-engine-v${version}_${target}.tar.gz"
fi
cp ./target/${target}/release/ffplayout .
cp ./target/${target}/release/ffpapi .
tar -czvf "ffplayout-engine-v${version}_${target}.tar.gz" --exclude='*.db' assets docs LICENSE README.md ffplayout ffpapi
rm -f ffplayout ffpapi
fi
echo ""
done
echo "Create debian package"
echo ""
cargo deb --target=x86_64-unknown-linux-musl
mv ./target/x86_64-unknown-linux-musl/debian/ffplayout-engine_${version}_amd64.deb .
echo ""
echo "Create rhel package"
echo ""
cargo generate-rpm --target=x86_64-unknown-linux-musl
mv ./target/x86_64-unknown-linux-musl/generate-rpm/ffplayout-engine-${version}-1.x86_64.rpm .

76
ffplayout-api/Cargo.toml Normal file
View File

@ -0,0 +1,76 @@
[package]
name = "ffplayout-api"
description = "Rest API for ffplayout"
license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md"
version = "0.3.0"
edition = "2021"
[dependencies]
ffplayout-lib = { path = "../lib" }
actix-web = "4"
actix-web-grants = "3"
actix-web-httpauth = "0.6"
argon2 = "0.4"
chrono = "0.4"
clap = { version = "3.2", features = ["derive"] }
derive_more = "0.99"
faccess = "0.2"
ffprobe = "0.3"
jsonwebtoken = "8"
log = "0.4"
once_cell = "1.10"
rand = "0.8"
rand_core = { version = "0.6", features = ["std"] }
relative-path = "1.6"
regex = "1"
reqwest = { version = "0.11", features = ["blocking", "json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
simplelog = { version = "^0.12", features = ["paris"] }
sqlx = { version = "0.5", features = [
"chrono",
"runtime-actix-native-tls",
"sqlite"
] }
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
[[bin]]
name = "ffpapi"
path = "src/main.rs"
# DEBIAN DEB PACKAGE
[package.metadata.deb]
name = "ffplayout-api"
priority = "optional"
section = "net"
license-file = ["../LICENSE", "0"]
depends = ""
suggests = "ffmpeg"
copyright = "Copyright (c) 2022, Jonathan Baecker. All rights reserved."
conf-files = ["/etc/ffplayout/ffplayout.yml"]
assets = [
[
"../target/x86_64-unknown-linux-musl/release/ffpapi",
"/usr/bin/ffpapi",
"755"
],
["README.md", "/usr/share/doc/ffplayout/README", "644"],
]
maintainer-scripts = "debian/"
systemd-units = { enable = false, unit-scripts = "unit" }
# REHL RPM PACKAGE
[package.metadata.generate-rpm]
name = "ffplayout-api"
license = "GPL-3.0"
assets = [
{ source = "../target/x86_64-unknown-linux-musl/release/ffpapi", dest = "/usr/bin/ffpapi", mode = "755" },
{ source = "unit/ffpapi.service", dest = "/lib/systemd/system/ffpapi.service", mode = "644" },
{ source = "README.md", dest = "/usr/share/doc/ffplayout/README", mode = "644", doc = true },
{ source = "../LICENSE", dest = "/usr/share/doc/ffplayout/LICENSE", mode = "644" },
]

2
ffplayout-api/README.md Normal file
View File

@ -0,0 +1,2 @@
**ffplayout-api**
================

View File

@ -8,10 +8,11 @@ use actix_web_httpauth::middleware::HttpAuthentication;
use clap::Parser;
use simplelog::*;
use ffplayout_engine::{
api::{
pub mod utils;
use utils::{
args_parse::Args,
auth,
auth, db_path, init_config,
models::LoginUser,
routes::{
add_preset, add_user, del_playlist, file_browser, gen_playlist, get_playlist,
@ -19,11 +20,11 @@ use ffplayout_engine::{
media_current, media_last, media_next, patch_settings, reset_playout, save_playlist,
send_text_message, update_playout_config, update_preset, update_user,
},
utils::{db_path, init_config, run_args, Role},
},
utils::{init_logging, PlayoutConfig},
run_args, Role,
};
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> {
// We just get permissions from JWT
let claims = auth::decode_jwt(credentials.token()).await?;

View File

@ -2,9 +2,7 @@ use clap::Parser;
#[derive(Parser, Debug, Clone)]
#[clap(version,
name = "ffpapi",
version = "0.3.0",
about = "ffplayout REST API",
about = "REST API for ffplayout",
long_about = None)]
pub struct Args {
#[clap(short, long, help = "Listen on IP:PORT, like: 127.0.0.1:8080")]

View File

@ -4,7 +4,7 @@ use chrono::{Duration, Utc};
use jsonwebtoken::{self, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use crate::api::utils::GlobalSettings;
use crate::utils::GlobalSettings;
// Token lifetime
const JWT_EXPIRATION_DAYS: i64 = 7;

View File

@ -7,7 +7,7 @@ use reqwest::{
use serde::{Deserialize, Serialize};
use simplelog::*;
use crate::api::{errors::ServiceError, utils::playout_config};
use crate::utils::{errors::ServiceError, playout_config};
#[derive(Debug, Deserialize, Serialize, Clone)]
struct RpcObj<T> {

View File

@ -4,8 +4,8 @@ use std::{fs, path::PathBuf};
use simplelog::*;
use crate::api::{errors::ServiceError, utils::playout_config};
use crate::utils::file_extension;
use crate::utils::{errors::ServiceError, playout_config};
use ffplayout_lib::utils::file_extension;
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct PathObject {

View File

@ -7,10 +7,10 @@ use rand::{distributions::Alphanumeric, Rng};
use simplelog::*;
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool};
use crate::api::utils::GlobalSettings;
use crate::api::{
use crate::utils::{
db_path,
models::{Settings, TextPreset, User},
utils::db_path,
GlobalSettings,
};
#[derive(Debug, sqlx::FromRow)]

View File

@ -4,13 +4,23 @@ use faccess::PathExt;
use once_cell::sync::OnceCell;
use simplelog::*;
use crate::api::{
pub mod args_parse;
pub mod auth;
pub mod control;
pub mod errors;
pub mod files;
pub mod handles;
pub mod models;
pub mod playlist;
pub mod routes;
use crate::utils::{
args_parse::Args,
errors::ServiceError,
handles::{db_add_user, db_get_settings, db_global, db_init},
models::{Settings, User},
};
use crate::utils::PlayoutConfig;
use ffplayout_lib::utils::PlayoutConfig;
#[derive(PartialEq, Clone)]
pub enum Role {

View File

@ -6,8 +6,8 @@ use std::{
use simplelog::*;
use crate::api::{errors::ServiceError, utils::playout_config};
use crate::utils::{generate_playlist as playlist_generator, JsonPlaylist};
use crate::utils::{errors::ServiceError, playout_config};
use ffplayout_lib::utils::{generate_playlist as playlist_generator, JsonPlaylist};
fn json_reader(path: &PathBuf) -> Result<JsonPlaylist, Error> {
let f = File::options().read(true).write(false).open(&path)?;

View File

@ -9,8 +9,7 @@ use argon2::{
use serde::Serialize;
use simplelog::*;
use crate::{
api::{
use crate::utils::{
auth::{create_jwt, Claims},
control::{control_state, media_info, send_message},
errors::ServiceError,
@ -21,10 +20,9 @@ use crate::{
},
models::{LoginUser, Settings, TextPreset, User},
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
utils::{read_playout_config, Role},
},
utils::{JsonPlaylist, PlayoutConfig},
read_playout_config, Role,
};
use ffplayout_lib::utils::{JsonPlaylist, PlayoutConfig};
#[derive(Serialize)]
struct ResponseObj<T> {

View File

@ -0,0 +1,79 @@
[package]
name = "ffplayout-engine"
description = "24/7 playout based on rust and ffmpeg"
license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md"
version = "0.9.9"
edition = "2021"
[dependencies]
ffplayout-lib = { path = "../lib" }
chrono = { git = "https://github.com/sbrocket/chrono", branch = "parse-error-kind-public" }
clap = { version = "3.2", features = ["derive"] }
crossbeam-channel = "0.5"
faccess = "0.2"
ffprobe = "0.3"
file-rotate = { git = "https://github.com/Ploppz/file-rotate.git", branch = "timestamp-parse-fix" }
futures = "0.3"
jsonrpc-http-server = "18.0"
lettre = "0.10.0-rc.7"
log = "0.4"
notify = "4.0"
rand = "0.8"
regex = "1"
reqwest = { version = "0.11", features = ["blocking", "json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
shlex = "1.1"
simplelog = { version = "^0.12", features = ["paris"] }
time = { version = "0.3", features = ["formatting", "macros"] }
walkdir = "2"
zeromq = { git = "https://github.com/zeromq/zmq.rs.git", default-features = false, features = [
"async-std-runtime",
"tcp-transport"
] }
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
[[bin]]
name = "ffplayout"
path = "src/main.rs"
# DEBIAN DEB PACKAGE
[package.metadata.deb]
name = "ffplayout-engine"
priority = "optional"
section = "net"
license-file = ["../LICENSE", "0"]
depends = ""
suggests = "ffmpeg"
copyright = "Copyright (c) 2022, Jonathan Baecker. All rights reserved."
conf-files = ["/etc/ffplayout/ffplayout.yml"]
assets = [
[
"../target/x86_64-unknown-linux-musl/release/ffplayout",
"/usr/bin/ffplayout",
"755"
],
["../assets/ffplayout.yml", "/etc/ffplayout/ffplayout.yml", "644"],
["../assets/logo.png", "/usr/share/ffplayout/logo.png", "644"],
["../README.md", "/usr/share/doc/ffplayout/README", "644"],
]
maintainer-scripts = "debian/"
systemd-units = { enable = false, unit-scripts = "unit" }
# REHL RPM PACKAGE
[package.metadata.generate-rpm]
name = "ffplayout-engine"
license = "GPL-3.0"
assets = [
{ source = "../target/x86_64-unknown-linux-musl/release/ffplayout", dest = "/usr/bin/ffplayout", mode = "755" },
{ source = "../assets/ffplayout.yml", dest = "/etc/ffplayout/ffplayout.yml", mode = "644", config = true },
{ source = "unit/ffplayout.service", dest = "/lib/systemd/system/ffplayout.service", mode = "644" },
{ source = "../README.md", dest = "/usr/share/doc/ffplayout/README", mode = "644", doc = true },
{ source = "../LICENSE", dest = "/usr/share/doc/ffplayout/LICENSE", mode = "644" },
{ source = "../assets/logo.png", dest = "/usr/share/ffplayout/logo.png", mode = "644" },
]

View File

@ -0,0 +1,2 @@
**ffplayout-engine**
================

View File

@ -0,0 +1,76 @@
use std::{
path::Path,
sync::{
atomic::{AtomicBool, Ordering},
mpsc::channel,
{Arc, Mutex},
},
thread::sleep,
time::Duration,
};
use notify::{
DebouncedEvent::{Create, Remove, Rename},
{watcher, RecursiveMode, Watcher},
};
use simplelog::*;
use ffplayout_lib::utils::{Media, PlayoutConfig};
/// Create a watcher, which monitor file changes.
/// When a change is register, update the current file list.
/// This makes it possible, to play infinitely and and always new files to it.
pub fn watchman(
config: PlayoutConfig,
is_terminated: Arc<AtomicBool>,
sources: Arc<Mutex<Vec<Media>>>,
) {
let (tx, rx) = channel();
let path = config.storage.path;
if !Path::new(&path).exists() {
error!("Folder path not exists: '{path}'");
panic!("Folder path not exists: '{path}'");
}
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
watcher.watch(path, RecursiveMode::Recursive).unwrap();
while !is_terminated.load(Ordering::SeqCst) {
if let Ok(res) = rx.try_recv() {
match res {
Create(new_path) => {
let index = sources.lock().unwrap().len();
let media = Media::new(index, new_path.display().to_string(), false);
sources.lock().unwrap().push(media);
info!("Create new file: <b><magenta>{new_path:?}</></b>");
}
Remove(old_path) => {
sources
.lock()
.unwrap()
.retain(|x| x.source != old_path.display().to_string());
info!("Remove file: <b><magenta>{old_path:?}</></b>");
}
Rename(old_path, new_path) => {
let index = sources
.lock()
.unwrap()
.iter()
.position(|x| *x.source == old_path.display().to_string())
.unwrap();
let media = Media::new(index, new_path.display().to_string(), false);
sources.lock().unwrap()[index] = media;
info!("Rename file: <b><magenta>{old_path:?}</></b> to <b><magenta>{new_path:?}</></b>");
}
_ => (),
}
}
sleep(Duration::from_secs(5));
}
}

View File

@ -8,9 +8,9 @@ use std::{
use crossbeam_channel::Sender;
use simplelog::*;
use crate::filter::ingest_filter::filter_cmd;
use crate::utils::{format_log_line, Ingest, PlayoutConfig, ProcessControl};
use crate::vec_strings;
use ffplayout_lib::filter::ingest_filter::filter_cmd;
use ffplayout_lib::utils::{format_log_line, Ingest, PlayoutConfig, ProcessControl};
use ffplayout_lib::vec_strings;
pub fn log_line(line: String, level: &str) {
if line.contains("[info]") && level.to_lowercase() == "info" {

View File

@ -9,16 +9,18 @@ use std::{
use simplelog::*;
use crate::utils::{Media, PlayoutConfig, PlayoutStatus};
use ffplayout_lib::utils::{Media, PlayoutConfig, PlayoutStatus};
pub mod folder;
pub mod ingest;
pub mod playlist;
pub use folder::{watchman, FolderSource};
pub use folder::watchman;
pub use ingest::ingest_server;
pub use playlist::CurrentProgram;
use ffplayout_lib::utils::folder::FolderSource;
/// Create a source iterator from playlist, or from folder.
pub fn source_generator(
config: PlayoutConfig,

View File

@ -10,7 +10,7 @@ use std::{
use serde_json::json;
use simplelog::*;
use crate::utils::{
use ffplayout_lib::utils::{
check_sync, gen_dummy, get_delta, get_sec, is_close, is_remote, json_serializer::read_json,
modified_time, seek_and_length, valid_source, Media, PlayoutConfig, PlayoutStatus, DUMMY_LEN,
};

View File

@ -10,13 +10,23 @@ use serde::{Deserialize, Serialize};
use serde_json::json;
use simplelog::*;
use ffplayout_engine::{
pub mod input;
pub mod output;
pub mod rpc;
// #[cfg(test)]
// mod tests;
pub mod utils;
use utils::{arg_parse::get_args, get_config};
use crate::{
output::{player, write_hls},
rpc::json_rpc_server,
utils::{
generate_playlist, get_args, init_logging, send_mail, validate_ffmpeg, PlayerControl,
PlayoutConfig, PlayoutStatus, ProcessControl,
},
};
use ffplayout_lib::utils::{
generate_playlist, init_logging, send_mail, validate_ffmpeg, PlayerControl, PlayoutStatus,
ProcessControl,
};
#[derive(Serialize, Deserialize)]
@ -59,7 +69,7 @@ fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) {
fn main() {
let args = get_args();
let config = PlayoutConfig::new(Some(args));
let config = get_config(args);
let config_clone = config.clone();
let play_control = PlayerControl::new();
let playout_stat = PlayoutStatus::new();

View File

@ -2,9 +2,9 @@ use std::process::{self, Command, Stdio};
use simplelog::*;
use crate::filter::v_drawtext;
use crate::utils::{Media, PlayoutConfig};
use crate::vec_strings;
use ffplayout_lib::filter::v_drawtext;
use ffplayout_lib::utils::{Media, PlayoutConfig};
use ffplayout_lib::vec_strings;
/// Desktop Output
///

View File

@ -27,13 +27,13 @@ use std::{
use simplelog::*;
use crate::filter::ingest_filter::filter_cmd;
use crate::input::{ingest::log_line, source_generator};
use crate::utils::{
use ffplayout_lib::filter::ingest_filter::filter_cmd;
use ffplayout_lib::utils::{
prepare_output_cmd, sec_to_time, stderr_reader, Decoder, Ingest, PlayerControl, PlayoutConfig,
PlayoutStatus, ProcessControl,
};
use crate::vec_strings;
use ffplayout_lib::vec_strings;
/// Ingest Server for HLS
fn ingest_to_hls_server(

View File

@ -16,11 +16,11 @@ mod stream;
pub use hls::write_hls;
use crate::input::{ingest_server, source_generator};
use crate::utils::{
use ffplayout_lib::utils::{
sec_to_time, stderr_reader, Decoder, PlayerControl, PlayoutConfig, PlayoutStatus,
ProcessControl,
};
use crate::vec_strings;
use ffplayout_lib::vec_strings;
/// Player
///

View File

@ -2,9 +2,9 @@ use std::process::{self, Command, Stdio};
use simplelog::*;
use crate::filter::v_drawtext;
use crate::utils::{prepare_output_cmd, Media, PlayoutConfig};
use crate::vec_strings;
use ffplayout_lib::filter::v_drawtext;
use ffplayout_lib::utils::{prepare_output_cmd, Media, PlayoutConfig};
use ffplayout_lib::vec_strings;
/// Streaming Output
///

View File

@ -11,7 +11,7 @@ use jsonrpc_http_server::{
use serde_json::{json, Map};
use simplelog::*;
use crate::utils::{
use ffplayout_lib::utils::{
get_delta, get_filter_from_json, get_sec, sec_to_time, write_status, Media, PlayerControl,
PlayoutConfig, PlayoutStatus, ProcessControl,
};

View File

@ -3,12 +3,10 @@ use std::{
time::Duration,
};
mod utils;
#[cfg(test)]
use crate::output::player;
#[cfg(test)]
use crate::utils::*;
use ffplayout_lib::utils::*;
#[cfg(test)]
use simplelog::*;

View File

@ -0,0 +1,64 @@
use std::path::Path;
pub mod arg_parse;
pub use arg_parse::Args;
use ffplayout_lib::utils::{time_to_sec, PlayoutConfig};
pub fn get_config(args: Args) -> PlayoutConfig {
let mut config = PlayoutConfig::new(args.config);
if let Some(gen) = args.generate {
config.general.generate = Some(gen);
}
if let Some(log_path) = args.log {
if Path::new(&log_path).is_dir() {
config.logging.log_to_file = true;
}
config.logging.log_path = log_path;
}
if let Some(playlist) = args.playlist {
config.playlist.path = playlist;
}
if let Some(mode) = args.play_mode {
config.processing.mode = mode;
}
if let Some(folder) = args.folder {
config.storage.path = folder;
config.processing.mode = "folder".into();
}
if let Some(start) = args.start {
config.playlist.day_start = start.clone();
config.playlist.start_sec = Some(time_to_sec(&start));
}
if let Some(length) = args.length {
config.playlist.length = length.clone();
if length.contains(':') {
config.playlist.length_sec = Some(time_to_sec(&length));
} else {
config.playlist.length_sec = Some(86400.0);
}
}
if args.infinit {
config.playlist.infinit = args.infinit;
}
if let Some(output) = args.output {
config.out.mode = output;
}
if let Some(volume) = args.volume {
config.processing.volume = volume;
}
config
}
// Read command line arguments, and override the config with them.

34
lib/Cargo.toml Normal file
View File

@ -0,0 +1,34 @@
[package]
name = "ffplayout-lib"
description = "Library for ffplayout"
license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md"
version = "0.9.9"
edition = "2021"
[dependencies]
chrono = { git = "https://github.com/sbrocket/chrono", branch = "parse-error-kind-public" }
crossbeam-channel = "0.5"
faccess = "0.2"
ffprobe = "0.3"
file-rotate = { git = "https://github.com/Ploppz/file-rotate.git", branch = "timestamp-parse-fix" }
futures = "0.3"
jsonrpc-http-server = "18.0"
lettre = "0.10.0-rc.7"
log = "0.4"
notify = "4.0"
once_cell = "1.10"
rand = "0.8"
regex = "1"
reqwest = { version = "0.11", features = ["blocking", "json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
shlex = "1.1"
simplelog = { version = "^0.12", features = ["paris"] }
time = { version = "0.3", features = ["formatting", "macros"] }
walkdir = "2"
[target.x86_64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }

View File

@ -1,12 +1,9 @@
extern crate log;
extern crate simplelog;
pub mod api;
pub mod filter;
pub mod input;
pub mod macros;
pub mod output;
pub mod rpc;
pub mod utils;
#[cfg(test)]
mod tests;
pub mod utils;

View File

@ -8,7 +8,7 @@ use std::{
use serde::{Deserialize, Serialize};
use shlex::split;
use crate::utils::{free_tcp_socket, time_to_sec, Args};
use crate::utils::{free_tcp_socket, time_to_sec};
use crate::vec_strings;
/// Global Config
@ -168,10 +168,10 @@ pub struct Out {
impl PlayoutConfig {
/// Read config from YAML file, and set some extra config values.
pub fn new(args: Option<Args>) -> Self {
pub fn new(cfg_path: Option<String>) -> Self {
let mut config_path = PathBuf::from("/etc/ffplayout/ffplayout.yml");
if let Some(cfg) = args.clone().and_then(|a| a.config) {
if let Some(cfg) = cfg_path {
config_path = PathBuf::from(cfg);
}
@ -259,61 +259,6 @@ impl PlayoutConfig {
config.text.node_pos = None;
}
// Read command line arguments, and override the config with them.
if let Some(arg) = args {
if let Some(gen) = arg.generate {
config.general.generate = Some(gen);
}
if let Some(log_path) = arg.log {
if Path::new(&log_path).is_dir() {
config.logging.log_to_file = true;
}
config.logging.log_path = log_path;
}
if let Some(playlist) = arg.playlist {
config.playlist.path = playlist;
}
if let Some(mode) = arg.play_mode {
config.processing.mode = mode;
}
if let Some(folder) = arg.folder {
config.storage.path = folder;
config.processing.mode = "folder".into();
}
if let Some(start) = arg.start {
config.playlist.day_start = start.clone();
config.playlist.start_sec = Some(time_to_sec(&start));
}
if let Some(length) = arg.length {
config.playlist.length = length.clone();
if length.contains(':') {
config.playlist.length_sec = Some(time_to_sec(&length));
} else {
config.playlist.length_sec = Some(86400.0);
}
}
if arg.infinit {
config.playlist.infinit = arg.infinit;
}
if let Some(output) = arg.output {
config.out.mode = output;
}
if let Some(volume) = arg.volume {
config.processing.volume = volume;
}
}
config
}
}

View File

@ -2,18 +2,11 @@ use std::{
path::Path,
process::exit,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
mpsc::channel,
atomic::{AtomicUsize, Ordering},
{Arc, Mutex},
},
thread::sleep,
time::Duration,
};
use notify::{
DebouncedEvent::{Create, Remove, Rename},
{watcher, RecursiveMode, Watcher},
};
use rand::{seq::SliceRandom, thread_rng};
use simplelog::*;
use walkdir::WalkDir;
@ -153,61 +146,3 @@ impl Iterator for FolderSource {
}
}
}
/// Create a watcher, which monitor file changes.
/// When a change is register, update the current file list.
/// This makes it possible, to play infinitely and and always new files to it.
pub fn watchman(
config: PlayoutConfig,
is_terminated: Arc<AtomicBool>,
sources: Arc<Mutex<Vec<Media>>>,
) {
let (tx, rx) = channel();
let path = config.storage.path;
if !Path::new(&path).exists() {
error!("Folder path not exists: '{path}'");
panic!("Folder path not exists: '{path}'");
}
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
watcher.watch(path, RecursiveMode::Recursive).unwrap();
while !is_terminated.load(Ordering::SeqCst) {
if let Ok(res) = rx.try_recv() {
match res {
Create(new_path) => {
let index = sources.lock().unwrap().len();
let media = Media::new(index, new_path.display().to_string(), false);
sources.lock().unwrap().push(media);
info!("Create new file: <b><magenta>{new_path:?}</></b>");
}
Remove(old_path) => {
sources
.lock()
.unwrap()
.retain(|x| x.source != old_path.display().to_string());
info!("Remove file: <b><magenta>{old_path:?}</></b>");
}
Rename(old_path, new_path) => {
let index = sources
.lock()
.unwrap()
.iter()
.position(|x| *x.source == old_path.display().to_string())
.unwrap();
let media = Media::new(index, new_path.display().to_string(), false);
sources.lock().unwrap()[index] = media;
info!("Rename file: <b><magenta>{old_path:?}</></b> to <b><magenta>{new_path:?}</></b>");
}
_ => (),
}
}
sleep(Duration::from_secs(5));
}
}

View File

@ -17,7 +17,7 @@ use std::{
use chrono::{Duration, NaiveDate};
use simplelog::*;
use crate::input::FolderSource;
use super::folder::FolderSource;
use crate::utils::{json_serializer::JsonPlaylist, time_to_sec, Media, PlayoutConfig};
/// Generate a vector with dates, from given range.

View File

@ -18,15 +18,14 @@ use serde::{Deserialize, Serialize};
use serde_json::json;
use simplelog::*;
mod arg_parse;
pub mod config;
pub mod controller;
pub mod folder;
mod generator;
pub mod json_serializer;
mod json_validate;
mod logging;
pub use arg_parse::{get_args, Args};
pub use config::{self as playout_config, PlayoutConfig};
pub use controller::{PlayerControl, PlayoutStatus, ProcessControl, ProcessUnit::*};
pub use generator::generate_playlist;

View File

@ -1,10 +0,0 @@
pub mod args_parse;
pub mod auth;
pub mod control;
pub mod errors;
pub mod files;
pub mod handles;
pub mod models;
pub mod playlist;
pub mod routes;
pub mod utils;