commit
fc609c86d0
@ -12,7 +12,7 @@ COPY <<-EOT /run.sh
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -f /db/ffplayout.db ]; then
|
||||
ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared
|
||||
ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlists "/playlists" --public "/public" --logs "/logging" --mail-smtp "mail.example.org" --mail-user "admin@example.org" --mail-password "abcd" --mail-starttls
|
||||
fi
|
||||
|
||||
/usr/bin/ffplayout -l "0.0.0.0:8787"
|
||||
@ -28,6 +28,8 @@ RUN [[ -f "/tmp/ffplayout-v${FFPLAYOUT_VERSION}_x86_64-unknown-linux-musl.tar.gz
|
||||
cd /tmp && \
|
||||
tar xf "ffplayout-v${FFPLAYOUT_VERSION}_x86_64-unknown-linux-musl.tar.gz" && \
|
||||
cp ffplayout /usr/bin/ && \
|
||||
mkdir -p /usr/share/ffplayout/ && \
|
||||
cp assets/dummy.vtt assets/logo.png assets/DejaVuSans.ttf assets/FONT_LICENSE.txt /usr/share/ffplayout/ && \
|
||||
rm -rf /tmp/* && \
|
||||
mkdir ${DB}
|
||||
|
||||
|
@ -22,7 +22,7 @@ How to build the image:\
|
||||
# build default
|
||||
docker build -t ffplayout-image .
|
||||
|
||||
# build from root folder, to copy local *.rpm package
|
||||
# build from root folder, to copy *.tar.gz with self compiled binary
|
||||
docker build -f docker/Dockerfile -t ffplayout-image .
|
||||
|
||||
# build ffmpeg from source
|
||||
@ -45,6 +45,14 @@ docker run -d --name ffplayout -p 8787:8787 ffplayout-image
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
For setup mail server settings run:
|
||||
|
||||
```
|
||||
docker exec -it ffplayout ffplayout -i
|
||||
```
|
||||
|
||||
Then restart Container
|
||||
|
||||
#### Note from CentOS docker hub page
|
||||
There have been reports that if you're using an Ubuntu host, you will need to add `-v /tmp/$(mktemp -d):/run` to the mount.
|
||||
|
||||
|
@ -14,7 +14,7 @@ COPY <<-EOT /run.sh
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -f /db/ffplayout.db ]; then
|
||||
ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared
|
||||
ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlists "/playlists" --public "/public" --logs "/logging" --mail-smtp "mail.example.org" --mail-user "admin@example.org" --mail-password "abcd" --mail-starttls
|
||||
fi
|
||||
|
||||
/usr/bin/ffplayout -l "0.0.0.0:8787"
|
||||
@ -30,6 +30,8 @@ RUN [[ -f "/tmp/ffplayout-v${FFPLAYOUT_VERSION}_x86_64-unknown-linux-musl.tar.gz
|
||||
cd /tmp && \
|
||||
tar xf "ffplayout-v${FFPLAYOUT_VERSION}_x86_64-unknown-linux-musl.tar.gz" && \
|
||||
cp ffplayout /usr/bin/ && \
|
||||
mkdir -p /usr/share/ffplayout/ && \
|
||||
cp assets/dummy.vtt assets/logo.png assets/DejaVuSans.ttf assets/FONT_LICENSE.txt /usr/share/ffplayout/ && \
|
||||
rm -rf /tmp/* && \
|
||||
mkdir ${DB}
|
||||
|
||||
|
@ -204,7 +204,7 @@ COPY <<-EOT /run.sh
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -f /db/ffplayout.db ]; then
|
||||
ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlist "/playlists" --public "/public" --log-path "/logging" --shared
|
||||
ffplayout -i -u admin -p admin -m contact@example.com --storage "/tv-media" --playlists "/playlists" --public "/public" --logs "/logging" --mail-smtp "mail.example.org" --mail-user "admin@example.org" --mail-password "abcd" --mail-starttls
|
||||
fi
|
||||
|
||||
/usr/bin/ffplayout -l "0.0.0.0:8787"
|
||||
@ -217,6 +217,8 @@ RUN [[ -f "/tmp/ffplayout-v${FFPLAYOUT_VERSION}_x86_64-unknown-linux-musl.tar.gz
|
||||
cd /tmp && \
|
||||
tar xf "ffplayout-v${FFPLAYOUT_VERSION}_x86_64-unknown-linux-musl.tar.gz" && \
|
||||
cp ffplayout /usr/bin/ && \
|
||||
mmkdir -p /usr/share/ffplayout/ && \
|
||||
cp assets/dummy.vtt assets/logo.png assets/DejaVuSans.ttf assets/FONT_LICENSE.txt /usr/share/ffplayout/ && \
|
||||
rm -rf /tmp/* && \
|
||||
mkdir ${DB}
|
||||
|
||||
|
@ -29,7 +29,7 @@ The ffplayout engine has no special preview config parameters, but you can add y
|
||||
...
|
||||
```
|
||||
|
||||
In this documentation, we assume that you are using [ffplayout-frontend](https://github.com/ffplayout/ffplayout-frontend) and that you are using [SRS](https://github.com/ossrs/srs) at least for the preview stream. The most stable solution is previewing over HLS, but it is also possible to use [HTTP-FLV](https://github.com/ossrs/srs/wiki/v4_EN_DeliveryHttpStream) for lower latency.
|
||||
In this documentation, we assume that you are using [SRS](https://github.com/ossrs/srs) at least for the preview stream. The most stable solution is previewing over HLS, but it is also possible to use [HTTP-FLV](https://github.com/ossrs/srs/wiki/v4_EN_DeliveryHttpStream) for lower latency.
|
||||
|
||||
To get this working, we need to follow some steps.
|
||||
|
||||
|
@ -9,7 +9,10 @@ use tokio::task;
|
||||
|
||||
use super::models::{AdvancedConfiguration, Configuration};
|
||||
use crate::db::models::{Channel, GlobalSettings, Role, TextPreset, User};
|
||||
use crate::utils::{advanced_config::AdvancedConfig, config::PlayoutConfig, local_utc_offset};
|
||||
use crate::utils::{
|
||||
advanced_config::AdvancedConfig, config::PlayoutConfig, is_running_in_container,
|
||||
local_utc_offset,
|
||||
};
|
||||
|
||||
pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
sqlx::migrate!("../migrations").run(conn).await?;
|
||||
@ -20,6 +23,7 @@ pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<(), Box<dyn std::error::E
|
||||
.take(80)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
let shared = is_running_in_container().await;
|
||||
|
||||
let query = "CREATE TRIGGER global_row_count
|
||||
BEFORE INSERT ON global
|
||||
@ -27,9 +31,13 @@ pub async fn db_migrate(conn: &Pool<Sqlite>) -> Result<(), Box<dyn std::error::E
|
||||
BEGIN
|
||||
SELECT RAISE(FAIL, 'Database is already initialized!');
|
||||
END;
|
||||
INSERT INTO global(secret) VALUES($1);";
|
||||
INSERT INTO global(secret, shared) VALUES($1, $2);";
|
||||
|
||||
sqlx::query(query).bind(secret).execute(conn).await?;
|
||||
sqlx::query(query)
|
||||
.bind(secret)
|
||||
.bind(shared)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -46,9 +54,8 @@ pub async fn update_global(
|
||||
conn: &Pool<Sqlite>,
|
||||
global: GlobalSettings,
|
||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
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";
|
||||
let query = "UPDATE global SET logs = $2, playlists = $3, public = $4, storage = $5,
|
||||
mail_smtp = $6, mail_user = $7, mail_password = $8, mail_starttls = $9 WHERE id = 1";
|
||||
|
||||
sqlx::query(query)
|
||||
.bind(global.id)
|
||||
@ -56,7 +63,6 @@ pub async fn update_global(
|
||||
.bind(global.playlists)
|
||||
.bind(global.public)
|
||||
.bind(global.storage)
|
||||
.bind(global.shared)
|
||||
.bind(global.mail_smtp)
|
||||
.bind(global.mail_user)
|
||||
.bind(global.mail_password)
|
||||
|
@ -71,14 +71,6 @@ pub struct Args {
|
||||
#[clap(long, env, help_heading = Some("Initial Setup"), help = "Use TLS for system mails")]
|
||||
pub mail_starttls: bool,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
env,
|
||||
help_heading = Some("Initial Setup"),
|
||||
help = "Share storage across channels, important for running in Containers"
|
||||
)]
|
||||
pub shared: bool,
|
||||
|
||||
#[clap(long, env, help_heading = Some("Initial Setup / General"), help = "Logging path")]
|
||||
pub logs: Option<String>,
|
||||
|
||||
@ -251,7 +243,6 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
||||
let mut playlist = String::new();
|
||||
let mut logging = String::new();
|
||||
let mut public = String::new();
|
||||
let mut shared_store = String::new();
|
||||
let mut mail_smtp = String::new();
|
||||
let mut mail_user = String::new();
|
||||
let mut mail_starttls = String::new();
|
||||
@ -329,23 +320,6 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
||||
}
|
||||
}
|
||||
|
||||
if args.shared {
|
||||
global.shared = true;
|
||||
} else {
|
||||
print!(
|
||||
"Shared storage [{}]: ",
|
||||
if global.shared { "yes" } else { "no" }
|
||||
);
|
||||
stdout().flush().unwrap();
|
||||
stdin()
|
||||
.read_line(&mut shared_store)
|
||||
.expect("Did not enter a yes or no?");
|
||||
|
||||
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 {
|
||||
|
@ -35,14 +35,8 @@ pub async fn create_channel(
|
||||
target_channel: Channel,
|
||||
) -> Result<Channel, ServiceError> {
|
||||
let channel = handles::insert_channel(conn, target_channel).await?;
|
||||
let storage_path = PathBuf::from(channel.storage.clone());
|
||||
|
||||
handles::new_channel_presets(conn, channel.id).await?;
|
||||
|
||||
if let Err(e) = copy_assets(&storage_path).await {
|
||||
error!("{e}");
|
||||
};
|
||||
|
||||
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();
|
||||
@ -52,7 +46,11 @@ pub async fn create_channel(
|
||||
|
||||
let config = get_config(conn, channel.id).await?;
|
||||
let m_queue = Arc::new(Mutex::new(MailQueue::new(channel.id, config.mail.clone())));
|
||||
let manager = ChannelManager::new(Some(conn.clone()), channel.clone(), config);
|
||||
let manager = ChannelManager::new(Some(conn.clone()), channel.clone(), config.clone());
|
||||
|
||||
if let Err(e) = copy_assets(&PathBuf::from(&config.storage.path)).await {
|
||||
error!("{e}");
|
||||
};
|
||||
|
||||
controllers
|
||||
.lock()
|
||||
|
@ -162,7 +162,6 @@ pub struct Source {
|
||||
/// Channel Config
|
||||
///
|
||||
/// This we init ones, when ffplayout is starting and use them globally in the hole program.
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, TS)]
|
||||
#[ts(export, export_to = "playout_config.d.ts")]
|
||||
pub struct PlayoutConfig {
|
||||
@ -624,7 +623,7 @@ impl PlayoutConfig {
|
||||
.unwrap_or_else(|_| panic!("Can't create storage folder: {:#?}", channel.storage));
|
||||
}
|
||||
|
||||
let mut storage = Storage::new(&config, channel.storage.clone(), global.shared);
|
||||
let mut storage = Storage::new(&config, channel.storage.clone(), channel.shared);
|
||||
|
||||
if !channel.playlists.is_dir() {
|
||||
tokio::fs::create_dir_all(&channel.playlists).await?;
|
||||
@ -945,12 +944,6 @@ pub async fn get_config(
|
||||
}
|
||||
}
|
||||
|
||||
if args.shared {
|
||||
// config.channel.shared could be true already,
|
||||
// so should not be overridden with false when args.shared is not set
|
||||
config.channel.shared = true
|
||||
}
|
||||
|
||||
if let Some(volume) = args.volume {
|
||||
config.processing.volume = volume;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use log::*;
|
||||
use path_clean::PathClean;
|
||||
use rand::Rng;
|
||||
use regex::Regex;
|
||||
use tokio::fs;
|
||||
use tokio::{fs, process::Command};
|
||||
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
@ -336,7 +336,7 @@ pub async fn copy_assets(storage_path: &Path) -> Result<(), std::io::Error> {
|
||||
let font_target = target.join("DejaVuSans.ttf");
|
||||
let logo_target = target.join("logo.png");
|
||||
|
||||
fs::create_dir(&target).await?;
|
||||
fs::create_dir_all(&target).await?;
|
||||
fs::copy(&dummy_source, &dummy_target).await?;
|
||||
fs::copy(&font_source, &font_target).await?;
|
||||
fs::copy(&logo_source, &logo_target).await?;
|
||||
@ -365,7 +365,25 @@ pub async fn copy_assets(storage_path: &Path) -> Result<(), std::io::Error> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Storage path {storage_path:?} not exists!");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Combined function to check if the program is running inside a container.
|
||||
/// Returns `true` if running inside a container, otherwise `false`.
|
||||
pub async fn is_running_in_container() -> bool {
|
||||
// Check for Docker or Podman specific files
|
||||
if Path::new("/.dockerenv").exists() || Path::new("/run/.containerenv").exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Run `systemd-detect-virt -c` to check if we are in a container
|
||||
if let Ok(output) = Command::new("systemd-detect-virt").arg("-c").output().await {
|
||||
return output.status.success();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
@ -36,10 +36,12 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: -c:v mpeg2video -g 1 -b:v 57600k -minrate 57600k -maxrate 57600k -bufsize 28800k
|
||||
-mpegts_flags initial_discontinuity -c:a s302m -strict -2 -sample_fmt s16 -ar 48000 -ac 2 -f
|
||||
mpegts
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default:
|
||||
<span class="select-text cursor-text">
|
||||
-c:v mpeg2video -g 1 -b:v 57600k -minrate 57600k -maxrate 57600k -bufsize 28800k
|
||||
-mpegts_flags initial_discontinuity
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -87,7 +89,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">Default: yadif=0:-1:0 </span>
|
||||
<span class="text-sm text-base-content/80"
|
||||
>Default: <span class="select-text cursor-text">yadif=0:-1:0 </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -101,7 +105,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: scale={}:-1 </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">scale={}:-1 </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -115,7 +121,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: scale=-1:{} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">scale=-1:{} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -129,8 +137,11 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: pad=max(iw\\,ih*({0}/{1})):ow/({0}/{1}):(ow-iw)/2:(oh-ih)/2
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default:
|
||||
<span class="select-text cursor-text"
|
||||
>pad=max(iw\\,ih*({0}/{1})):ow/({0}/{1}):(ow-iw)/2:(oh-ih)/2</span
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -145,7 +156,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: fps={} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">fps={} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -159,7 +172,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: scale={}:{} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">scale={}:{} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -173,7 +188,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: setdar=dar={} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">setdar=dar={} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -187,7 +204,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: fade=in:st=0:d=0.5 </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">fade=in:st=0:d=0.5 </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -201,7 +220,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: fade=out:st={}:d=1.0 </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">fade=out:st={}:d=1.0 </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -215,7 +236,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: scale={} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">scale={} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -229,8 +252,8 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: fade=in:st=0:d=1.0:alpha=1
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">fade=in:st=0:d=1.0:alpha=1</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -245,8 +268,8 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: fade=out:st={}:d=1.0:alpha=1
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">fade=out:st={}:d=1.0:alpha=1</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -261,8 +284,8 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: null[l];[v][l]overlay={}:shortest=1
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">null[l];[v][l]overlay={}:shortest=1</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -277,8 +300,8 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: tpad=stop_mode=add:stop_duration={}
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">tpad=stop_mode=add:stop_duration={}</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -293,7 +316,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: drawtext=text='{}':{}{} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">drawtext=text='{}':{}{} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -307,8 +332,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80">
|
||||
Default: zmq=b=tcp\\\\://'{}',drawtext@dyntext={}
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default:
|
||||
<span class="select-text cursor-text">zmq=b=tcp\\\\://'{}',drawtext@dyntext={}</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@ -323,7 +349,7 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80 break-all">
|
||||
<span class="text-sm text-base-content/80 break-all">
|
||||
Default: aevalsrc=0:channel_layout=stereo:duration={}:sample_rate=48000
|
||||
</span>
|
||||
</div>
|
||||
@ -339,7 +365,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: afade=in:st=0:d=0.5 </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">afade=in:st=0:d=0.5 </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -353,7 +381,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: afade=out:st={}:d=1.0 </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">afade=out:st={}:d=1.0 </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -367,7 +397,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: apad=whole_dur={} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">apad=whole_dur={} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -381,7 +413,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: volume={} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">volume={} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -395,7 +429,9 @@
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<div class="label">
|
||||
<span class="text-sm select-text text-base-content/80"> Default: split={}{} </span>
|
||||
<span class="text-sm text-base-content/80">
|
||||
Default: <span class="select-text cursor-text">split={}{} </span></span
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -116,6 +116,7 @@ const { t } = useI18n()
|
||||
|
||||
const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const mediaStore = useMedia()
|
||||
const indexStore = useIndex()
|
||||
const { i } = storeToRefs(useConfig())
|
||||
|
||||
@ -217,8 +218,10 @@ async function addUpdateChannel() {
|
||||
await updateChannel()
|
||||
}
|
||||
|
||||
await configStore.getAdvancedConfig()
|
||||
await configStore.getPlayoutConfig()
|
||||
await configStore.getUserConfig()
|
||||
await mediaStore.getTree('')
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,8 +243,10 @@ async function deleteChannel() {
|
||||
|
||||
i.value = configStore.i - 1
|
||||
await configStore.getChannelConfig()
|
||||
await configStore.getAdvancedConfig()
|
||||
await configStore.getPlayoutConfig()
|
||||
await configStore.getUserConfig()
|
||||
await mediaStore.getTree('')
|
||||
|
||||
if (response.status === 200) {
|
||||
indexStore.msgAlert('success', t('config.errorChannelDelete'), 2)
|
||||
|
@ -162,6 +162,7 @@ function logout() {
|
||||
|
||||
function selectChannel(index: number) {
|
||||
configStore.i = index
|
||||
configStore.getAdvancedConfig()
|
||||
configStore.getPlayoutConfig()
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ export default {
|
||||
publicPath: 'Public (HLS) Pfad',
|
||||
playlistPath: 'Wiedergabelistenpfad',
|
||||
storagePath: 'Speicherpfad',
|
||||
sharedStorage: 'Gemeinsamer Speicher ist aktiviert, verwende denselben Speicherstamm für alle Kanäle!',
|
||||
sharedStorage: 'ffplayout läuft innerhalb eines Containers, verwenden Sie den gleichen Speicherstamm für alle Kanäle!',
|
||||
},
|
||||
user: {
|
||||
title: 'Benutzer-Konfiguration',
|
||||
|
@ -227,7 +227,7 @@ export default {
|
||||
publicPath: 'Public (HLS) Path',
|
||||
playlistPath: 'Playlist Path',
|
||||
storagePath: 'Storage Path',
|
||||
sharedStorage: 'Shared storage is enabled, use the same storage root for all channels!',
|
||||
sharedStorage: 'ffplayout runs inside a container, use the same storage root for all channels!',
|
||||
},
|
||||
user: {
|
||||
title: 'User Configuration',
|
||||
|
@ -226,7 +226,7 @@ export default {
|
||||
publicPath: 'Public (HLS) Path',
|
||||
playlistPath: 'Playlist Path',
|
||||
storagePath: 'Storage Path',
|
||||
sharedStorage: 'O armazenamento compartilhado está ativado, use a mesma raiz de armazenamento para todos os canais',
|
||||
sharedStorage: 'O ffplayout é executado dentro de um contêiner; use a mesma raiz de armazenamento para todos os canais!',
|
||||
},
|
||||
user: {
|
||||
title: 'Configuração de usuário',
|
||||
|
@ -227,7 +227,7 @@ export default {
|
||||
publicPath: 'Public (HLS) Path',
|
||||
playlistPath: 'Playlist Path',
|
||||
storagePath: 'Storage Path',
|
||||
sharedStorage: 'Общее хранилище включено, используйте один и тот же корень хранилища для всех каналов!',
|
||||
sharedStorage: 'ffplayout работает внутри контейнера, используйте один и тот же корень хранилища для всех каналов!',
|
||||
},
|
||||
user: {
|
||||
title: 'Конфигурация пользователя',
|
||||
|
2077
frontend/package-lock.json
generated
2077
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,21 +27,21 @@
|
||||
"pinia": "^2.2.4",
|
||||
"sortablejs-vue3": "^1.2.11",
|
||||
"splitpanes": "^3.1.5",
|
||||
"video.js": "^8.17.4"
|
||||
"video.js": "^8.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint": "^0.5.7",
|
||||
"@nuxt/eslint": "^0.6.0",
|
||||
"@nuxtjs/i18n": "^8.5.5",
|
||||
"@nuxtjs/tailwindcss": "^6.12.1",
|
||||
"@nuxtjs/tailwindcss": "^6.12.2",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/video.js": "^7.3.58",
|
||||
"daisyui": "^4.12.12",
|
||||
"daisyui": "^4.12.13",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"sass": "^1.79.4",
|
||||
"sass": "^1.80.2",
|
||||
"sass-loader": "^16.0.2",
|
||||
"vue": "^3.5.11",
|
||||
"vue": "^3.5.12",
|
||||
"vue-router": "^4.4.5"
|
||||
}
|
||||
}
|
||||
|
@ -369,10 +369,6 @@ onMounted(async () => {
|
||||
let config_extensions = configStore.playout.storage.extensions
|
||||
let extra_extensions = configStore.channels[configStore.i].extra_extensions
|
||||
|
||||
if (typeof config_extensions === 'string') {
|
||||
config_extensions = config_extensions.split(',')
|
||||
}
|
||||
|
||||
if (typeof extra_extensions === 'string') {
|
||||
extra_extensions = extra_extensions.split(',')
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user