finish program init

This commit is contained in:
jb-alvarado 2024-06-14 10:37:49 +02:00
parent 9714c5dcaf
commit 4d3df70a2e
16 changed files with 145 additions and 175 deletions

33
Cargo.lock generated
View File

@ -1100,7 +1100,6 @@ dependencies = [
"serde_json", "serde_json",
"shlex", "shlex",
"signal-child", "signal-child",
"simplelog",
"sqlx", "sqlx",
"static-files", "static-files",
"sysinfo", "sysinfo",
@ -2218,15 +2217,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.35.0" version = "0.35.0"
@ -2972,18 +2962,6 @@ dependencies = [
"time", "time",
] ]
[[package]]
name = "simplelog"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
dependencies = [
"log",
"paris",
"termcolor",
"time",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -3435,15 +3413,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "tests" name = "tests"
version = "0.24.0" version = "0.24.0"
@ -3497,9 +3466,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa", "itoa",
"libc",
"num-conv", "num-conv",
"num_threads",
"powerfmt", "powerfmt",
"serde", "serde",
"time-core", "time-core",

View File

@ -50,7 +50,6 @@ rpassword = "7.2"
sanitize-filename = "0.5" sanitize-filename = "0.5"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
simplelog = { version = "0.12", features = ["paris"] }
shlex = "1.1" shlex = "1.1"
static-files = "0.2" static-files = "0.2"
sysinfo ={ version = "0.30", features = ["linux-netdevs"] } sysinfo ={ version = "0.30", features = ["linux-netdevs"] }

View File

@ -1,101 +0,0 @@
use log::{LevelFilter, Log, Metadata, Record};
use simplelog::*;
use std::fs::File;
pub struct LogMailer {
level: LevelFilter,
pub config: Config,
}
impl LogMailer {
pub fn new(log_level: LevelFilter, config: Config) -> Box<LogMailer> {
Box::new(LogMailer {
level: log_level,
config,
})
}
}
impl Log for LogMailer {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &Record<'_>) {
if self.enabled(record.metadata()) {
let _rec = record.args().to_string();
println!("{record:?}");
println!("target: {:?}", record.target());
}
}
fn flush(&self) {}
}
impl SharedLogger for LogMailer {
fn level(&self) -> LevelFilter {
self.level
}
fn config(&self) -> Option<&Config> {
Some(&self.config)
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
struct Log2 {
logger: Box<WriteLogger<File>>,
}
impl Log2 {
fn new() -> Self {
let log_file = File::create("log_file.log").expect("Failed to create log file");
let config = ConfigBuilder::new()
.set_time_format_custom(format_description!(
"[[[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:5]]"
))
.build();
let logger = WriteLogger::new(LevelFilter::Debug, config, log_file);
Log2 { logger }
}
fn debug(&self, message: &str) {
self.logger.log(
&Record::builder()
.args(format_args!("{}", message))
.level(Level::Debug)
.build(),
);
}
}
fn main() {
let log2 = Log2::new();
log2.debug("Debug-Message in Logger 2");
// std::thread::spawn(move || {
// log2.debug("Error-Message in Logger 2");
// });
CombinedLogger::init(vec![
TermLogger::new(
LevelFilter::Debug,
Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
),
LogMailer::new(LevelFilter::Info, Config::default()),
])
.unwrap();
info!("Info in Logger 1");
warn!("Warning in Logger 1");
}

View File

@ -3,8 +3,8 @@ use argon2::{
Argon2, PasswordHasher, Argon2, PasswordHasher,
}; };
use log::*;
use rand::{distributions::Alphanumeric, Rng}; use rand::{distributions::Alphanumeric, Rng};
use simplelog::*;
use sqlx::{sqlite::SqliteQueryResult, Pool, Sqlite}; use sqlx::{sqlite::SqliteQueryResult, Pool, Sqlite};
use tokio::task; use tokio::task;
@ -40,11 +40,28 @@ pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<&'static str, Box<dyn std
} }
pub async fn select_global(conn: &Pool<Sqlite>) -> Result<GlobalSettings, sqlx::Error> { pub async fn select_global(conn: &Pool<Sqlite>) -> Result<GlobalSettings, sqlx::Error> {
let query = "SELECT secret, hls_path, playlist_path, storage_path, logging_path FROM global WHERE id = 1"; let query = "SELECT id, secret, hls_path, logging_path, playlist_path, storage_path, shared_storage FROM global WHERE id = 1";
sqlx::query_as(query).fetch_one(conn).await sqlx::query_as(query).fetch_one(conn).await
} }
pub async fn update_global(
conn: &Pool<Sqlite>,
global: GlobalSettings,
) -> Result<SqliteQueryResult, sqlx::Error> {
let query = "UPDATE global SET hls_path = $2, playlist_path = $3, storage_path = $4, logging_path = $5, shared_storage = $6 WHERE id = 1";
sqlx::query(query)
.bind(global.id)
.bind(global.hls_path)
.bind(global.playlist_path)
.bind(global.storage_path)
.bind(global.logging_path)
.bind(global.shared_storage)
.execute(conn)
.await
}
pub async fn select_channel(conn: &Pool<Sqlite>, id: &i32) -> Result<Channel, sqlx::Error> { pub async fn select_channel(conn: &Pool<Sqlite>, id: &i32) -> Result<Channel, sqlx::Error> {
let query = "SELECT * FROM channels WHERE id = $1"; let query = "SELECT * FROM channels WHERE id = $1";
let mut result: Channel = sqlx::query_as(query).bind(id).fetch_one(conn).await?; let mut result: Channel = sqlx::query_as(query).bind(id).fetch_one(conn).await?;

View File

@ -13,11 +13,13 @@ use crate::utils::config::PlayoutConfig;
#[derive(Debug, Deserialize, Serialize, sqlx::FromRow)] #[derive(Debug, Deserialize, Serialize, sqlx::FromRow)]
pub struct GlobalSettings { pub struct GlobalSettings {
pub id: i32,
pub secret: Option<String>, pub secret: Option<String>,
pub hls_path: String, pub hls_path: String,
pub logging_path: String,
pub playlist_path: String, pub playlist_path: String,
pub storage_path: String, pub storage_path: String,
pub logging_path: String, pub shared_storage: bool,
} }
impl GlobalSettings { impl GlobalSettings {
@ -27,11 +29,13 @@ impl GlobalSettings {
match global_settings.await { match global_settings.await {
Ok(g) => g, Ok(g) => g,
Err(_) => GlobalSettings { Err(_) => GlobalSettings {
id: 0,
secret: None, secret: None,
hls_path: String::new(), hls_path: String::new(),
logging_path: String::new(),
playlist_path: String::new(), playlist_path: String::new(),
storage_path: String::new(), storage_path: String::new(),
logging_path: String::new(), shared_storage: false,
}, },
} }
} }

View File

@ -79,13 +79,13 @@ async fn main() -> std::io::Result<()> {
panic!("{e}"); panic!("{e}");
}; };
init_globales(&pool).await;
init_logging(mail_queues.clone())?;
if let Err(c) = run_args(&pool).await { if let Err(c) = run_args(&pool).await {
exit(c); exit(c);
} }
init_globales(&pool).await;
init_logging(mail_queues.clone())?;
let channel_controllers = Arc::new(Mutex::new(ChannelController::new())); let channel_controllers = Arc::new(Mutex::new(ChannelController::new()));
if let Some(conn) = &ARGS.listen { if let Some(conn) = &ARGS.listen {

View File

@ -4,8 +4,8 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use log::*;
use regex::Regex; use regex::Regex;
use simplelog::*;
mod custom; mod custom;
pub mod v_drawtext; pub mod v_drawtext;

View File

@ -9,13 +9,13 @@ use std::{
time::Duration, time::Duration,
}; };
use log::*;
use notify::{ use notify::{
event::{CreateKind, ModifyKind, RemoveKind, RenameMode}, event::{CreateKind, ModifyKind, RemoveKind, RenameMode},
EventKind::{Create, Modify, Remove}, EventKind::{Create, Modify, Remove},
RecursiveMode, Watcher, RecursiveMode, Watcher,
}; };
use notify_debouncer_full::new_debouncer; use notify_debouncer_full::new_debouncer;
use simplelog::*;
use crate::player::utils::{include_file_extension, Media}; use crate::player::utils::{include_file_extension, Media};
use crate::utils::config::PlayoutConfig; use crate::utils::config::PlayoutConfig;

View File

@ -6,7 +6,7 @@ use std::{
}; };
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use simplelog::*; use log::*;
use crate::utils::{ use crate::utils::{
config::{PlayoutConfig, FFMPEG_IGNORE_ERRORS, FFMPEG_UNRECOVERABLE_ERRORS}, config::{PlayoutConfig, FFMPEG_IGNORE_ERRORS, FFMPEG_UNRECOVERABLE_ERRORS},

View File

@ -4,8 +4,8 @@ use std::sync::{
}; };
use lexical_sort::natural_lexical_cmp; use lexical_sort::natural_lexical_cmp;
use log::*;
use rand::{seq::SliceRandom, thread_rng}; use rand::{seq::SliceRandom, thread_rng};
use simplelog::*;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::player::{ use crate::player::{

View File

@ -6,7 +6,7 @@ use std::{
thread, thread,
}; };
use simplelog::*; use log::*;
use crate::player::utils::{ use crate::player::utils::{
get_date, is_remote, json_validate::validate_playlist, modified_time, time_from_header, Media, get_date, is_remote, json_validate::validate_playlist, modified_time, time_from_header, Media,

View File

@ -12,12 +12,12 @@ use std::{
use chrono::{prelude::*, TimeDelta}; use chrono::{prelude::*, TimeDelta};
use ffprobe::{ffprobe, Stream as FFStream}; use ffprobe::{ffprobe, Stream as FFStream};
use log::*;
use rand::prelude::*; use rand::prelude::*;
use regex::Regex; use regex::Regex;
use reqwest::header; use reqwest::header;
use serde::{de::Deserializer, Deserialize, Serialize}; use serde::{de::Deserializer, Deserialize, Serialize};
use serde_json::{json, Map, Value}; use serde_json::{json, Map, Value};
use simplelog::*;
pub mod folder; pub mod folder;
pub mod import; pub mod import;

View File

@ -5,11 +5,13 @@ use std::{
}; };
use clap::Parser; use clap::Parser;
use log::*;
use rpassword::read_password; use rpassword::read_password;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use crate::db::{db_pool, handles::insert_user, models::User}; use crate::db::{
handles::{self, insert_user},
models::{GlobalSettings, User},
};
use crate::utils::config::PlayoutConfig; use crate::utils::config::PlayoutConfig;
use crate::ARGS; use crate::ARGS;
@ -62,7 +64,7 @@ pub struct Args {
#[clap( #[clap(
long, long,
env, env,
help = "Override logging level: trace, debug, info, warn, error" help = "Override logging level: trace, debug, println, warn, eprintln"
)] )]
pub log_level: Option<String>, pub log_level: Option<String>,
@ -81,6 +83,9 @@ pub struct Args {
#[clap(long, env, help = "Storage root path")] #[clap(long, env, help = "Storage root path")]
pub storage_path: Option<PathBuf>, pub storage_path: Option<PathBuf>,
#[clap(long, env, help = "Share storage across channels")]
pub shared_storage: bool,
#[clap(short, long, help = "domain name for initialization")] #[clap(short, long, help = "domain name for initialization")]
pub domain: Option<String>, pub domain: Option<String>,
@ -102,7 +107,18 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
let mut mail = String::new(); let mut mail = String::new();
let mut storage = String::new(); let mut storage = String::new();
let mut playlist = String::new(); let mut playlist = String::new();
let mut logging = String::new();
let mut hls = String::new(); let mut hls = String::new();
let mut shared_store = String::new();
let mut global = GlobalSettings {
id: 0,
secret: None,
hls_path: String::new(),
playlist_path: String::new(),
storage_path: String::new(),
logging_path: String::new(),
shared_storage: false,
};
print!("Global admin: "); print!("Global admin: ");
stdout().flush().unwrap(); stdout().flush().unwrap();
@ -136,17 +152,86 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
.expect("Did not enter a correct path?"); .expect("Did not enter a correct path?");
if storage.trim().is_empty() { if storage.trim().is_empty() {
args.storage_path = Some(PathBuf::from("/var/lib/ffplayout/tv-media")); global.storage_path = "/var/lib/ffplayout/tv-media".to_string();
} else { } else {
args.storage_path = Some(PathBuf::from(storage.trim())); global.storage_path = storage
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string();
} }
println!("{args:?}"); print!("Playlist path [/var/lib/ffplayout/playlists]: ");
stdout().flush().unwrap();
stdin()
.read_line(&mut playlist)
.expect("Did not enter a correct path?");
if playlist.trim().is_empty() {
global.playlist_path = "/var/lib/ffplayout/playlists".to_string();
} else {
global.playlist_path = playlist
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string();
}
print!("HLS path [/usr/share/ffplayout/public]: ");
stdout().flush().unwrap();
stdin()
.read_line(&mut hls)
.expect("Did not enter a correct path?");
if hls.trim().is_empty() {
global.hls_path = "/usr/share/ffplayout/public".to_string();
} else {
global.hls_path = hls
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string();
}
print!("Logging path [/var/log/ffplayout]: ");
stdout().flush().unwrap();
stdin()
.read_line(&mut logging)
.expect("Did not enter a correct path?");
if logging.trim().is_empty() {
global.logging_path = "/var/log/ffplayout".to_string();
} else {
global.logging_path = logging
.trim()
.trim_matches(|c| c == '"' || c == '\'')
.to_string();
}
print!("Shared storage [Y/n]: ");
stdout().flush().unwrap();
stdin()
.read_line(&mut shared_store)
.expect("Did not enter a correct path?");
if shared_store.trim().to_lowercase().starts_with('y') {
global.shared_storage = true;
} else {
global.shared_storage = false;
}
if let Err(e) = handles::update_global(&pool, global).await {
eprintln!("{e}");
return Err(1);
};
println!("Set global settings...");
} }
if let Some(username) = args.username { if let Some(username) = args.username {
if args.mail.is_none() || args.password.is_none() { if args.mail.is_none() || args.password.is_none() {
error!("Mail/password missing!"); eprintln!("Mail/password missing!");
return Err(1); return Err(1);
} }
@ -160,21 +245,12 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
token: None, token: None,
}; };
match db_pool().await { if let Err(e) = insert_user(&pool, user).await {
Ok(conn) => { eprintln!("{e}");
if let Err(e) = insert_user(&conn, user).await {
error!("{e}");
return Err(1); return Err(1);
}; };
}
Err(e) => { println!("Create global admin user \"{username}\" done...");
error!("{e}");
return Err(1);
}
};
info!("Create admin user \"{username}\" done...");
return Err(0); return Err(0);
} }
@ -182,11 +258,11 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
if let Some(id) = ARGS.dump_config { if let Some(id) = ARGS.dump_config {
match PlayoutConfig::dump(&pool, id).await { match PlayoutConfig::dump(&pool, id).await {
Ok(_) => { Ok(_) => {
info!("Dump config to: ffplayout_{id}.toml"); println!("Dump config to: ffplayout_{id}.toml");
exit(0); exit(0);
} }
Err(e) => { Err(e) => {
error!("Dump config: {e}"); eprintln!("Dump config: {e}");
exit(1); exit(1);
} }
@ -196,11 +272,11 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
if let Some(import) = &ARGS.import_config { if let Some(import) = &ARGS.import_config {
match PlayoutConfig::import(&pool, import.clone()).await { match PlayoutConfig::import(&pool, import.clone()).await {
Ok(_) => { Ok(_) => {
info!("Import config done..."); println!("Import config done...");
exit(0); exit(0);
} }
Err(e) => { Err(e) => {
error!("{e}"); eprintln!("{e}");
exit(1); exit(1);
} }

View File

@ -12,8 +12,8 @@ use std::{
use chrono::Timelike; use chrono::Timelike;
use lexical_sort::{natural_lexical_cmp, StringSort}; use lexical_sort::{natural_lexical_cmp, StringSort};
use log::*;
use rand::{seq::SliceRandom, thread_rng, Rng}; use rand::{seq::SliceRandom, thread_rng, Rng};
use simplelog::*;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::player::{ use crate::player::{

View File

@ -21,12 +21,17 @@ use paris::formatter::colorize_string;
use super::ARGS; use super::ARGS;
use crate::db::models::GlobalSettings;
use crate::utils::{config::Mail, errors::ProcessError, round_to_nearest_ten}; use crate::utils::{config::Mail, errors::ProcessError, round_to_nearest_ten};
#[derive(Debug)] #[derive(Debug)]
pub struct Target; pub struct Target;
impl Target { impl Target {
pub fn all() -> &'static str {
"{file,mail,_Default}"
}
pub fn console() -> &'static str { pub fn console() -> &'static str {
"{console}" "{console}"
} }
@ -258,10 +263,12 @@ fn file_formatter(
} }
pub fn log_file_path() -> PathBuf { pub fn log_file_path() -> PathBuf {
let config = GlobalSettings::global();
let mut log_path = ARGS let mut log_path = ARGS
.log_path .log_path
.clone() .clone()
.unwrap_or(PathBuf::from("/var/log/ffplayout")); .unwrap_or(PathBuf::from(&config.logging_path));
if !log_path.is_dir() { if !log_path.is_dir() {
log_path = env::current_dir().unwrap(); log_path = env::current_dir().unwrap();

View File

@ -6,9 +6,10 @@ CREATE TABLE
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
secret TEXT NOT NULL, secret TEXT NOT NULL,
hls_path TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public", hls_path TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
logging_path TEXT NOT NULL DEFAULT "/var/log/ffplayout",
playlist_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists", playlist_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists",
storage_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media", storage_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
logging_path TEXT NOT NULL DEFAULT "/var/log/ffplayout", shared_storage INTEGER NOT NULL DEFAULT 1,
UNIQUE (secret) UNIQUE (secret)
); );