reorder args, update config, fix channel add/update, fix #786
This commit is contained in:
parent
7b27dcd12b
commit
a35e75331c
@ -3,6 +3,8 @@ members = ["engine", "tests"]
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
description = "24/7 playout based on rust and ffmpeg"
|
||||||
|
readme = "README.md"
|
||||||
version = "0.24.0-beta5"
|
version = "0.24.0-beta5"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/ffplayout/ffplayout"
|
repository = "https://github.com/ffplayout/ffplayout"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ffplayout"
|
name = "ffplayout"
|
||||||
description = "24/7 playout based on rust and ffmpeg"
|
description.workspace = true
|
||||||
readme = "../README.md"
|
readme.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
@ -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
|
The target can be an HLS playlist, rtmp/srt/udp server, desktop player
|
||||||
or any other output supported by ffmpeg.\n
|
or any other output supported by ffmpeg.\n
|
||||||
ffplayout also provides a web frontend and API to control streaming,
|
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 {
|
pub struct Args {
|
||||||
#[clap(
|
#[clap(
|
||||||
short,
|
short,
|
||||||
@ -45,9 +47,6 @@ pub struct Args {
|
|||||||
)]
|
)]
|
||||||
pub init: bool,
|
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")]
|
#[clap(short, long, help_heading = Some("Initial Setup"), help = "Create admin user")]
|
||||||
pub username: Option<String>,
|
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.")]
|
#[clap(long, help_heading = Some("Initial Setup / Playlist"), help = "Path to playlist, or playlist root folder.")]
|
||||||
pub playlists: Option<String>,
|
pub playlists: Option<String>,
|
||||||
|
|
||||||
|
#[clap(short, long, help_heading = Some("General"), help = "Add a global admin")]
|
||||||
|
pub add: bool,
|
||||||
|
|
||||||
#[clap(long, env, help_heading = Some("General"), help = "Path to database file")]
|
#[clap(long, env, help_heading = Some("General"), help = "Path to database file")]
|
||||||
pub db: Option<PathBuf>,
|
pub db: Option<PathBuf>,
|
||||||
|
|
||||||
@ -360,7 +362,6 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
|
|||||||
if global.shared {
|
if global.shared {
|
||||||
storage_path = storage_path.join("1");
|
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)
|
channel.public = Path::new(&channel.public)
|
||||||
.join("1")
|
.join("1")
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
|
@ -11,21 +11,37 @@
|
|||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ t('config.name') }}</span>
|
<span class="label-text">{{ t('config.name') }}</span>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<label class="form-control w-full mt-5">
|
<label class="form-control w-full mt-5">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ t('config.previewUrl') }}</span>
|
<span class="label-text">{{ t('config.previewUrl') }}</span>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<label class="form-control w-full mt-5">
|
<label class="form-control w-full mt-5">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ t('config.extensions') }}</span>
|
<span class="label-text">{{ t('config.extensions') }}</span>
|
||||||
</div>
|
</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>
|
</label>
|
||||||
|
|
||||||
<template v-if="authStore.role === 'GlobalAdmin'">
|
<template v-if="authStore.role === 'GlobalAdmin'">
|
||||||
@ -39,21 +55,36 @@
|
|||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ t('config.publicPath') }}</span>
|
<span class="label-text">{{ t('config.publicPath') }}</span>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<label class="form-control w-full mt-5">
|
<label class="form-control w-full mt-5">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ t('config.playlistPath') }}</span>
|
<span class="label-text">{{ t('config.playlistPath') }}</span>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<label class="form-control w-full mt-5">
|
<label class="form-control w-full mt-5">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ t('config.storagePath') }}</span>
|
<span class="label-text">{{ t('config.storagePath') }}</span>
|
||||||
</div>
|
</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>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -62,7 +93,9 @@
|
|||||||
{{ t('config.save') }}
|
{{ t('config.save') }}
|
||||||
</button>
|
</button>
|
||||||
<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"
|
class="btn btn-primary"
|
||||||
@click="deleteChannel()"
|
@click="deleteChannel()"
|
||||||
>
|
>
|
||||||
@ -77,7 +110,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep, isEqual } from 'lodash-es'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
@ -88,9 +121,11 @@ const { i } = storeToRefs(useConfig())
|
|||||||
|
|
||||||
const saved = ref(true)
|
const saved = ref(true)
|
||||||
const channel = ref({} as Channel)
|
const channel = ref({} as Channel)
|
||||||
|
const channelOrig = ref({} as Channel)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
channel.value = cloneDeep(configStore.channels[i.value])
|
channel.value = cloneDeep(configStore.channels[i.value])
|
||||||
|
channelOrig.value = cloneDeep(configStore.channels[i.value])
|
||||||
})
|
})
|
||||||
|
|
||||||
watch([i], () => {
|
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) {
|
function rmId(path: string) {
|
||||||
return path.replace(/\/\d+$/, '')
|
return path.replace(/\/\d+$/, '')
|
||||||
}
|
}
|
||||||
@ -114,23 +157,68 @@ function newChannel() {
|
|||||||
saved.value = false
|
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() {
|
async function addUpdateChannel() {
|
||||||
/*
|
/*
|
||||||
Save channel settings.
|
Save or update channel settings.
|
||||||
*/
|
*/
|
||||||
|
if (!saved.value) {
|
||||||
saved.value = true
|
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) {
|
if (configStore.channels[i.value].id !== channel.value.id) {
|
||||||
indexStore.msgAlert('success', t('config.updateChannelSuccess'), 2)
|
await addNewChannel()
|
||||||
|
} else {
|
||||||
|
await updateChannel()
|
||||||
|
}
|
||||||
|
|
||||||
await configStore.getPlayoutConfig()
|
await configStore.getPlayoutConfig()
|
||||||
await configStore.getUserConfig()
|
await configStore.getUserConfig()
|
||||||
|
|
||||||
} else {
|
|
||||||
indexStore.msgAlert('error', t('config.updateChannelFailed'), 2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
async getPlayoutConfig() {
|
||||||
const { $i18n } = useNuxtApp()
|
const { $i18n } = useNuxtApp()
|
||||||
const { timeToSeconds } = stringFormatter()
|
const { timeToSeconds } = stringFormatter()
|
||||||
|
Loading…
Reference in New Issue
Block a user