move storage paths to channel
This commit is contained in:
parent
aabc2b8df5
commit
568fcab859
49
Cargo.lock
generated
49
Cargo.lock
generated
@ -3256,15 +3256,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stacker"
|
name = "stacker"
|
||||||
version = "0.1.15"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
|
checksum = "95a5daa25ea337c85ed954c0496e3bdd2c7308cc3b24cf7b50d04876654c579f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"psm",
|
"psm",
|
||||||
"winapi",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4076,6 +4076,19 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc 0.36.1",
|
||||||
|
"windows_i686_gnu 0.36.1",
|
||||||
|
"windows_i686_msvc 0.36.1",
|
||||||
|
"windows_x86_64_gnu 0.36.1",
|
||||||
|
"windows_x86_64_msvc 0.36.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
@ -4146,6 +4159,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@ -4158,6 +4177,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@ -4176,6 +4201,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@ -4188,6 +4219,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@ -4212,6 +4249,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
|
@ -478,10 +478,17 @@ async fn patch_channel(
|
|||||||
role: AuthDetails<Role>,
|
role: AuthDetails<Role>,
|
||||||
user: web::ReqData<UserMeta>,
|
user: web::ReqData<UserMeta>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
if handles::update_channel(&pool, *id, data.into_inner())
|
let mut data = data.into_inner();
|
||||||
.await
|
|
||||||
.is_ok()
|
if !role.has_authority(&Role::GlobalAdmin) {
|
||||||
{
|
let channel = handles::select_channel(&pool, &id).await?;
|
||||||
|
|
||||||
|
data.hls_path = channel.hls_path;
|
||||||
|
data.playlist_path = channel.playlist_path;
|
||||||
|
data.storage_path = channel.storage_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if handles::update_channel(&pool, *id, data).await.is_ok() {
|
||||||
return Ok("Update Success");
|
return Ok("Update Success");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1022,7 +1029,7 @@ pub async fn gen_playlist(
|
|||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
let manager = controllers.lock().unwrap().get(params.0).unwrap();
|
let manager = controllers.lock().unwrap().get(params.0).unwrap();
|
||||||
manager.config.lock().unwrap().general.generate = Some(vec![params.1.clone()]);
|
manager.config.lock().unwrap().general.generate = Some(vec![params.1.clone()]);
|
||||||
let storage_path = manager.config.lock().unwrap().global.storage_path.clone();
|
let storage_path = manager.config.lock().unwrap().channel.storage_path.clone();
|
||||||
|
|
||||||
if let Some(obj) = data {
|
if let Some(obj) = data {
|
||||||
if let Some(paths) = &obj.paths {
|
if let Some(paths) = &obj.paths {
|
||||||
@ -1264,7 +1271,7 @@ async fn get_file(
|
|||||||
let id: i32 = req.match_info().query("id").parse()?;
|
let id: i32 = req.match_info().query("id").parse()?;
|
||||||
let manager = controllers.lock().unwrap().get(id).unwrap();
|
let manager = controllers.lock().unwrap().get(id).unwrap();
|
||||||
let config = manager.config.lock().unwrap();
|
let config = manager.config.lock().unwrap();
|
||||||
let storage_path = config.global.storage_path.clone();
|
let storage_path = config.channel.storage_path.clone();
|
||||||
let file_path = req.match_info().query("filename");
|
let file_path = req.match_info().query("filename");
|
||||||
let (path, _, _) = norm_abs_path(&storage_path, file_path)?;
|
let (path, _, _) = norm_abs_path(&storage_path, file_path)?;
|
||||||
let file = actix_files::NamedFile::open(path)?;
|
let file = actix_files::NamedFile::open(path)?;
|
||||||
@ -1295,7 +1302,7 @@ async fn get_public(
|
|||||||
let absolute_path = if file_stem.ends_with(".ts") || file_stem.ends_with(".m3u8") {
|
let absolute_path = if file_stem.ends_with(".ts") || file_stem.ends_with(".m3u8") {
|
||||||
let manager = controllers.lock().unwrap().get(id).unwrap();
|
let manager = controllers.lock().unwrap().get(id).unwrap();
|
||||||
let config = manager.config.lock().unwrap();
|
let config = manager.config.lock().unwrap();
|
||||||
config.global.hls_path.join(public)
|
config.channel.hls_path.join(public)
|
||||||
} else if public_path.is_absolute() {
|
} else if public_path.is_absolute() {
|
||||||
public_path.to_path_buf()
|
public_path.to_path_buf()
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,7 +40,7 @@ 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 id, secret, hls_path, logging_path, playlist_path, storage_path, shared_storage FROM global WHERE id = 1";
|
let query = "SELECT id, secret, logging_path, playlist_root, public_root, storage_root, shared_storage FROM global WHERE id = 1";
|
||||||
|
|
||||||
sqlx::query_as(query).fetch_one(conn).await
|
sqlx::query_as(query).fetch_one(conn).await
|
||||||
}
|
}
|
||||||
@ -49,14 +49,14 @@ 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 hls_path = $2, playlist_path = $3, storage_path = $4, logging_path = $5, shared_storage = $6 WHERE id = 1";
|
let query = "UPDATE global SET logging_path = $2, playlist_root = $3, public_root = $4, storage_root = $5, shared_storage = $6 WHERE id = 1";
|
||||||
|
|
||||||
sqlx::query(query)
|
sqlx::query(query)
|
||||||
.bind(global.id)
|
.bind(global.id)
|
||||||
.bind(global.hls_path)
|
|
||||||
.bind(global.playlist_path)
|
|
||||||
.bind(global.storage_path)
|
|
||||||
.bind(global.logging_path)
|
.bind(global.logging_path)
|
||||||
|
.bind(global.playlist_root)
|
||||||
|
.bind(global.public_root)
|
||||||
|
.bind(global.storage_root)
|
||||||
.bind(global.shared_storage)
|
.bind(global.shared_storage)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
@ -77,7 +77,7 @@ pub async fn select_related_channels(
|
|||||||
) -> Result<Vec<Channel>, sqlx::Error> {
|
) -> Result<Vec<Channel>, sqlx::Error> {
|
||||||
let query = match user_id {
|
let query = match user_id {
|
||||||
Some(id) => format!(
|
Some(id) => format!(
|
||||||
"SELECT c.id, c.name, c.preview_url, c.extra_extensions, c.active, c.last_date, c.time_shift FROM channels c
|
"SELECT c.id, c.name, c.preview_url, c.extra_extensions, c.active, c.hls_path, c.playlist_path, c.storage_path, c.last_date, c.time_shift FROM channels c
|
||||||
left join user_channels uc on uc.channel_id = c.id
|
left join user_channels uc on uc.channel_id = c.id
|
||||||
left join user u on u.id = uc.user_id
|
left join user u on u.id = uc.user_id
|
||||||
WHERE u.id = {id} ORDER BY c.id ASC;"
|
WHERE u.id = {id} ORDER BY c.id ASC;"
|
||||||
@ -114,13 +114,16 @@ pub async fn update_channel(
|
|||||||
channel: Channel,
|
channel: Channel,
|
||||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query =
|
let query =
|
||||||
"UPDATE channels SET name = $2, preview_url = $3, extra_extensions = $4 WHERE id = $1";
|
"UPDATE channels SET name = $2, preview_url = $3, extra_extensions = $4, hls_path = $5, playlist_path = $6, storage_path = $7 WHERE id = $1";
|
||||||
|
|
||||||
sqlx::query(query)
|
sqlx::query(query)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(channel.name)
|
.bind(channel.name)
|
||||||
.bind(channel.preview_url)
|
.bind(channel.preview_url)
|
||||||
.bind(channel.extra_extensions)
|
.bind(channel.extra_extensions)
|
||||||
|
.bind(channel.hls_path)
|
||||||
|
.bind(channel.playlist_path)
|
||||||
|
.bind(channel.storage_path)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -152,11 +155,14 @@ pub async fn update_player(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert_channel(conn: &Pool<Sqlite>, channel: Channel) -> Result<Channel, sqlx::Error> {
|
pub async fn insert_channel(conn: &Pool<Sqlite>, channel: Channel) -> Result<Channel, sqlx::Error> {
|
||||||
let query = "INSERT INTO channels (name, preview_url, extra_extensions) VALUES($1, $2, $3)";
|
let query = "INSERT INTO channels (name, preview_url, extra_extensions, hls_path, playlist_path, storage_path) VALUES($1, $2, $3, $4, $5, $6)";
|
||||||
let result = sqlx::query(query)
|
let result = sqlx::query(query)
|
||||||
.bind(channel.name)
|
.bind(channel.name)
|
||||||
.bind(channel.preview_url)
|
.bind(channel.preview_url)
|
||||||
.bind(channel.extra_extensions)
|
.bind(channel.extra_extensions)
|
||||||
|
.bind(channel.hls_path)
|
||||||
|
.bind(channel.playlist_path)
|
||||||
|
.bind(channel.storage_path)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@ use crate::utils::config::PlayoutConfig;
|
|||||||
pub struct GlobalSettings {
|
pub struct GlobalSettings {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub secret: Option<String>,
|
pub secret: Option<String>,
|
||||||
pub hls_path: String,
|
|
||||||
pub logging_path: String,
|
pub logging_path: String,
|
||||||
pub playlist_path: String,
|
pub playlist_root: String,
|
||||||
pub storage_path: String,
|
pub public_root: String,
|
||||||
|
pub storage_root: String,
|
||||||
pub shared_storage: bool,
|
pub shared_storage: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,10 +32,10 @@ impl GlobalSettings {
|
|||||||
Err(_) => GlobalSettings {
|
Err(_) => GlobalSettings {
|
||||||
id: 0,
|
id: 0,
|
||||||
secret: None,
|
secret: None,
|
||||||
hls_path: String::new(),
|
|
||||||
logging_path: String::new(),
|
logging_path: String::new(),
|
||||||
playlist_path: String::new(),
|
playlist_root: String::new(),
|
||||||
storage_path: String::new(),
|
public_root: String::new(),
|
||||||
|
storage_root: String::new(),
|
||||||
shared_storage: false,
|
shared_storage: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -240,6 +240,9 @@ pub struct Channel {
|
|||||||
pub preview_url: String,
|
pub preview_url: String,
|
||||||
pub extra_extensions: String,
|
pub extra_extensions: String,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
|
pub hls_path: String,
|
||||||
|
pub playlist_path: String,
|
||||||
|
pub storage_path: String,
|
||||||
pub last_date: Option<String>,
|
pub last_date: Option<String>,
|
||||||
pub time_shift: f64,
|
pub time_shift: f64,
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
exit(1);
|
exit(1);
|
||||||
};
|
};
|
||||||
} else if ARGS.validate {
|
} else if ARGS.validate {
|
||||||
let mut playlist_path = config.global.playlist_path.clone();
|
let mut playlist_path = config.channel.playlist_path.clone();
|
||||||
let start_sec = config.playlist.start_sec.unwrap();
|
let start_sec = config.playlist.start_sec.unwrap();
|
||||||
let date = get_date(false, start_sec, false);
|
let date = get_date(false, start_sec, false);
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ pub fn start_channel(manager: ChannelManager) -> Result<(), ProcessError> {
|
|||||||
let channel_id = config.general.channel_id;
|
let channel_id = config.general.channel_id;
|
||||||
|
|
||||||
drain_hls_path(
|
drain_hls_path(
|
||||||
&config.global.hls_path,
|
&config.channel.hls_path,
|
||||||
&config.output.output_cmd.clone().unwrap_or_default(),
|
&config.output.output_cmd.clone().unwrap_or_default(),
|
||||||
channel_id,
|
channel_id,
|
||||||
)?;
|
)?;
|
||||||
|
@ -29,7 +29,7 @@ pub fn watchman(
|
|||||||
sources: Arc<Mutex<Vec<Media>>>,
|
sources: Arc<Mutex<Vec<Media>>>,
|
||||||
) {
|
) {
|
||||||
let id = config.general.channel_id;
|
let id = config.general.channel_id;
|
||||||
let path = Path::new(&config.global.storage_path);
|
let path = Path::new(&config.channel.storage_path);
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
error!("Folder path not exists: '{path:?}'");
|
error!("Folder path not exists: '{path:?}'");
|
||||||
|
@ -28,7 +28,7 @@ pub fn source_generator(manager: ChannelManager) -> Box<dyn Iterator<Item = Medi
|
|||||||
info!(target: Target::file_mail(), channel = id; "Playout in folder mode");
|
info!(target: Target::file_mail(), channel = id; "Playout in folder mode");
|
||||||
debug!(target: Target::file_mail(), channel = id;
|
debug!(target: Target::file_mail(), channel = id;
|
||||||
"Monitor folder: <b><magenta>{:?}</></b>",
|
"Monitor folder: <b><magenta>{:?}</></b>",
|
||||||
config.global.storage_path
|
config.channel.storage_path
|
||||||
);
|
);
|
||||||
|
|
||||||
let config_clone = config.clone();
|
let config_clone = config.clone();
|
||||||
|
@ -317,7 +317,7 @@ impl CurrentProgram {
|
|||||||
if self.current_node.source.contains(
|
if self.current_node.source.contains(
|
||||||
&self
|
&self
|
||||||
.config
|
.config
|
||||||
.global
|
.channel
|
||||||
.storage_path
|
.storage_path
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
@ -40,7 +40,7 @@ impl FolderSource {
|
|||||||
path_list.push(path)
|
path_list.push(path)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
path_list.push(&config.global.storage_path)
|
path_list.push(&config.channel.storage_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
for path in &path_list {
|
for path in &path_list {
|
||||||
|
@ -28,13 +28,13 @@ pub fn import_file(
|
|||||||
program: vec![],
|
program: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let playlist_root = &config.global.playlist_path;
|
let playlist_root = &config.channel.playlist_path;
|
||||||
if !playlist_root.is_dir() {
|
if !playlist_root.is_dir() {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"Playlist folder <b><magenta>{:?}</></b> not exists!",
|
"Playlist folder <b><magenta>{:?}</></b> not exists!",
|
||||||
config.global.playlist_path,
|
config.channel.playlist_path,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -100,11 +100,11 @@ pub fn read_json(
|
|||||||
) -> JsonPlaylist {
|
) -> JsonPlaylist {
|
||||||
let id = config.general.channel_id;
|
let id = config.general.channel_id;
|
||||||
let config_clone = config.clone();
|
let config_clone = config.clone();
|
||||||
let mut playlist_path = config.global.playlist_path.clone();
|
let mut playlist_path = config.channel.playlist_path.clone();
|
||||||
let start_sec = config.playlist.start_sec.unwrap();
|
let start_sec = config.playlist.start_sec.unwrap();
|
||||||
let date = get_date(seek, start_sec, get_next);
|
let date = get_date(seek, start_sec, get_next);
|
||||||
|
|
||||||
if playlist_path.is_dir() || is_remote(&config.global.playlist_path.to_string_lossy()) {
|
if playlist_path.is_dir() || is_remote(&config.channel.playlist_path.to_string_lossy()) {
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
playlist_path = playlist_path
|
playlist_path = playlist_path
|
||||||
.join(d[0])
|
.join(d[0])
|
||||||
|
@ -113,16 +113,20 @@ pub struct Args {
|
|||||||
#[clap(long, env, help = "Log to console")]
|
#[clap(long, env, help = "Log to console")]
|
||||||
pub log_to_console: bool,
|
pub log_to_console: bool,
|
||||||
|
|
||||||
#[clap(long, env, help = "HLS output path")]
|
#[clap(long, env, help = "Public (HLS) output path")]
|
||||||
pub hls_path: Option<String>,
|
pub public_root: Option<String>,
|
||||||
|
|
||||||
#[clap(long, env, help = "Playlist root path")]
|
#[clap(long, env, help = "Playlist root path")]
|
||||||
pub playlist_path: Option<String>,
|
pub playlist_root: Option<String>,
|
||||||
|
|
||||||
#[clap(long, env, help = "Storage root path")]
|
#[clap(long, env, help = "Storage root path")]
|
||||||
pub storage_path: Option<String>,
|
pub storage_root: Option<String>,
|
||||||
|
|
||||||
#[clap(long, env, help = "Share storage across channels")]
|
#[clap(
|
||||||
|
long,
|
||||||
|
env,
|
||||||
|
help = "Share storage root across channels, important for running in Container"
|
||||||
|
)]
|
||||||
pub shared_storage: bool,
|
pub shared_storage: bool,
|
||||||
|
|
||||||
#[clap(short, long, help = "Create admin user")]
|
#[clap(short, long, help = "Create admin user")]
|
||||||
@ -216,7 +220,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
let mut fix_perm = String::new();
|
let mut fix_perm = String::new();
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"\nYou run the initialization as user {}. Fix permissions after initialization?",
|
"\nYou run the initialization as user {}.\nFix permissions after initialization?\n",
|
||||||
user_name
|
user_name
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -230,7 +234,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
fix_permission = fix_perm.trim().to_lowercase().starts_with('y');
|
fix_permission = fix_perm.trim().to_lowercase().starts_with('y');
|
||||||
|
|
||||||
if fix_permission && user_name != "root" {
|
if fix_permission && user_name != "root" {
|
||||||
println!("You do not have permission to change DB file ownership! Run as proper process user or root.");
|
println!("\nYou do not have permission to change DB file ownership!\nRun as proper process user or root.");
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -246,10 +250,10 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
let mut global = GlobalSettings {
|
let mut global = GlobalSettings {
|
||||||
id: 0,
|
id: 0,
|
||||||
secret: None,
|
secret: None,
|
||||||
hls_path: String::new(),
|
|
||||||
playlist_path: String::new(),
|
|
||||||
storage_path: String::new(),
|
|
||||||
logging_path: String::new(),
|
logging_path: String::new(),
|
||||||
|
playlist_root: String::new(),
|
||||||
|
public_root: String::new(),
|
||||||
|
storage_root: String::new(),
|
||||||
shared_storage: false,
|
shared_storage: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -265,9 +269,9 @@ 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() {
|
||||||
global.storage_path = "/var/lib/ffplayout/tv-media".to_string();
|
global.storage_root = "/var/lib/ffplayout/tv-media".to_string();
|
||||||
} else {
|
} else {
|
||||||
global.storage_path = storage
|
global.storage_root = storage
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string();
|
.to_string();
|
||||||
@ -281,9 +285,9 @@ 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 playlist.trim().is_empty() {
|
if playlist.trim().is_empty() {
|
||||||
global.playlist_path = "/var/lib/ffplayout/playlists".to_string();
|
global.playlist_root = "/var/lib/ffplayout/playlists".to_string();
|
||||||
} else {
|
} else {
|
||||||
global.playlist_path = playlist
|
global.playlist_root = playlist
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string();
|
.to_string();
|
||||||
@ -305,7 +309,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
.to_string();
|
.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("HLS path [/usr/share/ffplayout/public]: ");
|
print!("Public (HLS) path [/usr/share/ffplayout/public]: ");
|
||||||
stdout().flush().unwrap();
|
stdout().flush().unwrap();
|
||||||
|
|
||||||
stdin()
|
stdin()
|
||||||
@ -313,9 +317,9 @@ 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 hls.trim().is_empty() {
|
if hls.trim().is_empty() {
|
||||||
global.hls_path = "/usr/share/ffplayout/public".to_string();
|
global.public_root = "/usr/share/ffplayout/public".to_string();
|
||||||
} else {
|
} else {
|
||||||
global.hls_path = hls
|
global.public_root = hls
|
||||||
.trim()
|
.trim()
|
||||||
.trim_matches(|c| c == '"' || c == '\'')
|
.trim_matches(|c| c == '"' || c == '\'')
|
||||||
.to_string();
|
.to_string();
|
||||||
@ -335,13 +339,29 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
error_code = 1;
|
error_code = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !global.shared_storage {
|
let mut channel = handles::select_channel(pool, &1).await.unwrap();
|
||||||
let mut channel = handles::select_channel(pool, &1).await.unwrap();
|
channel.hls_path = global.public_root;
|
||||||
channel.preview_url = "http://127.0.0.1:8787/1/stream.m3u8".to_string();
|
channel.playlist_path = global.playlist_root;
|
||||||
|
channel.storage_path = global.storage_root;
|
||||||
|
|
||||||
handles::update_channel(pool, 1, channel).await.unwrap();
|
if global.shared_storage {
|
||||||
|
channel.preview_url = "http://127.0.0.1:8787/1/stream.m3u8".to_string();
|
||||||
|
channel.hls_path = Path::new(&channel.hls_path)
|
||||||
|
.join("1")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
channel.playlist_path = Path::new(&channel.playlist_path)
|
||||||
|
.join("1")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
channel.storage_path = Path::new(&channel.storage_path)
|
||||||
|
.join("1")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handles::update_channel(pool, 1, channel).await.unwrap();
|
||||||
|
|
||||||
if fix_permission {
|
if fix_permission {
|
||||||
let db_path = Path::new(db_path().unwrap()).with_extension("");
|
let db_path = Path::new(db_path().unwrap()).with_extension("");
|
||||||
let user = process_user.unwrap();
|
let user = process_user.unwrap();
|
||||||
@ -399,9 +419,9 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !args.init
|
if !args.init
|
||||||
&& args.storage_path.is_some()
|
&& args.storage_root.is_some()
|
||||||
&& args.playlist_path.is_some()
|
&& args.playlist_root.is_some()
|
||||||
&& args.hls_path.is_some()
|
&& args.public_root.is_some()
|
||||||
&& args.log_path.is_some()
|
&& args.log_path.is_some()
|
||||||
{
|
{
|
||||||
error_code = 0;
|
error_code = 0;
|
||||||
@ -409,18 +429,19 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
let global = GlobalSettings {
|
let global = GlobalSettings {
|
||||||
id: 0,
|
id: 0,
|
||||||
secret: None,
|
secret: None,
|
||||||
hls_path: args.hls_path.unwrap(),
|
|
||||||
playlist_path: args.playlist_path.unwrap(),
|
|
||||||
storage_path: args.storage_path.unwrap(),
|
|
||||||
logging_path: args.log_path.unwrap().to_string_lossy().to_string(),
|
logging_path: args.log_path.unwrap().to_string_lossy().to_string(),
|
||||||
|
playlist_root: args.playlist_root.unwrap(),
|
||||||
|
public_root: args.public_root.unwrap(),
|
||||||
|
storage_root: args.storage_root.unwrap(),
|
||||||
shared_storage: args.shared_storage,
|
shared_storage: args.shared_storage,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = handles::update_global(pool, global.clone()).await {
|
match handles::update_global(pool, global.clone()).await {
|
||||||
eprintln!("{e}");
|
Ok(_) => println!("Update global paths..."),
|
||||||
error_code = 1;
|
Err(e) => {
|
||||||
} else {
|
eprintln!("{e}");
|
||||||
println!("Update global paths...");
|
error_code = 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,10 +61,26 @@ pub async fn create_channel(
|
|||||||
queue: Arc<Mutex<Vec<Arc<Mutex<MailQueue>>>>>,
|
queue: Arc<Mutex<Vec<Arc<Mutex<MailQueue>>>>>,
|
||||||
target_channel: Channel,
|
target_channel: Channel,
|
||||||
) -> Result<Channel, ServiceError> {
|
) -> Result<Channel, ServiceError> {
|
||||||
|
let global = handles::select_global(conn).await?;
|
||||||
let mut channel = handles::insert_channel(conn, target_channel).await?;
|
let mut channel = handles::insert_channel(conn, target_channel).await?;
|
||||||
|
|
||||||
channel.preview_url = preview_url(&channel.preview_url, channel.id);
|
channel.preview_url = preview_url(&channel.preview_url, channel.id);
|
||||||
|
|
||||||
|
if global.shared_storage {
|
||||||
|
channel.hls_path = Path::new(&channel.hls_path)
|
||||||
|
.join(channel.id.to_string())
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
channel.playlist_path = Path::new(&channel.playlist_path)
|
||||||
|
.join(channel.id.to_string())
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
channel.storage_path = Path::new(&channel.storage_path)
|
||||||
|
.join(channel.id.to_string())
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
handles::update_channel(conn, channel.id, channel.clone()).await?;
|
handles::update_channel(conn, channel.id, channel.clone()).await?;
|
||||||
|
|
||||||
let output_param = "-c:v libx264 -crf 23 -x264-params keyint=50:min-keyint=25:scenecut=-1 -maxrate 1300k -bufsize 2600k -preset faster -tune zerolatency -profile:v Main -level 3.1 -c:a aac -ar 44100 -b:a 128k -flags +cgop -f hls -hls_time 6 -hls_list_size 600 -hls_flags append_list+delete_segments+omit_endlist -hls_segment_filename live/stream-%d.ts live/stream.m3u8".to_string();
|
let output_param = "-c:v libx264 -crf 23 -x264-params keyint=50:min-keyint=25:scenecut=-1 -maxrate 1300k -bufsize 2600k -preset faster -tune zerolatency -profile:v Main -level 3.1 -c:a aac -ar 44100 -b:a 128k -flags +cgop -f hls -hls_time 6 -hls_list_size 600 -hls_flags append_list+delete_segments+omit_endlist -hls_segment_filename live/stream-%d.ts live/stream.m3u8".to_string();
|
||||||
|
@ -26,12 +26,13 @@ pub const IMAGE_FORMAT: [&str; 21] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Some well known errors can be safely ignore
|
// Some well known errors can be safely ignore
|
||||||
pub const FFMPEG_IGNORE_ERRORS: [&str; 12] = [
|
pub const FFMPEG_IGNORE_ERRORS: [&str; 13] = [
|
||||||
"ac-tex damaged",
|
"ac-tex damaged",
|
||||||
"codec s302m, is muxed as a private data stream",
|
"codec s302m, is muxed as a private data stream",
|
||||||
"corrupt decoded frame in stream",
|
"corrupt decoded frame in stream",
|
||||||
"corrupt input packet in stream",
|
"corrupt input packet in stream",
|
||||||
"end mismatch left",
|
"end mismatch left",
|
||||||
|
"Invalid mb type in I-frame at",
|
||||||
"Packet corrupt",
|
"Packet corrupt",
|
||||||
"Referenced QT chapter track not found",
|
"Referenced QT chapter track not found",
|
||||||
"skipped MB in I-frame at",
|
"skipped MB in I-frame at",
|
||||||
@ -151,13 +152,13 @@ pub struct Source {
|
|||||||
pub paths: Vec<PathBuf>,
|
pub paths: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Global Config
|
/// Channel Config
|
||||||
///
|
///
|
||||||
/// This we init ones, when ffplayout is starting and use them globally in the hole program.
|
/// This we init ones, when ffplayout is starting and use them globally in the hole program.
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||||
pub struct PlayoutConfig {
|
pub struct PlayoutConfig {
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub global: Global,
|
pub channel: Channel,
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub advanced: AdvancedConfig,
|
pub advanced: AdvancedConfig,
|
||||||
pub general: General,
|
pub general: General,
|
||||||
@ -174,21 +175,21 @@ pub struct PlayoutConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||||
pub struct Global {
|
pub struct Channel {
|
||||||
|
pub logging_path: PathBuf,
|
||||||
pub hls_path: PathBuf,
|
pub hls_path: PathBuf,
|
||||||
pub playlist_path: PathBuf,
|
pub playlist_path: PathBuf,
|
||||||
pub storage_path: PathBuf,
|
pub storage_path: PathBuf,
|
||||||
pub logging_path: PathBuf,
|
|
||||||
pub shared_storage: bool,
|
pub shared_storage: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Global {
|
impl Channel {
|
||||||
pub fn new(config: &models::GlobalSettings) -> Self {
|
pub fn new(config: &models::GlobalSettings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
hls_path: PathBuf::from(config.hls_path.clone()),
|
|
||||||
playlist_path: PathBuf::from(config.playlist_path.clone()),
|
|
||||||
storage_path: PathBuf::from(config.storage_path.clone()),
|
|
||||||
logging_path: PathBuf::from(config.logging_path.clone()),
|
logging_path: PathBuf::from(config.logging_path.clone()),
|
||||||
|
hls_path: PathBuf::from(config.public_root.clone()),
|
||||||
|
playlist_path: PathBuf::from(config.playlist_root.clone()),
|
||||||
|
storage_path: PathBuf::from(config.storage_root.clone()),
|
||||||
shared_storage: config.shared_storage,
|
shared_storage: config.shared_storage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,7 +560,7 @@ impl PlayoutConfig {
|
|||||||
.await
|
.await
|
||||||
.expect("Can't read advanced config");
|
.expect("Can't read advanced config");
|
||||||
|
|
||||||
let mut global = Global::new(&global);
|
let mut channel = Channel::new(&global);
|
||||||
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(&config);
|
||||||
@ -571,42 +572,42 @@ impl PlayoutConfig {
|
|||||||
let task = Task::new(&config);
|
let task = Task::new(&config);
|
||||||
let mut output = Output::new(&config);
|
let mut output = Output::new(&config);
|
||||||
|
|
||||||
if !global.shared_storage {
|
if global.shared_storage {
|
||||||
global.storage_path = global.storage_path.join(channel_id.to_string());
|
channel.storage_path = channel.storage_path.join(channel_id.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !global.storage_path.is_dir() {
|
if !channel.storage_path.is_dir() {
|
||||||
tokio::fs::create_dir_all(&global.storage_path)
|
tokio::fs::create_dir_all(&channel.storage_path)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
panic!("Can't create storage folder: {:#?}", global.storage_path)
|
panic!("Can't create storage folder: {:#?}", channel.storage_path)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut storage = Storage::new(&config, global.storage_path.clone());
|
let mut storage = Storage::new(&config, channel.storage_path.clone());
|
||||||
|
|
||||||
if channel_id > 1 || !global.shared_storage {
|
if channel_id > 1 || !global.shared_storage {
|
||||||
global.playlist_path = global.playlist_path.join(channel_id.to_string());
|
channel.playlist_path = channel.playlist_path.join(channel_id.to_string());
|
||||||
global.hls_path = global.hls_path.join(channel_id.to_string());
|
channel.hls_path = channel.hls_path.join(channel_id.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !global.playlist_path.is_dir() {
|
if !channel.playlist_path.is_dir() {
|
||||||
tokio::fs::create_dir_all(&global.playlist_path)
|
tokio::fs::create_dir_all(&channel.playlist_path)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
panic!("Can't create playlist folder: {:#?}", global.playlist_path)
|
panic!("Can't create playlist folder: {:#?}", channel.playlist_path)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if !global.logging_path.is_dir() {
|
if !channel.logging_path.is_dir() {
|
||||||
tokio::fs::create_dir_all(&global.logging_path)
|
tokio::fs::create_dir_all(&channel.logging_path)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
panic!("Can't create logging folder: {:#?}", global.logging_path)
|
panic!("Can't create logging folder: {:#?}", channel.logging_path)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let (filler_path, _, _) = norm_abs_path(&global.storage_path, &config.storage_filler)
|
let (filler_path, _, _) = norm_abs_path(&channel.storage_path, &config.storage_filler)
|
||||||
.expect("Can't get filler path");
|
.expect("Can't get filler path");
|
||||||
|
|
||||||
storage.filler = filler_path;
|
storage.filler = filler_path;
|
||||||
@ -700,7 +701,7 @@ impl PlayoutConfig {
|
|||||||
|
|
||||||
for item in cmd.iter_mut() {
|
for item in cmd.iter_mut() {
|
||||||
if item.ends_with(".ts") || (item.ends_with(".m3u8") && item != "master.m3u8") {
|
if item.ends_with(".ts") || (item.ends_with(".m3u8") && item != "master.m3u8") {
|
||||||
if let Ok((hls_path, _, _)) = norm_abs_path(&global.hls_path, item) {
|
if let Ok((hls_path, _, _)) = norm_abs_path(&channel.hls_path, item) {
|
||||||
let parent = hls_path.parent().expect("HLS parent path");
|
let parent = hls_path.parent().expect("HLS parent path");
|
||||||
|
|
||||||
if !parent.is_dir() {
|
if !parent.is_dir() {
|
||||||
@ -728,7 +729,7 @@ impl PlayoutConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
global,
|
channel,
|
||||||
advanced,
|
advanced,
|
||||||
general,
|
general,
|
||||||
mail,
|
mail,
|
||||||
@ -749,7 +750,7 @@ impl PlayoutConfig {
|
|||||||
&config
|
&config
|
||||||
.storage
|
.storage
|
||||||
.filler
|
.filler
|
||||||
.strip_prefix(config.global.storage_path.clone())
|
.strip_prefix(config.channel.storage_path.clone())
|
||||||
.unwrap_or(&config.storage.filler)
|
.unwrap_or(&config.storage.filler)
|
||||||
.to_path_buf(),
|
.to_path_buf(),
|
||||||
);
|
);
|
||||||
@ -849,11 +850,11 @@ pub async fn get_config(pool: &Pool<Sqlite>, channel_id: i32) -> Result<PlayoutC
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(playlist) = args.playlist {
|
if let Some(playlist) = args.playlist {
|
||||||
config.global.playlist_path = playlist;
|
config.channel.playlist_path = playlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(folder) = args.folder {
|
if let Some(folder) = args.folder {
|
||||||
config.global.storage_path = folder;
|
config.channel.storage_path = folder;
|
||||||
config.processing.mode = ProcessMode::Folder;
|
config.processing.mode = ProcessMode::Folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,12 +116,12 @@ pub async fn browser(
|
|||||||
extensions.append(&mut channel_extensions);
|
extensions.append(&mut channel_extensions);
|
||||||
|
|
||||||
let (path, parent, path_component) =
|
let (path, parent, path_component) =
|
||||||
norm_abs_path(&config.global.storage_path, &path_obj.source)?;
|
norm_abs_path(&config.channel.storage_path, &path_obj.source)?;
|
||||||
|
|
||||||
let parent_path = if !path_component.is_empty() {
|
let parent_path = if !path_component.is_empty() {
|
||||||
path.parent().unwrap()
|
path.parent().unwrap()
|
||||||
} else {
|
} else {
|
||||||
&config.global.storage_path
|
&config.channel.storage_path
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut obj = PathObject::new(path_component, Some(parent));
|
let mut obj = PathObject::new(path_component, Some(parent));
|
||||||
@ -212,7 +212,7 @@ pub async fn create_directory(
|
|||||||
config: &PlayoutConfig,
|
config: &PlayoutConfig,
|
||||||
path_obj: &PathObject,
|
path_obj: &PathObject,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
let (path, _, _) = norm_abs_path(&config.global.storage_path, &path_obj.source)?;
|
let (path, _, _) = norm_abs_path(&config.channel.storage_path, &path_obj.source)?;
|
||||||
|
|
||||||
if let Err(e) = fs::create_dir_all(&path).await {
|
if let Err(e) = fs::create_dir_all(&path).await {
|
||||||
return Err(ServiceError::BadRequest(e.to_string()));
|
return Err(ServiceError::BadRequest(e.to_string()));
|
||||||
@ -281,8 +281,8 @@ pub async fn rename_file(
|
|||||||
config: &PlayoutConfig,
|
config: &PlayoutConfig,
|
||||||
move_object: &MoveObject,
|
move_object: &MoveObject,
|
||||||
) -> Result<MoveObject, ServiceError> {
|
) -> Result<MoveObject, ServiceError> {
|
||||||
let (source_path, _, _) = norm_abs_path(&config.global.storage_path, &move_object.source)?;
|
let (source_path, _, _) = norm_abs_path(&config.channel.storage_path, &move_object.source)?;
|
||||||
let (mut target_path, _, _) = norm_abs_path(&config.global.storage_path, &move_object.target)?;
|
let (mut target_path, _, _) = norm_abs_path(&config.channel.storage_path, &move_object.target)?;
|
||||||
|
|
||||||
if !source_path.exists() {
|
if !source_path.exists() {
|
||||||
return Err(ServiceError::BadRequest("Source file not exist!".into()));
|
return Err(ServiceError::BadRequest("Source file not exist!".into()));
|
||||||
@ -314,7 +314,7 @@ pub async fn remove_file_or_folder(
|
|||||||
config: &PlayoutConfig,
|
config: &PlayoutConfig,
|
||||||
source_path: &str,
|
source_path: &str,
|
||||||
) -> Result<(), ServiceError> {
|
) -> Result<(), ServiceError> {
|
||||||
let (source, _, _) = norm_abs_path(&config.global.storage_path, source_path)?;
|
let (source, _, _) = norm_abs_path(&config.channel.storage_path, source_path)?;
|
||||||
|
|
||||||
if !source.exists() {
|
if !source.exists() {
|
||||||
return Err(ServiceError::BadRequest("Source does not exists!".into()));
|
return Err(ServiceError::BadRequest("Source does not exists!".into()));
|
||||||
@ -346,7 +346,7 @@ pub async fn remove_file_or_folder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn valid_path(config: &PlayoutConfig, path: &str) -> Result<PathBuf, ServiceError> {
|
async fn valid_path(config: &PlayoutConfig, path: &str) -> Result<PathBuf, ServiceError> {
|
||||||
let (test_path, _, _) = norm_abs_path(&config.global.storage_path, path)?;
|
let (test_path, _, _) = norm_abs_path(&config.channel.storage_path, path)?;
|
||||||
|
|
||||||
if !test_path.is_dir() {
|
if !test_path.is_dir() {
|
||||||
return Err(ServiceError::BadRequest("Target folder not exists!".into()));
|
return Err(ServiceError::BadRequest("Target folder not exists!".into()));
|
||||||
|
@ -215,7 +215,7 @@ pub fn playlist_generator(manager: &ChannelManager) -> Result<Vec<JsonPlaylist>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let playlist_root = &config.global.playlist_path;
|
let playlist_root = &config.channel.playlist_path;
|
||||||
let mut playlists = vec![];
|
let mut playlists = vec![];
|
||||||
let mut date_range = vec![];
|
let mut date_range = vec![];
|
||||||
let mut from_template = false;
|
let mut from_template = false;
|
||||||
@ -224,7 +224,7 @@ pub fn playlist_generator(manager: &ChannelManager) -> Result<Vec<JsonPlaylist>,
|
|||||||
error!(
|
error!(
|
||||||
target: Target::all(), channel = id;
|
target: Target::all(), channel = id;
|
||||||
"Playlist folder <b><magenta>{:?}</></b> not exists!",
|
"Playlist folder <b><magenta>{:?}</></b> not exists!",
|
||||||
config.global.playlist_path
|
config.channel.playlist_path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ pub async fn read_playlist(
|
|||||||
date: String,
|
date: String,
|
||||||
) -> Result<JsonPlaylist, ServiceError> {
|
) -> Result<JsonPlaylist, ServiceError> {
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
let mut playlist_path = config.global.playlist_path.clone();
|
let mut playlist_path = config.channel.playlist_path.clone();
|
||||||
|
|
||||||
playlist_path = playlist_path
|
playlist_path = playlist_path
|
||||||
.join(d[0])
|
.join(d[0])
|
||||||
@ -34,7 +34,7 @@ pub async fn write_playlist(
|
|||||||
) -> Result<String, ServiceError> {
|
) -> Result<String, ServiceError> {
|
||||||
let date = json_data.date.clone();
|
let date = json_data.date.clone();
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
let mut playlist_path = config.global.playlist_path.clone();
|
let mut playlist_path = config.channel.playlist_path.clone();
|
||||||
|
|
||||||
if !playlist_path
|
if !playlist_path
|
||||||
.extension()
|
.extension()
|
||||||
@ -93,7 +93,7 @@ pub fn generate_playlist(manager: ChannelManager) -> Result<JsonPlaylist, Servic
|
|||||||
|
|
||||||
for path in &source.paths {
|
for path in &source.paths {
|
||||||
let (safe_path, _, _) =
|
let (safe_path, _, _) =
|
||||||
norm_abs_path(&config.global.storage_path, &path.to_string_lossy())?;
|
norm_abs_path(&config.channel.storage_path, &path.to_string_lossy())?;
|
||||||
paths.push(safe_path);
|
paths.push(safe_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ pub fn generate_playlist(manager: ChannelManager) -> Result<JsonPlaylist, Servic
|
|||||||
|
|
||||||
pub async fn delete_playlist(config: &PlayoutConfig, date: &str) -> Result<String, ServiceError> {
|
pub async fn delete_playlist(config: &PlayoutConfig, date: &str) -> Result<String, ServiceError> {
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
let mut playlist_path = PathBuf::from(&config.global.playlist_path);
|
let mut playlist_path = PathBuf::from(&config.channel.playlist_path);
|
||||||
|
|
||||||
playlist_path = playlist_path
|
playlist_path = playlist_path
|
||||||
.join(d[0])
|
.join(d[0])
|
||||||
|
@ -118,7 +118,7 @@ pub fn stat(config: PlayoutConfig) -> SystemStat {
|
|||||||
|
|
||||||
for disk in &*disks {
|
for disk in &*disks {
|
||||||
if disk.mount_point().to_string_lossy().len() > 1
|
if disk.mount_point().to_string_lossy().len() > 1
|
||||||
&& config.global.storage_path.starts_with(disk.mount_point())
|
&& config.channel.storage_path.starts_with(disk.mount_point())
|
||||||
{
|
{
|
||||||
storage.path = disk.name().to_string_lossy().to_string();
|
storage.path = disk.name().to_string_lossy().to_string();
|
||||||
storage.total = disk.total_space();
|
storage.total = disk.total_space();
|
||||||
|
2
frontend
2
frontend
@ -1 +1 @@
|
|||||||
Subproject commit 86b2b00e0e121f72ab7c2cc2fb28f2a110c4c762
|
Subproject commit e183320e13bd972d632b841bde722fa7bbed70e5
|
@ -5,11 +5,11 @@ CREATE TABLE
|
|||||||
global (
|
global (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
secret TEXT NOT NULL,
|
secret TEXT NOT NULL,
|
||||||
hls_path TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
|
|
||||||
logging_path TEXT NOT NULL DEFAULT "/var/log/ffplayout",
|
logging_path TEXT NOT NULL DEFAULT "/var/log/ffplayout",
|
||||||
playlist_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists",
|
playlist_root TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists",
|
||||||
storage_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
|
public_root TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
|
||||||
shared_storage INTEGER NOT NULL DEFAULT 1,
|
storage_root TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
|
||||||
|
shared_storage INTEGER NOT NULL DEFAULT 0,
|
||||||
UNIQUE (secret)
|
UNIQUE (secret)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -27,6 +27,9 @@ CREATE TABLE
|
|||||||
preview_url TEXT NOT NULL,
|
preview_url TEXT NOT NULL,
|
||||||
extra_extensions TEXT NOT NULL DEFAULT 'jpg,jpeg,png',
|
extra_extensions TEXT NOT NULL DEFAULT 'jpg,jpeg,png',
|
||||||
active INTEGER NOT NULL DEFAULT 0,
|
active INTEGER NOT NULL DEFAULT 0,
|
||||||
|
hls_path TEXT NOT NULL DEFAULT "/usr/share/ffplayout/public",
|
||||||
|
playlist_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/playlists",
|
||||||
|
storage_path TEXT NOT NULL DEFAULT "/var/lib/ffplayout/tv-media",
|
||||||
last_date TEXT,
|
last_date TEXT,
|
||||||
time_shift REAL NOT NULL DEFAULT 0
|
time_shift REAL NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
|
@ -104,7 +104,7 @@ fn test_generate_playlist_from_folder() {
|
|||||||
config.processing.mode = Playlist;
|
config.processing.mode = Playlist;
|
||||||
config.storage.filler = "assets/".into();
|
config.storage.filler = "assets/".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
|
|
||||||
let playlist = generate_playlist(manager);
|
let playlist = generate_playlist(manager);
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ fn test_generate_playlist_from_template() {
|
|||||||
config.processing.mode = Playlist;
|
config.processing.mode = Playlist;
|
||||||
config.storage.filler = "assets/".into();
|
config.storage.filler = "assets/".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
|
|
||||||
let playlist = generate_playlist(manager);
|
let playlist = generate_playlist(manager);
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ fn test_gen_source() {
|
|||||||
config.playlist.start_sec = Some(0.0);
|
config.playlist.start_sec = Some(0.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
|
|
||||||
let mut valid_source_with_probe = Media::new(0, "assets/media_mix/av_sync.mp4", true);
|
let mut valid_source_with_probe = Media::new(0, "assets/media_mix/av_sync.mp4", true);
|
||||||
@ -113,7 +113,7 @@ fn playlist_missing() {
|
|||||||
config.playlist.start_sec = Some(0.0);
|
config.playlist.start_sec = Some(0.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
@ -147,7 +147,7 @@ fn playlist_next_missing() {
|
|||||||
config.playlist.start_sec = Some(0.0);
|
config.playlist.start_sec = Some(0.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
@ -181,7 +181,7 @@ fn playlist_to_short() {
|
|||||||
config.playlist.start_sec = Some(21600.0);
|
config.playlist.start_sec = Some(21600.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
@ -215,7 +215,7 @@ fn playlist_init_after_list_end() {
|
|||||||
config.playlist.start_sec = Some(21600.0);
|
config.playlist.start_sec = Some(21600.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
@ -249,7 +249,7 @@ fn playlist_change_at_midnight() {
|
|||||||
config.playlist.start_sec = Some(0.0);
|
config.playlist.start_sec = Some(0.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
@ -283,7 +283,7 @@ fn playlist_change_before_midnight() {
|
|||||||
config.playlist.start_sec = Some(0.0);
|
config.playlist.start_sec = Some(0.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
@ -317,7 +317,7 @@ fn playlist_change_at_six() {
|
|||||||
config.playlist.start_sec = Some(21600.0);
|
config.playlist.start_sec = Some(21600.0);
|
||||||
config.playlist.length = "24:00:00".into();
|
config.playlist.length = "24:00:00".into();
|
||||||
config.playlist.length_sec = Some(86400.0);
|
config.playlist.length_sec = Some(86400.0);
|
||||||
config.global.playlist_path = "assets/playlists".into();
|
config.channel.playlist_path = "assets/playlists".into();
|
||||||
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
config.storage.filler = "assets/media_filler/filler_0.mp4".into();
|
||||||
config.output.mode = Null;
|
config.output.mode = Null;
|
||||||
config.output.output_count = 1;
|
config.output.output_count = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user