Merge pull request #745 from jb-alvarado/master

This commit is contained in:
jb-alvarado 2024-09-10 17:47:37 +02:00 committed by GitHub
commit b582186bff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 87 additions and 76 deletions

8
Cargo.lock generated
View File

@ -2924,9 +2924,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.35"
version = "0.38.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36"
dependencies = [
"bitflags 2.6.0",
"errno",
@ -3842,9 +3842,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.15"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
dependencies = [
"futures-core",
"pin-project-lite",

View File

@ -1,6 +1,6 @@
FROM alpine:latest
ARG FFPLAYOUT_VERSION=0.24.0-beta2
ARG FFPLAYOUT_VERSION=0.24.0-beta4
ARG SHARED_STORAGE=false
ENV DB=/db
@ -12,7 +12,7 @@ COPY <<-EOT /run.sh
#!/bin/sh
if [ ! -f /db/ffplayout.db ]; then
ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public-root "/public" --log-path "/logging" --shared-storage
ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public "/public" --log-path "/logging" --shared-storage
fi
/usr/bin/ffplayout -l "0.0.0.0:8787"

View File

@ -1,6 +1,6 @@
FROM alpine:latest
ARG FFPLAYOUT_VERSION=0.24.0-beta2
ARG FFPLAYOUT_VERSION=0.24.0-beta4
ARG SHARED_STORAGE=false
ENV DB=/db
@ -14,7 +14,7 @@ COPY <<-EOT /run.sh
#!/bin/sh
if [ ! -f /db/ffplayout.db ]; then
ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public-root "/public" --log-path "/logging" --shared-storage
ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public "/public" --log-path "/logging" --shared-storage
fi
/usr/bin/ffplayout -l "0.0.0.0:8787"

View File

@ -1,6 +1,6 @@
FROM nvidia/cuda:12.5.0-runtime-rockylinux9
ARG FFPLAYOUT_VERSION=0.24.0-beta2
ARG FFPLAYOUT_VERSION=0.24.0-beta4
ARG SHARED_STORAGE=false
ENV DB=/db
@ -204,7 +204,7 @@ COPY <<-EOT /run.sh
#!/bin/sh
if [ ! -f /db/ffplayout.db ]; then
ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public-root "/public" --log-path "/logging" --shared-storage
ffplayout -u admin -p admin -m contact@example.com --storage-root "/tv-media" --playlist-root "/playlists" --public "/public" --log-path "/logging" --shared-storage
fi
/usr/bin/ffplayout -l "0.0.0.0:8787"

View File

@ -52,10 +52,10 @@ use crate::utils::{
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
public_path, read_log_file, system, TextFilter,
};
use crate::vec_strings;
use crate::{
api::auth::{create_jwt, Claims},
utils::advanced_config::AdvancedConfig,
vec_strings,
};
use crate::{
db::{
@ -1309,24 +1309,21 @@ async fn get_file(
/// Can be used for HLS Playlist and other static files in public folder
///
/// ```BASH
/// curl -X GET http://127.0.0.1:8787/live/1/stream.m3u8
/// curl -X GET http://127.0.0.1:8787/1/live/stream.m3u8
/// ```
#[get("/{public:live|preview|public}/{id}/{file_stem:.*}")]
#[get("/{id}/{public:live|preview|public}/{file_stem:.*}")]
async fn get_public(
path: web::Path<(String, i32, String)>,
path: web::Path<(i32, String, String)>,
controllers: web::Data<Mutex<ChannelController>>,
) -> Result<actix_files::NamedFile, ServiceError> {
let (public, id, file_stem) = path.into_inner();
let public_path = public_path();
let (id, public, file_stem) = path.into_inner();
let absolute_path = if file_stem.ends_with(".ts") || file_stem.ends_with(".m3u8") {
let manager = controllers.lock().unwrap().get(id).unwrap();
let config = manager.config.lock().unwrap();
config.channel.hls_path.join(public)
} else if public_path.is_absolute() {
public_path.to_path_buf()
} else {
env::current_dir()?.join(public_path)
public_path()
}
.clean();

View File

@ -1,6 +1,5 @@
use std::{
collections::HashSet,
env,
fs::File,
io,
process::exit,
@ -8,16 +7,16 @@ use std::{
thread,
};
use actix_files::Files;
use actix_web::{middleware::Logger, web, App, HttpServer};
use actix_web_httpauth::middleware::HttpAuthentication;
#[cfg(any(debug_assertions, not(feature = "embed_frontend")))]
use actix_files::Files;
#[cfg(all(not(debug_assertions), feature = "embed_frontend"))]
use actix_web_static_files::ResourceFiles;
use log::*;
use path_clean::PathClean;
use ffplayout::{
api::routes::*,
@ -105,12 +104,14 @@ async fn main() -> std::io::Result<()> {
info!("Running ffplayout API, listen on http://{conn}");
let db_clone = pool.clone();
// no 'allow origin' here, give it to the reverse proxy
HttpServer::new(move || {
let queues = mail_queues.clone();
let auth = HttpAuthentication::bearer(validator);
let db_pool = web::Data::new(pool.clone());
let db_pool = web::Data::new(db_clone.clone());
// Customize logging format to get IP though proxies.
let logger = Logger::new("%{r}a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T")
.exclude_regex(r"/_nuxt/*");
@ -171,18 +172,7 @@ async fn main() -> std::io::Result<()> {
)
.service(get_file);
if let Some(public) = &ARGS.public {
// When public path is set as argument use this path for serving extra static files,
// is useful for HLS stream etc.
let absolute_path = if public.is_absolute() {
public.to_path_buf()
} else {
env::current_dir().unwrap_or_default().join(public)
}
.clean();
web_app = web_app.service(Files::new("/", absolute_path));
} else {
if ARGS.public.is_none() {
// When no public path is given as argument, use predefine keywords in path,
// like /live; /preview; /public, or HLS extensions to recognize file should get from public folder
web_app = web_app.service(get_public);
@ -283,5 +273,7 @@ async fn main() -> std::io::Result<()> {
channel_ctl.stop_all();
}
pool.close().await;
Ok(())
}

View File

@ -264,7 +264,7 @@ impl ChannelManager {
self.run_count.fetch_sub(1, Ordering::SeqCst);
let pool = self.db_pool.clone().unwrap();
let channel_id = self.channel.lock().unwrap().id;
debug!(target: Target::all(), channel = channel_id; "Stop all child processes from channel {channel_id}");
debug!(target: Target::all(), channel = channel_id; "Deactivate playout and stop all child processes from channel: <yellow>{channel_id}</>");
if let Err(e) = handles::update_player(&pool, channel_id, false).await {
error!(target: Target::all(), channel = channel_id; "Unable write to player status: {e}");
@ -273,7 +273,7 @@ impl ChannelManager {
for unit in [Decoder, Encoder, Ingest] {
if let Err(e) = self.stop(unit) {
if !e.to_string().contains("exited process") {
error!("{e}")
error!(target: Target::all(), channel = channel_id; "{e}")
}
}
}
@ -286,7 +286,7 @@ impl ChannelManager {
self.ingest_is_running.store(false, Ordering::SeqCst);
self.run_count.fetch_sub(1, Ordering::SeqCst);
let channel_id = self.channel.lock().unwrap().id;
debug!(target: Target::all(), channel = channel_id; "Stop all child processes from channel {channel_id}");
debug!(target: Target::all(), channel = channel_id; "Stop all child processes from channel: <yellow>{channel_id}</>");
for unit in [Decoder, Encoder, Ingest] {
if let Err(e) = self.stop(unit) {

View File

@ -356,12 +356,23 @@ impl CurrentProgram {
}
fn recalculate_begin(&mut self, extend: bool) {
debug!(target: Target::file_mail(), channel = self.id; "Infinit playlist reaches end, recalculate clip begins.");
debug!(target: Target::file_mail(), channel = self.id; "Infinit playlist reaches end, recalculate clip begins. Extend: <yellow>{extend}</>");
let mut time_sec = time_in_seconds();
if extend {
time_sec = self.start_sec + self.json_playlist.length.unwrap();
// Calculate the elapsed time since the playlist start
let elapsed_sec = if time_sec >= self.start_sec {
time_sec - self.start_sec
} else {
time_sec + 86400.0 - self.start_sec
};
// Time passed within the current playlist loop
let time_in_current_loop = elapsed_sec % self.json_playlist.length.unwrap();
// Adjust the start time so that the playlist starts at the correct point in time
time_sec -= time_in_current_loop;
}
self.json_playlist.start_sec = Some(time_sec);

View File

@ -86,9 +86,6 @@ pub struct Args {
#[clap(long, help = "List available channel ids")]
pub list_channels: bool,
#[clap(long, env, help = "path to public files")]
pub public: Option<PathBuf>,
#[clap(short, env, long, help = "Listen on IP:PORT, like: 127.0.0.1:8787")]
pub listen: Option<String>,
@ -123,8 +120,8 @@ pub struct Args {
#[clap(long, env, help = "Log to console")]
pub log_to_console: bool,
#[clap(long, env, help = "Public (HLS) output path")]
pub public_root: Option<String>,
#[clap(long, env, help = "Path to public files, also HLS playlists")]
pub public: Option<String>,
#[clap(long, env, help = "Playlist root path")]
pub playlist_root: Option<String>,
@ -438,7 +435,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
if !args.init
&& args.storage_root.is_some()
&& args.playlist_root.is_some()
&& args.public_root.is_some()
&& args.public.is_some()
&& args.log_path.is_some()
{
error_code = 0;
@ -448,7 +445,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
secret: None,
logging_path: args.log_path.unwrap().to_string_lossy().to_string(),
playlist_root: args.playlist_root.unwrap(),
public_root: args.public_root.unwrap(),
public_root: args.public.unwrap(),
storage_root: args.storage_root.unwrap(),
shared_storage: args.shared_storage,
};
@ -523,19 +520,6 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
};
}
if let Some(id) = ARGS.dump_config {
match PlayoutConfig::dump(pool, id).await {
Ok(_) => {
println!("Dump config to: ffplayout_{id}.toml");
error_code = 0;
}
Err(e) => {
eprintln!("Dump config: {e}");
error_code = 1;
}
};
}
if let Some(id) = ARGS.dump_advanced {
match AdvancedConfig::dump(pool, id).await {
Ok(_) => {

View File

@ -407,14 +407,14 @@ impl Playlist {
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct Storage {
pub help_text: String,
#[serde(skip_deserializing)]
#[serde(skip_serializing, skip_deserializing)]
pub path: PathBuf,
#[serde(skip_serializing, skip_deserializing)]
pub paths: Vec<PathBuf>,
pub filler: PathBuf,
pub extensions: Vec<String>,
pub shuffle: bool,
#[serde(skip_deserializing)]
#[serde(skip_serializing, skip_deserializing)]
pub shared_storage: bool,
}
@ -603,10 +603,14 @@ impl PlayoutConfig {
playlist.length_sec = Some(86400.0);
}
if processing.add_logo && !Path::new(&processing.logo).is_file() {
let (logo_path, _, _) = norm_abs_path(&channel.storage_path, &processing.logo)?;
if processing.add_logo && !logo_path.is_file() {
processing.add_logo = false;
}
processing.logo = logo_path.to_string_lossy().to_string();
if processing.audio_tracks < 1 {
processing.audio_tracks = 1
}
@ -711,6 +715,9 @@ impl PlayoutConfig {
text.node_pos = None;
}
let (text_path, _, _) = norm_abs_path(&channel.storage_path, &text.fontfile)?;
text.fontfile = text_path.to_string_lossy().to_string();
Ok(Self {
channel,
advanced,
@ -859,6 +866,12 @@ pub async fn get_config(
}
}
if args.shared_storage {
// config.channel.shared_storage could be true already,
// so should not be overridden with false when args.shared_storage is not set
config.channel.shared_storage = args.shared_storage
}
if let Some(volume) = args.volume {
config.processing.volume = volume;
}

View File

@ -305,6 +305,10 @@ pub fn log_file_path() -> PathBuf {
.clone()
.unwrap_or(PathBuf::from(&config.logging_path));
if !log_path.is_absolute() {
log_path = env::current_dir().unwrap().join(log_path);
}
if !log_path.is_dir() {
log_path = env::current_dir().unwrap();
}

View File

@ -30,6 +30,7 @@ pub mod playlist;
pub mod system;
pub mod task_runner;
use crate::db::models::GlobalSettings;
use crate::player::utils::time_to_sec;
use crate::utils::{errors::ServiceError, logging::log_file_path};
use crate::ARGS;
@ -195,19 +196,28 @@ pub fn db_path() -> Result<&'static str, Box<dyn std::error::Error>> {
}
pub fn public_path() -> PathBuf {
let path = PathBuf::from("./ffplayout-frontend/.output/public/");
let config = GlobalSettings::global();
let dev_path = env::current_dir()
.unwrap_or_default()
.join("frontend/.output/public/");
let mut public_path = PathBuf::from(&config.public_root);
if cfg!(debug_assertions) && path.is_dir() {
return path;
if let Some(p) = &ARGS.public {
// When public path is set as argument use this path for serving static files.
// Works only when feature embed_frontend is not set.
let public = PathBuf::from(p);
public_path = if public.is_absolute() {
public.to_path_buf()
} else {
env::current_dir().unwrap_or_default().join(public)
}
.clean();
} else if cfg!(debug_assertions) && dev_path.is_dir() {
public_path = dev_path;
}
let path = PathBuf::from("/usr/share/ffplayout/public/");
if path.is_dir() {
return path;
}
PathBuf::from("./public/")
public_path
}
pub async fn read_log_file(channel_id: &i32, date: &str) -> Result<String, ServiceError> {

@ -1 +1 @@
Subproject commit 48f123bf6ad136968495e9e5e22249b8ca5ef192
Subproject commit bb7446850c683c3a4465c336e348476d3c8bb49c

View File

@ -188,7 +188,7 @@ INSERT INTO
VALUES
(
'Channel 1',
'http://127.0.0.1:8787/live/1/stream.m3u8',
'http://127.0.0.1:8787/1/live/stream.m3u8',
'jpg,jpeg,png',
0
);