reorder args, update config, fix channel add/update, fix #786

This commit is contained in:
Jonathan Baecker 2024-10-02 15:03:03 +02:00
parent 7b27dcd12b
commit a35e75331c
5 changed files with 116 additions and 64 deletions

View File

@ -3,6 +3,8 @@ members = ["engine", "tests"]
resolver = "2"
[workspace.package]
description = "24/7 playout based on rust and ffmpeg"
readme = "README.md"
version = "0.24.0-beta5"
license = "GPL-3.0"
repository = "https://github.com/ffplayout/ffplayout"

View File

@ -1,7 +1,7 @@
[package]
name = "ffplayout"
description = "24/7 playout based on rust and ffmpeg"
readme = "../README.md"
description.workspace = true
readme.workspace = true
version.workspace = true
license.workspace = true
authors.workspace = true

View File

@ -35,7 +35,9 @@ Stream dynamic playlists or folder contents with the power of ffmpeg.
The target can be an HLS playlist, rtmp/srt/udp server, desktop player
or any other output supported by ffmpeg.\n
ffplayout also provides a web frontend and API to control streaming,
manage config, files, text overlay, etc. "))]
manage config, files, text overlay, etc."),
next_line_help = false,
)]
pub struct Args {
#[clap(
short,
@ -45,9 +47,6 @@ pub struct Args {
)]
pub init: bool,
#[clap(short, long, help_heading = Some("Initial Setup"), help = "Add a global admin")]
pub add: bool,
#[clap(short, long, help_heading = Some("Initial Setup"), help = "Create admin user")]
pub username: Option<String>,
@ -77,6 +76,9 @@ pub struct Args {
#[clap(long, help_heading = Some("Initial Setup / Playlist"), help = "Path to playlist, or playlist root folder.")]
pub playlists: Option<String>,
#[clap(short, long, help_heading = Some("General"), help = "Add a global admin")]
pub add: bool,
#[clap(long, env, help_heading = Some("General"), help = "Path to database file")]
pub db: Option<PathBuf>,
@ -360,7 +362,6 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
if global.shared {
storage_path = storage_path.join("1");
channel.preview_url = "http://127.0.0.1:8787/1/stream.m3u8".to_string();
channel.public = Path::new(&channel.public)
.join("1")
.to_string_lossy()

View File

@ -11,21 +11,37 @@
<div class="label">
<span class="label-text">{{ t('config.name') }}</span>
</div>
<input v-model="channel.name" type="text" placeholder="Type here" class="input input-bordered w-full" />
<input
v-model="channel.name"
type="text"
placeholder="Type here"
class="input input-bordered w-full"
@keyup="isChanged"
/>
</label>
<label class="form-control w-full mt-5">
<div class="label">
<span class="label-text">{{ t('config.previewUrl') }}</span>
</div>
<input v-model="channel.preview_url" type="text" class="input input-bordered w-full" />
<input
v-model="channel.preview_url"
type="text"
class="input input-bordered w-full"
@keyup="isChanged"
/>
</label>
<label class="form-control w-full mt-5">
<div class="label">
<span class="label-text">{{ t('config.extensions') }}</span>
</div>
<input v-model="channel.extra_extensions" type="text" class="input input-bordered w-full" />
<input
v-model="channel.extra_extensions"
type="text"
class="input input-bordered w-full"
@keyup="isChanged"
/>
</label>
<template v-if="authStore.role === 'GlobalAdmin'">
@ -39,21 +55,36 @@
<div class="label">
<span class="label-text">{{ t('config.publicPath') }}</span>
</div>
<input v-model="channel.public" type="text" class="input input-bordered w-full" />
<input
v-model="channel.public"
type="text"
class="input input-bordered w-full"
@keyup="isChanged"
/>
</label>
<label class="form-control w-full mt-5">
<div class="label">
<span class="label-text">{{ t('config.playlistPath') }}</span>
</div>
<input v-model="channel.playlists" type="text" class="input input-bordered w-full" />
<input
v-model="channel.playlists"
type="text"
class="input input-bordered w-full"
@keyup="isChanged"
/>
</label>
<label class="form-control w-full mt-5">
<div class="label">
<span class="label-text">{{ t('config.storagePath') }}</span>
</div>
<input v-model="channel.storage" type="text" class="input input-bordered w-full" />
<input
v-model="channel.storage"
type="text"
class="input input-bordered w-full"
@keyup="isChanged"
/>
</label>
</template>
@ -62,7 +93,9 @@
{{ t('config.save') }}
</button>
<button
v-if="authStore.role === 'GlobalAdmin' && configStore.channels.length > 1 && channel.id > 1 && saved"
v-if="
authStore.role === 'GlobalAdmin' && configStore.channels.length > 1 && channel.id > 1 && saved
"
class="btn btn-primary"
@click="deleteChannel()"
>
@ -77,7 +110,7 @@
</template>
<script setup lang="ts">
import { cloneDeep } from 'lodash-es'
import { cloneDeep, isEqual } from 'lodash-es'
const { t } = useI18n()
@ -88,9 +121,11 @@ const { i } = storeToRefs(useConfig())
const saved = ref(true)
const channel = ref({} as Channel)
const channelOrig = ref({} as Channel)
onMounted(() => {
channel.value = cloneDeep(configStore.channels[i.value])
channelOrig.value = cloneDeep(configStore.channels[i.value])
})
watch([i], () => {
@ -99,6 +134,14 @@ watch([i], () => {
}
})
function isChanged() {
if (isEqual(channel.value, channelOrig.value)) {
saved.value = true
} else {
saved.value = false
}
}
function rmId(path: string) {
return path.replace(/\/\d+$/, '')
}
@ -114,23 +157,68 @@ function newChannel() {
saved.value = false
}
async function addNewChannel() {
await $fetch('/api/channel/', {
method: 'POST',
headers: { ...configStore.contentType, ...authStore.authHeader },
body: JSON.stringify(channel.value),
})
.then((chl) => {
i.value = channel.value.id - 1
configStore.channels.push(cloneDeep(chl))
configStore.channelsRaw.push(chl)
configStore.configCount = configStore.channels.length
indexStore.msgAlert('success', t('config.updateChannelSuccess'), 2)
})
.catch(() => {
indexStore.msgAlert('error', t('config.updateChannelFailed'), 3)
})
}
async function updateChannel() {
await fetch(`/api/channel/${channel.value.id}`, {
method: 'PATCH',
headers: { ...configStore.contentType, ...authStore.authHeader },
body: JSON.stringify(channel.value),
})
.then(() => {
for (let i = 0; i < configStore.channels.length; i++) {
if (configStore.channels[i].id === channel.value.id) {
configStore.channels[i] = cloneDeep(channel.value)
break
}
}
for (let i = 0; i < configStore.channelsRaw.length; i++) {
if (configStore.channelsRaw[i].id === channel.value.id) {
configStore.channelsRaw[i] = cloneDeep(channel.value)
break
}
}
indexStore.msgAlert('success', t('config.updateChannelSuccess'), 2)
})
.catch(() => {
indexStore.msgAlert('error', t('config.updateChannelFailed'), 3)
})
}
async function addUpdateChannel() {
/*
Save channel settings.
Save or update channel settings.
*/
if (!saved.value) {
saved.value = true
i.value = channel.value.id - 1
configStore.channels.push(cloneDeep(channel.value))
const update = await configStore.setChannelConfig(channel.value)
if (update.status && update.status < 400) {
indexStore.msgAlert('success', t('config.updateChannelSuccess'), 2)
if (configStore.channels[i.value].id !== channel.value.id) {
await addNewChannel()
} else {
await updateChannel()
}
await configStore.getPlayoutConfig()
await configStore.getUserConfig()
} else {
indexStore.msgAlert('error', t('config.updateChannelFailed'), 2)
}
}

View File

@ -95,45 +95,6 @@ export const useConfig = defineStore('config', {
})
},
async setChannelConfig(obj: Channel): Promise<any> {
const authStore = useAuth()
const stringObj = cloneDeep(obj)
let response
if (this.channelsRaw.some((e) => e.id === stringObj.id)) {
response = await fetch(`/api/channel/${obj.id}`, {
method: 'PATCH',
headers: { ...this.contentType, ...authStore.authHeader },
body: JSON.stringify(stringObj),
})
} else {
response = await fetch('/api/channel/', {
method: 'POST',
headers: { ...this.contentType, ...authStore.authHeader },
body: JSON.stringify(stringObj),
})
const json = await response.json()
const channelConfigs = []
for (const obj of this.channels) {
if (obj.name === stringObj.name) {
channelConfigs.push(json)
} else {
channelConfigs.push(obj)
}
}
this.channels = channelConfigs
this.channelsRaw = cloneDeep(channelConfigs)
this.configCount = channelConfigs.length
}
await this.getPlayoutConfig()
return response
},
async getPlayoutConfig() {
const { $i18n } = useNuxtApp()
const { timeToSeconds } = stringFormatter()