move mail server config to global settings, change add argument to user-set to implement upsert query.
This commit is contained in:
parent
8c6dc956dd
commit
f0ec172564
23
.vscode/settings.json
vendored
23
.vscode/settings.json
vendored
@ -48,28 +48,49 @@
|
|||||||
},
|
},
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"actix",
|
"actix",
|
||||||
|
"aevalsrc",
|
||||||
|
"afade",
|
||||||
|
"apad",
|
||||||
|
"boxborderw",
|
||||||
|
"boxcolor",
|
||||||
"canonicalize",
|
"canonicalize",
|
||||||
|
"cgop",
|
||||||
|
"coeffs",
|
||||||
"ffpengine",
|
"ffpengine",
|
||||||
"flexi",
|
"flexi",
|
||||||
|
"fontcolor",
|
||||||
"fontfile",
|
"fontfile",
|
||||||
|
"fontsize",
|
||||||
"httpauth",
|
"httpauth",
|
||||||
|
"ifnot",
|
||||||
|
"keyint",
|
||||||
"lettre",
|
"lettre",
|
||||||
"libc",
|
"libc",
|
||||||
|
"libx",
|
||||||
|
"libzmq",
|
||||||
"maxrate",
|
"maxrate",
|
||||||
"minrate",
|
"minrate",
|
||||||
|
"muxdelay",
|
||||||
"muxer",
|
"muxer",
|
||||||
|
"muxpreload",
|
||||||
|
"n'vtt",
|
||||||
"neli",
|
"neli",
|
||||||
"nuxt",
|
"nuxt",
|
||||||
"paris",
|
"paris",
|
||||||
"Referer",
|
"Referer",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rsplit",
|
"rsplit",
|
||||||
|
"RTSP",
|
||||||
"rustls",
|
"rustls",
|
||||||
|
"scenecut",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"starttls",
|
"starttls",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tpad",
|
||||||
"unistd",
|
"unistd",
|
||||||
"uuids"
|
"uuids",
|
||||||
|
"webm",
|
||||||
|
"zerolatency"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -1215,7 +1215,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout"
|
name = "ffplayout"
|
||||||
version = "0.24.0-beta5"
|
version = "0.24.0-rc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
@ -1324,9 +1324,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flexi_logger"
|
name = "flexi_logger"
|
||||||
version = "0.29.1"
|
version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4aecae2cdf1c33e1c83896a37cc155e207a6358c915795932c338126e8eb3392"
|
checksum = "a250587a211932896a131f214a4f64c047b826ce072d2018764e5ff5141df8fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"glob",
|
"glob",
|
||||||
@ -3674,7 +3674,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tests"
|
name = "tests"
|
||||||
version = "0.24.0-beta5"
|
version = "0.24.0-rc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-test",
|
"actix-test",
|
||||||
|
@ -5,7 +5,7 @@ resolver = "2"
|
|||||||
[workspace.package]
|
[workspace.package]
|
||||||
description = "24/7 playout based on rust and ffmpeg"
|
description = "24/7 playout based on rust and ffmpeg"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.24.0-beta5"
|
version = "0.24.0-rc1"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/ffplayout/ffplayout"
|
repository = "https://github.com/ffplayout/ffplayout"
|
||||||
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
||||||
|
@ -27,7 +27,7 @@ crossbeam-channel = "0.5"
|
|||||||
derive_more = { version = "1", features = ["display"] }
|
derive_more = { version = "1", features = ["display"] }
|
||||||
faccess = "0.2"
|
faccess = "0.2"
|
||||||
ffprobe = "0.4"
|
ffprobe = "0.4"
|
||||||
flexi_logger = { version = "0.29", features = ["kv", "colors"] }
|
flexi_logger = { version = "=0.29.0", features = ["kv", "colors"] }
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
home = "0.5"
|
home = "0.5"
|
||||||
jsonwebtoken = "9"
|
jsonwebtoken = "9"
|
||||||
|
@ -37,7 +37,7 @@ pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<(), Box<dyn std::error::E
|
|||||||
|
|
||||||
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 =
|
let query =
|
||||||
"SELECT id, secret, logs, playlists, public, storage, shared FROM global WHERE id = 1";
|
"SELECT id, secret, logs, playlists, public, storage, shared, mail_smtp, mail_user, mail_password, mail_starttls FROM global WHERE id = 1";
|
||||||
|
|
||||||
sqlx::query_as(query).fetch_one(conn).await
|
sqlx::query_as(query).fetch_one(conn).await
|
||||||
}
|
}
|
||||||
@ -46,7 +46,9 @@ pub async fn update_global(
|
|||||||
conn: &Pool<Sqlite>,
|
conn: &Pool<Sqlite>,
|
||||||
global: GlobalSettings,
|
global: GlobalSettings,
|
||||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = "UPDATE global SET logs = $2, playlists = $3, public = $4, storage = $5, shared = $6 WHERE id = 1";
|
let query =
|
||||||
|
"UPDATE global SET logs = $2, playlists = $3, public = $4, storage = $5, shared = $6,
|
||||||
|
mail_smtp = $7, mail_user = $8, mail_password = $9, mail_starttls = $10 WHERE id = 1";
|
||||||
|
|
||||||
sqlx::query(query)
|
sqlx::query(query)
|
||||||
.bind(global.id)
|
.bind(global.id)
|
||||||
@ -55,6 +57,10 @@ pub async fn update_global(
|
|||||||
.bind(global.public)
|
.bind(global.public)
|
||||||
.bind(global.storage)
|
.bind(global.storage)
|
||||||
.bind(global.shared)
|
.bind(global.shared)
|
||||||
|
.bind(global.mail_smtp)
|
||||||
|
.bind(global.mail_user)
|
||||||
|
.bind(global.mail_password)
|
||||||
|
.bind(global.mail_starttls)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -212,17 +218,13 @@ pub async fn update_configuration(
|
|||||||
id: i32,
|
id: i32,
|
||||||
config: PlayoutConfig,
|
config: PlayoutConfig,
|
||||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = "UPDATE configurations SET general_stop_threshold = $2, mail_subject = $3, mail_smtp = $4, mail_addr = $5, mail_pass = $6, mail_recipient = $7, mail_starttls = $8, mail_level = $9, mail_interval = $10, logging_ffmpeg_level = $11, logging_ingest_level = $12, logging_detect_silence = $13, logging_ignore = $14, processing_mode = $15, processing_audio_only = $16, processing_copy_audio = $17, processing_copy_video = $18, processing_width = $19, processing_height = $20, processing_aspect = $21, processing_fps = $22, processing_add_logo = $23, processing_logo = $24, processing_logo_scale = $25, processing_logo_opacity = $26, processing_logo_position = $27, processing_audio_tracks = $28, processing_audio_track_index = $29, processing_audio_channels = $30, processing_volume = $31, processing_filter = $32, processing_vtt_enable = $33, processing_vtt_dummy = $34, ingest_enable = $35, ingest_param = $36, ingest_filter = $37, playlist_day_start = $38, playlist_length = $39, playlist_infinit = $40, storage_filler = $41, storage_extensions = $42, storage_shuffle = $43, text_add = $44, text_from_filename = $45, text_font = $46, text_style = $47, text_regex = $48, task_enable = $49, task_path = $50, output_mode = $51, output_param = $52 WHERE id = $1";
|
let query = "UPDATE configurations SET general_stop_threshold = $2, mail_subject = $3, mail_recipient = $4, mail_level = $5, mail_interval = $6, logging_ffmpeg_level = $7, logging_ingest_level = $8, logging_detect_silence = $9, logging_ignore = $10, processing_mode = $11, processing_audio_only = $12, processing_copy_audio = $13, processing_copy_video = $14, processing_width = $15, processing_height = $16, processing_aspect = $17, processing_fps = $18, processing_add_logo = $19, processing_logo = $20, processing_logo_scale = $21, processing_logo_opacity = $22, processing_logo_position = $23, processing_audio_tracks = $24, processing_audio_track_index = $25, processing_audio_channels = $26, processing_volume = $27, processing_filter = $28, processing_vtt_enable = $29, processing_vtt_dummy = $30, ingest_enable = $31, ingest_param = $32, ingest_filter = $33, playlist_day_start = $34, playlist_length = $35, playlist_infinit = $36, storage_filler = $37, storage_extensions = $38, storage_shuffle = $39, text_add = $40, text_from_filename = $41, text_font = $42, text_style = $43, text_regex = $44, task_enable = $45, task_path = $46, output_mode = $47, output_param = $48 WHERE id = $1";
|
||||||
|
|
||||||
sqlx::query(query)
|
sqlx::query(query)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(config.general.stop_threshold)
|
.bind(config.general.stop_threshold)
|
||||||
.bind(config.mail.subject)
|
.bind(config.mail.subject)
|
||||||
.bind(config.mail.smtp_server)
|
|
||||||
.bind(config.mail.sender_addr)
|
|
||||||
.bind(config.mail.sender_pass)
|
|
||||||
.bind(config.mail.recipient)
|
.bind(config.mail.recipient)
|
||||||
.bind(config.mail.starttls)
|
|
||||||
.bind(config.mail.mail_level.as_str())
|
.bind(config.mail.mail_level.as_str())
|
||||||
.bind(config.mail.interval)
|
.bind(config.mail.interval)
|
||||||
.bind(config.logging.ffmpeg_level)
|
.bind(config.logging.ffmpeg_level)
|
||||||
@ -397,6 +399,39 @@ pub async fn insert_user(conn: &Pool<Sqlite>, user: User) -> Result<(), sqlx::Er
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn insert_or_update_user(conn: &Pool<Sqlite>, user: User) -> Result<(), sqlx::Error> {
|
||||||
|
let password_hash = task::spawn_blocking(move || {
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
let hash = Argon2::default()
|
||||||
|
.hash_password(user.password.clone().as_bytes(), &salt)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
hash.to_string()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let query = "INSERT INTO user (mail, username, password, role_id) VALUES($1, $2, $3, $4)
|
||||||
|
ON CONFLICT(username) DO UPDATE SET
|
||||||
|
mail = excluded.mail, username = excluded.username, password = excluded.password, role_id = excluded.role_id
|
||||||
|
RETURNING id";
|
||||||
|
|
||||||
|
let user_id: i32 = sqlx::query(query)
|
||||||
|
.bind(user.mail)
|
||||||
|
.bind(user.username)
|
||||||
|
.bind(password_hash)
|
||||||
|
.bind(user.role_id)
|
||||||
|
.fetch_one(conn)
|
||||||
|
.await?
|
||||||
|
.get("id");
|
||||||
|
|
||||||
|
if let Some(channel_ids) = user.channel_ids {
|
||||||
|
insert_user_channel(conn, user_id, channel_ids).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn update_user(
|
pub async fn update_user(
|
||||||
conn: &Pool<Sqlite>,
|
conn: &Pool<Sqlite>,
|
||||||
id: i32,
|
id: i32,
|
||||||
@ -493,7 +528,7 @@ pub async fn new_channel_presets(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = "INSERT INTO presets (name, text, x, y, fontsize, line_spacing, fontcolor, box, boxcolor, boxborderw, alpha, channel_id)
|
let query = "INSERT INTO presets (name, text, x, y, fontsize, line_spacing, fontcolor, box, boxcolor, boxborderw, alpha, channel_id)
|
||||||
VALUES ('Default', 'Wellcome to ffplayout messenger!', '(w-text_w)/2', '(h-text_h)/2', '24', '4', '#ffffff@0xff', '0', '#000000@0x80', '4', '1.0', $1),
|
VALUES ('Default', 'Welcome to ffplayout messenger!', '(w-text_w)/2', '(h-text_h)/2', '24', '4', '#ffffff@0xff', '0', '#000000@0x80', '4', '1.0', $1),
|
||||||
('Empty Text', '', '0', '0', '24', '4', '#000000', '0', '#000000', '0', '0', $1),
|
('Empty Text', '', '0', '0', '24', '4', '#000000', '0', '#000000', '0', '0', $1),
|
||||||
('Bottom Text fade in', 'The upcoming event will be delayed by a few minutes.', '(w-text_w)/2', '(h-line_h)*0.9', '24', '4', '#ffffff', '1', '#000000@0x80', '4', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),0,if(lt(t,ld(1)+2),(t-(ld(1)+1))/1,if(lt(t,ld(1)+8),1,if(lt(t,ld(1)+9),(1-(t-(ld(1)+8)))/1,0))))', $1),
|
('Bottom Text fade in', 'The upcoming event will be delayed by a few minutes.', '(w-text_w)/2', '(h-line_h)*0.9', '24', '4', '#ffffff', '1', '#000000@0x80', '4', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),0,if(lt(t,ld(1)+2),(t-(ld(1)+1))/1,if(lt(t,ld(1)+8),1,if(lt(t,ld(1)+9),(1-(t-(ld(1)+8)))/1,0))))', $1),
|
||||||
('Scrolling Text', 'We have a very important announcement to make.', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),w+4,w-w/12*mod(t-ld(1),12*(w+tw)/w))', '(h-line_h)*0.9', '24', '4', '#ffffff', '1', '#000000@0x80', '4', '1.0', $1);";
|
('Scrolling Text', 'We have a very important announcement to make.', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),w+4,w-w/12*mod(t-ld(1),12*(w+tw)/w))', '(h-line_h)*0.9', '24', '4', '#ffffff', '1', '#000000@0x80', '4', '1.0', $1);";
|
||||||
|
@ -12,7 +12,7 @@ use sqlx::{sqlite::SqliteRow, FromRow, Pool, Row, Sqlite};
|
|||||||
use crate::db::handles;
|
use crate::db::handles;
|
||||||
use crate::utils::config::PlayoutConfig;
|
use crate::utils::config::PlayoutConfig;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, sqlx::FromRow)]
|
#[derive(Clone, Default, Debug, Deserialize, Serialize, sqlx::FromRow)]
|
||||||
pub struct GlobalSettings {
|
pub struct GlobalSettings {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub secret: Option<String>,
|
pub secret: Option<String>,
|
||||||
@ -21,6 +21,10 @@ pub struct GlobalSettings {
|
|||||||
pub public: String,
|
pub public: String,
|
||||||
pub storage: String,
|
pub storage: String,
|
||||||
pub shared: bool,
|
pub shared: bool,
|
||||||
|
pub mail_smtp: String,
|
||||||
|
pub mail_user: String,
|
||||||
|
pub mail_password: String,
|
||||||
|
pub mail_starttls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalSettings {
|
impl GlobalSettings {
|
||||||
@ -37,6 +41,10 @@ impl GlobalSettings {
|
|||||||
public: String::new(),
|
public: String::new(),
|
||||||
storage: String::new(),
|
storage: String::new(),
|
||||||
shared: false,
|
shared: false,
|
||||||
|
mail_smtp: String::new(),
|
||||||
|
mail_user: String::new(),
|
||||||
|
mail_password: String::new(),
|
||||||
|
mail_starttls: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,11 +272,7 @@ pub struct Configuration {
|
|||||||
|
|
||||||
pub mail_help: String,
|
pub mail_help: String,
|
||||||
pub mail_subject: String,
|
pub mail_subject: String,
|
||||||
pub mail_smtp: String,
|
|
||||||
pub mail_addr: String,
|
|
||||||
pub mail_pass: String,
|
|
||||||
pub mail_recipient: String,
|
pub mail_recipient: String,
|
||||||
pub mail_starttls: bool,
|
|
||||||
pub mail_level: String,
|
pub mail_level: String,
|
||||||
pub mail_interval: i64,
|
pub mail_interval: i64,
|
||||||
|
|
||||||
@ -348,10 +352,6 @@ impl Configuration {
|
|||||||
general_stop_threshold: config.general.stop_threshold,
|
general_stop_threshold: config.general.stop_threshold,
|
||||||
mail_help: config.mail.help_text,
|
mail_help: config.mail.help_text,
|
||||||
mail_subject: config.mail.subject,
|
mail_subject: config.mail.subject,
|
||||||
mail_smtp: config.mail.smtp_server,
|
|
||||||
mail_starttls: config.mail.starttls,
|
|
||||||
mail_addr: config.mail.sender_addr,
|
|
||||||
mail_pass: config.mail.sender_pass,
|
|
||||||
mail_recipient: config.mail.recipient,
|
mail_recipient: config.mail.recipient,
|
||||||
mail_level: config.mail.mail_level.to_string(),
|
mail_level: config.mail.mail_level.to_string(),
|
||||||
mail_interval: config.mail.interval,
|
mail_interval: config.mail.interval,
|
||||||
|
@ -15,7 +15,7 @@ use tokio::fs;
|
|||||||
|
|
||||||
use crate::db::{
|
use crate::db::{
|
||||||
handles,
|
handles,
|
||||||
models::{Channel, GlobalSettings, User},
|
models::{Channel, User},
|
||||||
};
|
};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
advanced_config::AdvancedConfig,
|
advanced_config::AdvancedConfig,
|
||||||
@ -59,6 +59,18 @@ pub struct Args {
|
|||||||
#[clap(long, env, help_heading = Some("Initial Setup"), help = "Storage root path")]
|
#[clap(long, env, help_heading = Some("Initial Setup"), help = "Storage root path")]
|
||||||
pub storage: Option<String>,
|
pub storage: Option<String>,
|
||||||
|
|
||||||
|
#[clap(long, env, help_heading = Some("Initial Setup"), help = "SMTP server for system mails")]
|
||||||
|
pub mail_smtp: Option<String>,
|
||||||
|
|
||||||
|
#[clap(long, env, help_heading = Some("Initial Setup"), help = "Mail user for system mails")]
|
||||||
|
pub mail_user: Option<String>,
|
||||||
|
|
||||||
|
#[clap(long, env, help_heading = Some("Initial Setup"), help = "Mail password for system mails")]
|
||||||
|
pub mail_password: Option<String>,
|
||||||
|
|
||||||
|
#[clap(long, env, help_heading = Some("Initial Setup"), help = "Use TLS for system mails")]
|
||||||
|
pub mail_starttls: bool,
|
||||||
|
|
||||||
#[clap(
|
#[clap(
|
||||||
long,
|
long,
|
||||||
env,
|
env,
|
||||||
@ -76,8 +88,8 @@ pub struct Args {
|
|||||||
#[clap(long, help_heading = Some("Initial Setup / Playlist"), help = "Path to playlist, or playlist root folder.")]
|
#[clap(long, help_heading = Some("Initial Setup / Playlist"), help = "Path to playlist, or playlist root folder.")]
|
||||||
pub playlists: Option<String>,
|
pub playlists: Option<String>,
|
||||||
|
|
||||||
#[clap(short, long, help_heading = Some("General"), help = "Add a global admin")]
|
#[clap(long, help_heading = Some("General"), help = "Add or update a global admin use")]
|
||||||
pub add: bool,
|
pub user_set: bool,
|
||||||
|
|
||||||
#[clap(long, env, help_heading = Some("General"), help = "Path to database file")]
|
#[clap(long, env, help_heading = Some("General"), help = "Path to database file")]
|
||||||
pub db: Option<PathBuf>,
|
pub db: Option<PathBuf>,
|
||||||
@ -240,15 +252,10 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
let mut logging = String::new();
|
let mut logging = String::new();
|
||||||
let mut public = String::new();
|
let mut public = String::new();
|
||||||
let mut shared_store = String::new();
|
let mut shared_store = String::new();
|
||||||
let mut global = GlobalSettings {
|
let mut mail_smtp = String::new();
|
||||||
id: 0,
|
let mut mail_user = String::new();
|
||||||
secret: None,
|
let mut mail_starttls = String::new();
|
||||||
logs: String::new(),
|
let mut global = handles::select_global(pool).await.map_err(|_| 1)?;
|
||||||
playlists: String::new(),
|
|
||||||
public: String::new(),
|
|
||||||
storage: String::new(),
|
|
||||||
shared: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if check_user.unwrap_or_default().is_empty() {
|
if check_user.unwrap_or_default().is_empty() {
|
||||||
global_user(&mut args);
|
global_user(&mut args);
|
||||||
@ -257,94 +264,156 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
if let Some(st) = args.storage {
|
if let Some(st) = args.storage {
|
||||||
global.storage = st;
|
global.storage = st;
|
||||||
} else {
|
} else {
|
||||||
print!("Storage path [/var/lib/ffplayout/tv-media]: ");
|
print!("Storage path [{}]: ", global.storage);
|
||||||
stdout().flush().unwrap();
|
stdout().flush().unwrap();
|
||||||
|
|
||||||
stdin()
|
stdin()
|
||||||
.read_line(&mut storage)
|
.read_line(&mut storage)
|
||||||
.expect("Did not enter a correct path?");
|
.expect("Did not enter a correct path?");
|
||||||
|
|
||||||
global.storage = if storage.trim().is_empty() {
|
if !storage.trim().is_empty() {
|
||||||
"/var/lib/ffplayout/tv-media".to_string()
|
global.storage = storage
|
||||||
} else {
|
|
||||||
storage
|
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string()
|
.to_string();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pl) = args.playlists {
|
if let Some(pl) = args.playlists {
|
||||||
global.playlists = pl
|
global.playlists = pl
|
||||||
} else {
|
} else {
|
||||||
print!("Playlist path [/var/lib/ffplayout/playlists]: ");
|
print!("Playlist path [{}]: ", global.playlists);
|
||||||
stdout().flush().unwrap();
|
stdout().flush().unwrap();
|
||||||
|
|
||||||
stdin()
|
stdin()
|
||||||
.read_line(&mut playlist)
|
.read_line(&mut playlist)
|
||||||
.expect("Did not enter a correct path?");
|
.expect("Did not enter a correct path?");
|
||||||
|
|
||||||
global.playlists = if playlist.trim().is_empty() {
|
if !playlist.trim().is_empty() {
|
||||||
"/var/lib/ffplayout/playlists".to_string()
|
global.playlists = playlist
|
||||||
} else {
|
|
||||||
playlist
|
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string()
|
.to_string();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(lp) = args.logs {
|
if let Some(lp) = args.logs {
|
||||||
global.logs = lp;
|
global.logs = lp;
|
||||||
} else {
|
} else {
|
||||||
print!("Logging path [/var/log/ffplayout]: ");
|
print!("Logging path [{}]: ", global.logs);
|
||||||
stdout().flush().unwrap();
|
stdout().flush().unwrap();
|
||||||
|
|
||||||
stdin()
|
stdin()
|
||||||
.read_line(&mut logging)
|
.read_line(&mut logging)
|
||||||
.expect("Did not enter a correct path?");
|
.expect("Did not enter a correct path?");
|
||||||
|
|
||||||
global.logs = if logging.trim().is_empty() {
|
if !logging.trim().is_empty() {
|
||||||
"/var/log/ffplayout".to_string()
|
global.logs = logging
|
||||||
} else {
|
|
||||||
logging
|
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string()
|
.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(p) = args.public {
|
if let Some(p) = args.public {
|
||||||
global.public = p;
|
global.public = p;
|
||||||
} else {
|
} else {
|
||||||
print!("Public (HLS) path [/usr/share/ffplayout/public]: ");
|
print!("Public (HLS) path [{}]: ", global.public);
|
||||||
stdout().flush().unwrap();
|
stdout().flush().unwrap();
|
||||||
|
|
||||||
stdin()
|
stdin()
|
||||||
.read_line(&mut public)
|
.read_line(&mut public)
|
||||||
.expect("Did not enter a correct path?");
|
.expect("Did not enter a correct path?");
|
||||||
|
|
||||||
global.public = if public.trim().is_empty() {
|
if !public.trim().is_empty() {
|
||||||
"/usr/share/ffplayout/public".to_string()
|
global.public = public
|
||||||
} else {
|
|
||||||
public
|
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string()
|
.to_string();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.shared {
|
if args.shared {
|
||||||
global.shared = true;
|
global.shared = true;
|
||||||
} else {
|
} else {
|
||||||
print!("Shared storage [Y/n]: ");
|
print!(
|
||||||
|
"Shared storage [{}]: ",
|
||||||
|
if global.shared { "yes" } else { "no" }
|
||||||
|
);
|
||||||
stdout().flush().unwrap();
|
stdout().flush().unwrap();
|
||||||
|
|
||||||
stdin()
|
stdin()
|
||||||
.read_line(&mut shared_store)
|
.read_line(&mut shared_store)
|
||||||
.expect("Did not enter a yes or no?");
|
.expect("Did not enter a yes or no?");
|
||||||
|
|
||||||
global.shared = shared_store.trim().to_lowercase().starts_with('y');
|
if !shared_store.trim().is_empty() {
|
||||||
|
global.shared = shared_store.trim().to_lowercase().starts_with('y');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(smtp) = args.mail_smtp {
|
||||||
|
global.mail_smtp = smtp;
|
||||||
|
} else {
|
||||||
|
print!("SMTP server [{}]: ", global.mail_smtp);
|
||||||
|
stdout().flush().unwrap();
|
||||||
|
stdin()
|
||||||
|
.read_line(&mut mail_smtp)
|
||||||
|
.expect("Did not enter a correct SMTP server?");
|
||||||
|
|
||||||
|
if !mail_smtp.trim().is_empty() {
|
||||||
|
global.mail_smtp = mail_smtp
|
||||||
|
.trim()
|
||||||
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(user) = args.mail_user {
|
||||||
|
global.mail_user = user;
|
||||||
|
} else {
|
||||||
|
print!("SMTP user [{}]: ", global.mail_user);
|
||||||
|
stdout().flush().unwrap();
|
||||||
|
stdin()
|
||||||
|
.read_line(&mut mail_user)
|
||||||
|
.expect("Did not enter a correct SMTP user?");
|
||||||
|
|
||||||
|
if !mail_user.trim().is_empty() {
|
||||||
|
global.mail_user = mail_user
|
||||||
|
.trim()
|
||||||
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pass) = args.mail_password {
|
||||||
|
global.mail_password = pass;
|
||||||
|
} else {
|
||||||
|
print!(
|
||||||
|
"SMTP password [{}]: ",
|
||||||
|
if global.mail_password.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
"********"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
stdout().flush().unwrap();
|
||||||
|
let password = read_password().unwrap_or_default();
|
||||||
|
|
||||||
|
if !password.trim().is_empty() {
|
||||||
|
global.mail_password = password.trim().to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.mail_starttls {
|
||||||
|
global.mail_starttls = true;
|
||||||
|
} else {
|
||||||
|
print!(
|
||||||
|
"SMTP use TLS [{}]: ",
|
||||||
|
if global.mail_starttls { "yes" } else { "no" }
|
||||||
|
);
|
||||||
|
stdout().flush().unwrap();
|
||||||
|
stdin()
|
||||||
|
.read_line(&mut mail_starttls)
|
||||||
|
.expect("Did not enter a yes or no?");
|
||||||
|
|
||||||
|
if !mail_starttls.trim().is_empty() {
|
||||||
|
global.mail_starttls = mail_starttls.trim().to_lowercase().starts_with('y');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = handles::update_global(pool, global.clone()).await {
|
if let Err(e) = handles::update_global(pool, global.clone()).await {
|
||||||
@ -385,7 +454,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("\nSet global settings done...");
|
println!("\nSet global settings done...");
|
||||||
} else if args.add {
|
} else if args.user_set {
|
||||||
global_user(&mut args);
|
global_user(&mut args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,12 +473,12 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
token: None,
|
token: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = handles::insert_user(pool, ff_user).await {
|
if let Err(e) = handles::insert_or_update_user(pool, ff_user).await {
|
||||||
eprintln!("{e}");
|
eprintln!("{e}");
|
||||||
error_code = 1;
|
error_code = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Create global admin user \"{username}\" done...");
|
println!("Create/update global admin user \"{username}\" done...");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ARGS.list_channels {
|
if ARGS.list_channels {
|
||||||
|
@ -43,12 +43,13 @@ pub const FFMPEG_IGNORE_ERRORS: [&str; 13] = [
|
|||||||
"frame size not set",
|
"frame size not set",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const FFMPEG_UNRECOVERABLE_ERRORS: [&str; 5] = [
|
pub const FFMPEG_UNRECOVERABLE_ERRORS: [&str; 6] = [
|
||||||
"Address already in use",
|
"Address already in use",
|
||||||
"Invalid argument",
|
"Invalid argument",
|
||||||
"Numerical result",
|
"Numerical result",
|
||||||
"Error initializing complex filters",
|
"Error initializing complex filters",
|
||||||
"Error while decoding stream #0:0: Invalid data found when processing input",
|
"Error while decoding stream #0:0: Invalid data found when processing input",
|
||||||
|
"Unrecognized option",
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
@ -239,9 +240,13 @@ impl General {
|
|||||||
pub struct Mail {
|
pub struct Mail {
|
||||||
pub help_text: String,
|
pub help_text: String,
|
||||||
pub subject: String,
|
pub subject: String,
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub smtp_server: String,
|
pub smtp_server: String,
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub starttls: bool,
|
pub starttls: bool,
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub sender_addr: String,
|
pub sender_addr: String,
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub sender_pass: String,
|
pub sender_pass: String,
|
||||||
pub recipient: String,
|
pub recipient: String,
|
||||||
pub mail_level: Level,
|
pub mail_level: Level,
|
||||||
@ -249,14 +254,14 @@ pub struct Mail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mail {
|
impl Mail {
|
||||||
fn new(config: &models::Configuration) -> Self {
|
fn new(global: &models::GlobalSettings, config: &models::Configuration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
help_text: config.mail_help.clone(),
|
help_text: config.mail_help.clone(),
|
||||||
subject: config.mail_subject.clone(),
|
subject: config.mail_subject.clone(),
|
||||||
smtp_server: config.mail_smtp.clone(),
|
smtp_server: global.mail_smtp.clone(),
|
||||||
starttls: config.mail_starttls,
|
starttls: global.mail_starttls,
|
||||||
sender_addr: config.mail_addr.clone(),
|
sender_addr: global.mail_user.clone(),
|
||||||
sender_pass: config.mail_pass.clone(),
|
sender_pass: global.mail_password.clone(),
|
||||||
recipient: config.mail_recipient.clone(),
|
recipient: config.mail_recipient.clone(),
|
||||||
mail_level: string_to_log_level(config.mail_level.clone()),
|
mail_level: string_to_log_level(config.mail_level.clone()),
|
||||||
interval: config.mail_interval,
|
interval: config.mail_interval,
|
||||||
@ -570,9 +575,7 @@ fn default_track_index() -> i32 {
|
|||||||
|
|
||||||
impl PlayoutConfig {
|
impl PlayoutConfig {
|
||||||
pub async fn new(pool: &Pool<Sqlite>, channel_id: i32) -> Result<Self, ServiceError> {
|
pub async fn new(pool: &Pool<Sqlite>, channel_id: i32) -> Result<Self, ServiceError> {
|
||||||
let global = handles::select_global(pool)
|
let global = handles::select_global(pool).await?;
|
||||||
.await
|
|
||||||
.expect("Can't read globals");
|
|
||||||
let channel = handles::select_channel(pool, &channel_id).await?;
|
let channel = handles::select_channel(pool, &channel_id).await?;
|
||||||
let config = handles::select_configuration(pool, channel_id).await?;
|
let config = handles::select_configuration(pool, channel_id).await?;
|
||||||
let adv_config = handles::select_advanced_configuration(pool, channel_id).await?;
|
let adv_config = handles::select_advanced_configuration(pool, channel_id).await?;
|
||||||
@ -580,7 +583,7 @@ impl PlayoutConfig {
|
|||||||
let channel = Channel::new(&global, channel);
|
let channel = Channel::new(&global, channel);
|
||||||
let advanced = AdvancedConfig::new(adv_config);
|
let advanced = AdvancedConfig::new(adv_config);
|
||||||
let general = General::new(&config);
|
let general = General::new(&config);
|
||||||
let mail = Mail::new(&config);
|
let mail = Mail::new(&global, &config);
|
||||||
let logging = Logging::new(&config);
|
let logging = Logging::new(&config);
|
||||||
let mut processing = Processing::new(&config);
|
let mut processing = Processing::new(&config);
|
||||||
let mut ingest = Ingest::new(&config);
|
let mut ingest = Ingest::new(&config);
|
||||||
@ -919,12 +922,28 @@ pub async fn get_config(
|
|||||||
if args.shared {
|
if args.shared {
|
||||||
// config.channel.shared could be true already,
|
// config.channel.shared could be true already,
|
||||||
// so should not be overridden with false when args.shared is not set
|
// so should not be overridden with false when args.shared is not set
|
||||||
config.channel.shared = args.shared
|
config.channel.shared = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(volume) = args.volume {
|
if let Some(volume) = args.volume {
|
||||||
config.processing.volume = volume;
|
config.processing.volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(mail_smtp) = args.mail_smtp {
|
||||||
|
config.mail.smtp_server = mail_smtp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mail_user) = args.mail_user {
|
||||||
|
config.mail.sender_addr = mail_user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mail_password) = args.mail_password {
|
||||||
|
config.mail.sender_pass = mail_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.mail_starttls {
|
||||||
|
config.mail.starttls = true;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ pub fn mail_queue(mail_queues: Arc<Mutex<Vec<Arc<Mutex<MailQueue>>>>>) {
|
|||||||
poisoned.into_inner()
|
poisoned.into_inner()
|
||||||
});
|
});
|
||||||
|
|
||||||
let expire = round_to_nearest_ten(q_lock.config.interval);
|
let expire = round_to_nearest_ten(q_lock.config.interval.max(30));
|
||||||
|
|
||||||
if interval % expire == 0 && !q_lock.is_empty() {
|
if interval % expire == 0 && !q_lock.is_empty() {
|
||||||
if q_lock.config.recipient.contains('@') {
|
if q_lock.config.recipient.contains('@') {
|
||||||
|
@ -10,6 +10,10 @@ CREATE TABLE
|
|||||||
public TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
|
public TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
|
||||||
storage TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
|
storage TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
|
||||||
shared INTEGER NOT NULL DEFAULT 0,
|
shared INTEGER NOT NULL DEFAULT 0,
|
||||||
|
mail_smtp TEXT NOT NULL DEFAULT "mail.example.org",
|
||||||
|
mail_user TEXT NOT NULL DEFAULT "ffplayout@example.org",
|
||||||
|
mail_password TEXT NOT NULL DEFAULT "",
|
||||||
|
mail_starttls INTEGER NULL DEFAULT 0,
|
||||||
UNIQUE (secret)
|
UNIQUE (secret)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -60,7 +64,7 @@ CREATE TABLE
|
|||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
role_id INTEGER NOT NULL DEFAULT 3,
|
role_id INTEGER NOT NULL DEFAULT 3,
|
||||||
FOREIGN KEY (role_id) REFERENCES roles (id) ON UPDATE SET NULL ON DELETE SET DEFAULT,
|
FOREIGN KEY (role_id) REFERENCES roles (id) ON UPDATE SET NULL ON DELETE SET DEFAULT,
|
||||||
UNIQUE (mail, username)
|
UNIQUE (username)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE
|
CREATE TABLE
|
||||||
@ -80,13 +84,9 @@ CREATE TABLE
|
|||||||
channel_id INTEGER NOT NULL DEFAULT 1,
|
channel_id INTEGER NOT NULL DEFAULT 1,
|
||||||
general_help TEXT NOT NULL DEFAULT "Sometimes it can happen that a file is corrupt but still playable. This can produce a streaming error for all following files. The only solution in this case is to stop ffplayout and start it again.\n'stop_threshold' stops ffplayout if it is asynchronous in time above this value. A number below 3 can cause unexpected errors.",
|
general_help TEXT NOT NULL DEFAULT "Sometimes it can happen that a file is corrupt but still playable. This can produce a streaming error for all following files. The only solution in this case is to stop ffplayout and start it again.\n'stop_threshold' stops ffplayout if it is asynchronous in time above this value. A number below 3 can cause unexpected errors.",
|
||||||
general_stop_threshold REAL NOT NULL DEFAULT 11.0,
|
general_stop_threshold REAL NOT NULL DEFAULT 11.0,
|
||||||
mail_help TEXT NOT NULL DEFAULT "Send error messages to an email address, such as missing playlist, invalid JSON format, or missing clip path. Leave the recipient blank if you don't need this.\n'mail_level' can be INFO, WARNING, or ERROR.\n'interval' refers to the number of seconds until a new email is sent; the value must be in increments of 10.",
|
mail_help TEXT NOT NULL DEFAULT "Send error messages to an email address, such as missing clips, missing or invalid playlist format, etc.. Leave the recipient blank if you don't need this.\n'mail_level' can be INFO, WARNING, or ERROR.\n'interval' refers to the number of seconds until a new email is sent; the value must be in increments of 10 and not lower then 30 seconds.",
|
||||||
mail_subject TEXT NOT NULL DEFAULT "Playout Error",
|
mail_subject TEXT NOT NULL DEFAULT "Playout Error",
|
||||||
mail_smtp TEXT NOT NULL DEFAULT "mail.example.org",
|
|
||||||
mail_addr TEXT NOT NULL DEFAULT "ffplayout@example.org",
|
|
||||||
mail_pass TEXT NOT NULL DEFAULT "",
|
|
||||||
mail_recipient TEXT NOT NULL DEFAULT "",
|
mail_recipient TEXT NOT NULL DEFAULT "",
|
||||||
mail_starttls INTEGER NOT NULL DEFAULT 0,
|
|
||||||
mail_level TEXT NOT NULL DEFAULT "ERROR",
|
mail_level TEXT NOT NULL DEFAULT "ERROR",
|
||||||
mail_interval INTEGER NOT NULL DEFAULT 120,
|
mail_interval INTEGER NOT NULL DEFAULT 120,
|
||||||
logging_help TEXT NOT NULL DEFAULT "'ffmpeg_level/ingest_level' can be INFO, WARNING, or ERROR.\n'detect_silence' logs an error message if the audio line is silent for 15 seconds during the validation process.\n'ignore' allows logging to ignore strings that contain matched lines; the format is a semicolon-separated list.",
|
logging_help TEXT NOT NULL DEFAULT "'ffmpeg_level/ingest_level' can be INFO, WARNING, or ERROR.\n'detect_silence' logs an error message if the audio line is silent for 15 seconds during the validation process.\n'ignore' allows logging to ignore strings that contain matched lines; the format is a semicolon-separated list.",
|
||||||
@ -213,7 +213,7 @@ INSERT INTO
|
|||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
'Default',
|
'Default',
|
||||||
'Wellcome to ffplayout messenger!',
|
'Welcome to ffplayout messenger!',
|
||||||
'(w-text_w)/2',
|
'(w-text_w)/2',
|
||||||
'(h-text_h)/2',
|
'(h-text_h)/2',
|
||||||
'24',
|
'24',
|
||||||
|
Loading…
Reference in New Issue
Block a user