ignore escape characters in regex, get playlist from other days, fix ffplayout #599, colorize over length, save playlists longer then 24 hours, format with prettier, eslint ignore slash on void
This commit is contained in:
parent
6c574feda1
commit
cbb69d0e16
2
.gitignore
vendored
2
.gitignore
vendored
@ -29,8 +29,6 @@ tv-media/
|
||||
Videos
|
||||
Videos/
|
||||
*.tar*
|
||||
.vscode
|
||||
.vscode/
|
||||
home
|
||||
home/
|
||||
live1
|
||||
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
}
|
10
app.vue
10
app.vue
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
<div>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -18,7 +18,7 @@
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
class="input input-bordered w-full"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
@ -29,7 +29,7 @@
|
||||
v-model="configStore.configGui[configStore.configID].preview_url"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
@ -40,7 +40,7 @@
|
||||
v-model="configStore.configGui[configStore.configID].config_path"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
@ -51,7 +51,7 @@
|
||||
v-model="configStore.configGui[configStore.configID].extra_extensions"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
@ -63,7 +63,7 @@
|
||||
type="text"
|
||||
class="input input-bordered w-full !bg-base-100"
|
||||
disabled
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div class="join my-4">
|
||||
|
@ -2,11 +2,11 @@
|
||||
<div class="max-w-[1200px] pe-8">
|
||||
<h2 class="pt-3 text-3xl">{{ $t('config.playoutConf') }}</h2>
|
||||
<form
|
||||
v-if="configStore.configPlayout"
|
||||
v-if="configStore.playout"
|
||||
class="mt-10 grid md:grid-cols-[180px_auto] gap-5"
|
||||
@submit.prevent="onSubmitPlayout"
|
||||
>
|
||||
<template v-for="(item, key) in configStore.configPlayout" :key="key">
|
||||
<template v-for="(item, key) in configStore.playout" :key="key">
|
||||
<div class="text-xl pt-3 text-right">{{ setTitle(key.toString()) }}:</div>
|
||||
<div class="md:pt-4">
|
||||
<label
|
||||
@ -15,63 +15,65 @@
|
||||
class="form-control w-full"
|
||||
:class="[typeof prop === 'boolean' && 'flex-row', name.toString() !== 'help_text' && 'mt-2']"
|
||||
>
|
||||
<div v-if="name.toString() !== 'help_text'" class="label">
|
||||
<span class="label-text !text-md font-bold">{{ name }}</span>
|
||||
</div>
|
||||
<div v-if="name.toString() === 'help_text'" class="whitespace-pre-line">
|
||||
{{ setHelp(key.toString(), prop) }}
|
||||
</div>
|
||||
<input
|
||||
v-else-if="name.toString() === 'sender_pass'"
|
||||
v-model="item[name]"
|
||||
type="password"
|
||||
:placeholder="$t('config.placeholderPass')"
|
||||
class="input input-sm input-bordered w-full"
|
||||
>
|
||||
<textarea
|
||||
v-else-if="name.toString() === 'output_param' || name.toString() === 'custom_filter'"
|
||||
v-model="item[name]"
|
||||
class="textarea textarea-bordered"
|
||||
rows="3"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number' && prop % 1 === 0"
|
||||
v-model="item[name]"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number'"
|
||||
v-model="item[name]"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
step="0.0001"
|
||||
style="max-width: 250px"
|
||||
>
|
||||
<input
|
||||
v-else-if="typeof prop === 'boolean'"
|
||||
v-model="item[name]"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm ms-2 mt-2"
|
||||
>
|
||||
<input
|
||||
v-else-if="name === 'ignore_lines'"
|
||||
v-model="formatIgnoreLines"
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
>
|
||||
<template v-if="name.toString() !== 'startInSec' && name.toString() !== 'lengthInSec'">
|
||||
<div v-if="name.toString() !== 'help_text'" class="label">
|
||||
<span class="label-text !text-md font-bold">{{ name }}</span>
|
||||
</div>
|
||||
<div v-if="name.toString() === 'help_text'" class="whitespace-pre-line">
|
||||
{{ setHelp(key.toString(), prop) }}
|
||||
</div>
|
||||
<input
|
||||
v-else-if="name.toString() === 'sender_pass'"
|
||||
v-model="item[name]"
|
||||
type="password"
|
||||
:placeholder="$t('config.placeholderPass')"
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<textarea
|
||||
v-else-if="name.toString() === 'output_param' || name.toString() === 'custom_filter'"
|
||||
v-model="item[name]"
|
||||
class="textarea textarea-bordered"
|
||||
rows="3"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number' && prop % 1 === 0"
|
||||
v-model="item[name]"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number'"
|
||||
v-model="item[name]"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
step="0.0001"
|
||||
style="max-width: 250px"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'boolean'"
|
||||
v-model="item[name]"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm ms-2 mt-2"
|
||||
/>
|
||||
<input
|
||||
v-else-if="name === 'ignore_lines'"
|
||||
v-model="formatIgnoreLines"
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
/>
|
||||
</template>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mt-5 mb-10">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<button class="btn btn-primary" type="submit">{{ $t('config.save') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -95,11 +97,11 @@ const showModal = ref(false)
|
||||
|
||||
const formatIgnoreLines = computed({
|
||||
get() {
|
||||
return configStore.configPlayout.logging.ignore_lines.join(';')
|
||||
return configStore.playout.logging.ignore_lines.join(';')
|
||||
},
|
||||
|
||||
set(value) {
|
||||
configStore.configPlayout.logging.ignore_lines = value.split(';')
|
||||
configStore.playout.logging.ignore_lines = value.split(';')
|
||||
},
|
||||
})
|
||||
|
||||
@ -162,7 +164,7 @@ function setHelp(key: string, text: string): string {
|
||||
}
|
||||
|
||||
async function onSubmitPlayout() {
|
||||
const update = await configStore.setPlayoutConfig(configStore.configPlayout)
|
||||
const update = await configStore.setPlayoutConfig(configStore.playout)
|
||||
|
||||
if (update.status === 200) {
|
||||
indexStore.msgAlert('success', 'Update playout config success!', 2)
|
||||
|
@ -3,8 +3,8 @@
|
||||
<h2 class="pt-3 text-3xl">{{ $t('user.title') }}</h2>
|
||||
<div class="flex flex-col xs:flex-row gap-2 w-full mb-5 mt-10">
|
||||
<div class="grow">
|
||||
<select class="select select-bordered w-full max-w-xs" v-model="selected" @change="onChange($event)">
|
||||
<option v-for="item in users">{{ item.username }}</option>
|
||||
<select v-model="selected" class="select select-bordered w-full max-w-xs" @change="onChange($event)">
|
||||
<option v-for="item in users" :key="item.username">{{ item.username }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-none join">
|
||||
@ -22,9 +22,9 @@
|
||||
<span class="label-text">{{ $t('user.name') }}</span>
|
||||
</div>
|
||||
<input
|
||||
v-model="configStore.configUser.username"
|
||||
type="text"
|
||||
class="input input-bordered w-full !bg-base-100"
|
||||
v-model="configStore.configUser.username"
|
||||
disabled
|
||||
/>
|
||||
</label>
|
||||
@ -33,33 +33,21 @@
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.mail') }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
class="input input-bordered w-full"
|
||||
v-model="configStore.configUser.mail"
|
||||
/>
|
||||
<input v-model="configStore.configUser.mail" type="email" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full max-w-md mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.newPass') }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
class="input input-bordered w-full"
|
||||
v-model="newPass"
|
||||
/>
|
||||
<input v-model="newPass" type="password" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full max-w-md mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.confirmPass') }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
class="input input-bordered w-full"
|
||||
v-model="confirmPass"
|
||||
/>
|
||||
<input v-model="confirmPass" type="password" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<div>
|
||||
@ -74,42 +62,34 @@
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.name') }}</span>
|
||||
</div>
|
||||
<input type="text" class="input input-bordered w-full" v-model="user.username" />
|
||||
<input v-model="user.username" type="text" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.mail') }}</span>
|
||||
</div>
|
||||
<input type="email" class="input input-bordered w-full" v-model="user.mail" />
|
||||
<input v-model="user.mail" type="email" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.password') }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
class="input input-bordered w-full"
|
||||
v-model="user.password"
|
||||
/>
|
||||
<input v-model="user.password" type="password" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('user.confirmPass') }}</span>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
class="input input-bordered w-full"
|
||||
v-model="user.confirm"
|
||||
/>
|
||||
<input v-model="user.confirm" type="password" class="input input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<div class="form-control mt-3">
|
||||
<label class="label cursor-pointer w-1/2">
|
||||
<span class="label-text">{{ $t('user.admin') }}</span>
|
||||
<input type="checkbox" class="checkbox" v-model.number="user.admin" />
|
||||
<input v-model.number="user.admin" type="checkbox" class="checkbox" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -243,7 +223,7 @@ async function addUser(add: boolean) {
|
||||
}
|
||||
|
||||
async function onSubmitUser() {
|
||||
if (newPass && newPass.value === confirmPass.value) {
|
||||
if (newPass.value && newPass.value === confirmPass.value) {
|
||||
configStore.configUser.password = newPass.value
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="flex gap-2">
|
||||
<div class="font-bold text-lg truncate flex-1 w-0">{{ title }}</div>
|
||||
<button v-if="hideButtons" class="btn btn-sm w-8 h-8 rounded-full" @click="modalAction(false)">
|
||||
<i class="bi bi-x-lg"/>
|
||||
<i class="bi bi-x-lg" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="navbar bg-base-100 min-h-[52px] p-0 shadow">
|
||||
<NuxtLink class="navbar-brand min-w-[46px] p-2" href="/">
|
||||
<img src="~/assets/images/ffplayout-small.png" class="img-fluid" alt="Logo" width="30" height="30" >
|
||||
<img src="~/assets/images/ffplayout-small.png" class="img-fluid" alt="Logo" width="30" height="30" />
|
||||
</NuxtLink>
|
||||
<div class="navbar-end w-1/5 grow">
|
||||
<label class="swap swap-rotate me-2 md:hidden">
|
||||
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" >
|
||||
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" />
|
||||
<SvgIcon name="swap-on" classes="w-5 h-5" />
|
||||
<SvgIcon name="swap-off" classes="w-5 h-5" />
|
||||
</label>
|
||||
@ -81,7 +81,7 @@
|
||||
</li>
|
||||
<li class="p-0">
|
||||
<label class="swap swap-rotate">
|
||||
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" >
|
||||
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" />
|
||||
<SvgIcon name="swap-on" classes="w-5 h-5" />
|
||||
<SvgIcon name="swap-off" classes="w-5 h-5" />
|
||||
</label>
|
||||
|
@ -61,8 +61,10 @@
|
||||
{{ filename(playlistStore.currentClip) }}
|
||||
</div>
|
||||
<div class="grow">
|
||||
<strong>{{ $t('player.duration') }}:</strong> {{ secToHMS(playlistStore.currentClipDuration) }} |
|
||||
<strong>{{ $t('player.in') }}:</strong> {{ secToHMS(playlistStore.currentClipIn) }} | <strong>{{ $t('player.out') }}:</strong>
|
||||
<strong>{{ $t('player.duration') }}:</strong>
|
||||
{{ secToHMS(playlistStore.currentClipDuration) }} |
|
||||
<strong>{{ $t('player.in') }}:</strong> {{ secToHMS(playlistStore.currentClipIn) }} |
|
||||
<strong>{{ $t('player.out') }}:</strong>
|
||||
{{ secToHMS(playlistStore.currentClipOut) }}
|
||||
</div>
|
||||
<div class="h-1/3">
|
||||
|
@ -17,7 +17,7 @@
|
||||
aria-label="Simple"
|
||||
checked
|
||||
@change="advancedGenerator = false"
|
||||
>
|
||||
/>
|
||||
<div role="tabpanel" class="tab-content w-full pt-3">
|
||||
<div class="w-full">
|
||||
<div class="grid">
|
||||
@ -78,7 +78,7 @@
|
||||
)
|
||||
)
|
||||
"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@ -93,7 +93,7 @@
|
||||
class="tab"
|
||||
aria-label="Advanced"
|
||||
@change=";(advancedGenerator = true), resetCheckboxes()"
|
||||
>
|
||||
/>
|
||||
<div role="tabpanel" class="tab-content pt-3">
|
||||
<div class="w-full">
|
||||
<div class="grid grid-cols-[auto_48px] px-3 pt-0">
|
||||
@ -121,7 +121,7 @@
|
||||
title="Add time block"
|
||||
@click="addTemplate()"
|
||||
>
|
||||
<i class="bi bi-folder-plus"/>
|
||||
<i class="bi bi-folder-plus" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -180,7 +180,7 @@
|
||||
v-model="item.start"
|
||||
type="text"
|
||||
class="input input-sm input-bordered join-item px-2 text-center"
|
||||
>
|
||||
/>
|
||||
<div
|
||||
class="input input-sm input-bordered join-item px-2 text-center bg-base-200"
|
||||
>
|
||||
@ -190,7 +190,7 @@
|
||||
v-model="item.duration"
|
||||
type="text"
|
||||
class="input input-sm input-bordered join-item px-2 text-center"
|
||||
>
|
||||
/>
|
||||
<button
|
||||
class="btn btn-sm input-bordered join-item"
|
||||
:class="item.shuffle ? 'bg-base-100' : 'bg-base-300'"
|
||||
@ -247,7 +247,7 @@
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-xs rounded"
|
||||
@change="resetCheckboxes()"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="join ms-2">
|
||||
@ -291,12 +291,14 @@ const advancedGenerator = ref(false)
|
||||
const selectedFolders = ref([] as string[])
|
||||
const generateFromAll = ref(false)
|
||||
const template = ref({
|
||||
sources: [{
|
||||
start: configStore.configPlayout.playlist.day_start,
|
||||
duration: '02:00:00',
|
||||
shuffle: false,
|
||||
paths: [],
|
||||
}],
|
||||
sources: [
|
||||
{
|
||||
start: configStore.playout.playlist.day_start,
|
||||
duration: '02:00:00',
|
||||
shuffle: false,
|
||||
paths: [],
|
||||
},
|
||||
],
|
||||
} as Template)
|
||||
|
||||
const templateBrowserSortOptions = {
|
||||
@ -331,12 +333,7 @@ async function generatePlaylist() {
|
||||
body,
|
||||
})
|
||||
.then((response: any) => {
|
||||
playlistStore.playlist = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
response.program,
|
||||
false
|
||||
)
|
||||
playlistStore.playlist = processPlaylist(playlistStore.listDate, response.program, false)
|
||||
indexStore.msgAlert('success', 'Generate Playlist done...', 2)
|
||||
})
|
||||
.catch((e: any) => {
|
||||
@ -379,7 +376,7 @@ function addFolderToTemplate(event: any, item: TemplateItem) {
|
||||
|
||||
event.item.remove()
|
||||
|
||||
const storagePath = configStore.configPlayout.storage.path
|
||||
const storagePath = configStore.playout.storage.path
|
||||
const navPath = mediaStore.folderCrumbs[mediaStore.folderCrumbs.length - 1].path
|
||||
const sourcePath = `${storagePath}/${navPath}/${mediaStore.folderList.folders[o].name}`.replace(/\/[/]+/g, '/')
|
||||
|
||||
@ -404,7 +401,10 @@ function addTemplate() {
|
||||
|
||||
if (last) {
|
||||
const t = $dayjs(`2000-01-01T${last.duration}`)
|
||||
start = $dayjs(`2000-01-01T${last.start}`).add(t.hour(), 'hour').add(t.minute(), 'minute').add(t.second(), 'second')
|
||||
start = $dayjs(`2000-01-01T${last.start}`)
|
||||
.add(t.hour(), 'hour')
|
||||
.add(t.minute(), 'minute')
|
||||
.add(t.second(), 'second')
|
||||
}
|
||||
|
||||
template.value.sources.push({
|
||||
|
@ -72,6 +72,7 @@
|
||||
playlistStore.playoutIsRunning &&
|
||||
listDate === todayDate &&
|
||||
index === playlistStore.currentClipIndex,
|
||||
'!bg-amber-600/40': element.overtime,
|
||||
}"
|
||||
>
|
||||
<td class="ps-4 py-2 text-left">{{ secondsToTime(element.begin) }}</td>
|
||||
@ -96,7 +97,7 @@
|
||||
type="checkbox"
|
||||
:checked="element.category && element.category === 'advertisement' ? true : false"
|
||||
@change="setCategory($event, element)"
|
||||
>
|
||||
/>
|
||||
</td>
|
||||
<td class="py-2 text-center hover:text-base-content/70">
|
||||
<button @click="editItem(index)">
|
||||
@ -193,7 +194,7 @@ function addClip(event: any) {
|
||||
|
||||
event.item.remove()
|
||||
|
||||
const storagePath = configStore.configPlayout.storage.path
|
||||
const storagePath = configStore.playout.storage.path
|
||||
const sourcePath = `${storagePath}/${mediaStore.folderTree.source}/${mediaStore.folderTree.files[o].name}`.replace(
|
||||
/\/[/]+/g,
|
||||
'/'
|
||||
@ -208,12 +209,7 @@ function addClip(event: any) {
|
||||
duration: mediaStore.folderTree.files[o].duration,
|
||||
})
|
||||
|
||||
playlistStore.playlist = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
playlistStore.playlist,
|
||||
false
|
||||
)
|
||||
playlistStore.playlist = processPlaylist(listDate.value, playlistStore.playlist, false)
|
||||
|
||||
nextTick(() => {
|
||||
const newNode = document.getElementById(`clip-${n}`)
|
||||
@ -225,12 +221,7 @@ function addClip(event: any) {
|
||||
function moveItemInArray(event: any) {
|
||||
playlistStore.playlist.splice(event.newIndex, 0, playlistStore.playlist.splice(event.oldIndex, 1)[0])
|
||||
|
||||
playlistStore.playlist = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
playlistStore.playlist,
|
||||
false
|
||||
)
|
||||
playlistStore.playlist = processPlaylist(listDate.value, playlistStore.playlist, false)
|
||||
|
||||
removeBG(event.item)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="p-4 bg-base-100">
|
||||
<span class="text-3xl">{{ sysStat.system.name }} {{ sysStat.system.version }}</span>
|
||||
<span v-if="sysStat.system.kernel">
|
||||
<br >
|
||||
<br />
|
||||
{{ sysStat.system.kernel }}
|
||||
</span>
|
||||
</div>
|
||||
@ -16,8 +16,12 @@
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">{{ $t('system.cpu') }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>{{ $t('system.cores') }}:</strong> {{ sysStat.cpu.cores }}</div>
|
||||
<div><strong>{{ $t('system.usage') }}:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.cores') }}:</strong> {{ sysStat.cpu.cores }}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.usage') }}:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
@ -31,15 +35,23 @@
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">{{ $t('system.memory') }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>{{ $t('system.total') }}:</strong> {{ fileSize(sysStat.memory.total) }}</div>
|
||||
<div><strong>{{ $t('system.usage') }}:</strong> {{ fileSize(sysStat.memory.used) }}</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.total') }}:</strong> {{ fileSize(sysStat.memory.total) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.usage') }}:</strong> {{ fileSize(sysStat.memory.used) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">{{ $t('system.swap') }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>{{ $t('system.total') }}:</strong> {{ fileSize(sysStat.swap.total) }}</div>
|
||||
<div><strong>{{ $t('system.usage') }}:</strong> {{ fileSize(sysStat.swap.used) }}</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.total') }}:</strong> {{ fileSize(sysStat.swap.total) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.usage') }}:</strong> {{ fileSize(sysStat.swap.used) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
@ -47,19 +59,29 @@
|
||||
{{ $t('system.network') }} <span v-if="sysStat.network" class="fs-6">{{ sysStat.network?.name }}</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>{{ $t('system.in') }}:</strong> {{ fileSize(sysStat.network?.current_in) }}</div>
|
||||
<div><strong>{{ $t('system.out') }}:</strong> {{ fileSize(sysStat.network?.current_out) }}</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.in') }}:</strong> {{ fileSize(sysStat.network?.current_in) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.out') }}:</strong> {{ fileSize(sysStat.network?.current_out) }}
|
||||
</div>
|
||||
<div>{{ fileSize(sysStat.network?.total_in) }}</div>
|
||||
<div>{{ fileSize(sysStat.network?.total_out) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="sysStat.storage?.path" class="p-4 border border-primary">
|
||||
<div class="text-xl">{{ $t('system.storage') }}</div>
|
||||
<div v-if="sysStat.storage"><strong>{{ $t('system.device') }}:</strong> {{ sysStat.storage?.path }}</div>
|
||||
<div v-if="sysStat.storage">
|
||||
<strong>{{ $t('system.device') }}:</strong> {{ sysStat.storage?.path }}
|
||||
</div>
|
||||
|
||||
<div v-if="sysStat.storage" class="grid grid-cols-2">
|
||||
<div><strong>{{ $t('system.size') }}:</strong> {{ fileSize(sysStat.storage?.total) }}</div>
|
||||
<div><strong>{{ $t('system.used') }}:</strong> {{ fileSize(sysStat.storage?.used) }}</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.size') }}:</strong> {{ fileSize(sysStat.storage?.total) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ $t('system.used') }}:</strong> {{ fileSize(sysStat.storage?.used) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="col-6 bg-primary p-2 border" />
|
||||
|
@ -181,15 +181,22 @@ export const playlistOperations = () => {
|
||||
return String(Date.now().toString(32) + Math.random().toString(16)).replace(/\./g, '')
|
||||
}
|
||||
|
||||
function processPlaylist(dayStart: number, length: number, list: PlaylistItem[], forSave: boolean) {
|
||||
if (!dayStart) {
|
||||
dayStart = 0
|
||||
}
|
||||
function processPlaylist(date: string, list: PlaylistItem[], forSave: boolean) {
|
||||
const configStore = useConfig()
|
||||
|
||||
let begin = configStore.playout.playlist.startInSec
|
||||
|
||||
let begin = dayStart
|
||||
const newList = []
|
||||
|
||||
for (const item of list) {
|
||||
if (configStore.playout.playlist.startInSec === begin) {
|
||||
if (!forSave) {
|
||||
item.date = date
|
||||
} else {
|
||||
delete item.date
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.uid) {
|
||||
item.uid = genUID()
|
||||
}
|
||||
@ -208,18 +215,17 @@ export const playlistOperations = () => {
|
||||
delete item.custom_filter
|
||||
}
|
||||
|
||||
if (begin + (item.out - item.in) > length + dayStart) {
|
||||
item.class = 'overLength'
|
||||
|
||||
if (
|
||||
begin >= configStore.playout.playlist.startInSec + configStore.playout.playlist.lengthInSec &&
|
||||
!configStore.playout.playlist.infinit
|
||||
) {
|
||||
if (forSave) {
|
||||
item.out = length + dayStart - begin
|
||||
break
|
||||
} else {
|
||||
item.overtime = true
|
||||
}
|
||||
}
|
||||
|
||||
if (forSave && begin >= length + dayStart) {
|
||||
break
|
||||
}
|
||||
|
||||
newList.push(item)
|
||||
|
||||
begin += item.out - item.in
|
||||
|
@ -13,8 +13,10 @@ export default withNuxt(
|
||||
// }
|
||||
{
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"vue/no-v-html": "off",
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-control-regex': 'off',
|
||||
'vue/html-self-closing': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -144,7 +144,8 @@ export default {
|
||||
help: 'Hilfe',
|
||||
generalText: `Manchmal kann es vorkommen, dass eine Datei beschädigt, aber noch abspielbar ist, was einen Streaming-Fehler bei allen folgenden Dateien verursachen kann. Die einzige Möglichkeit, dies zu beheben, besteht darin, ffplayout anzuhalten und neu zu starten. Wir sagen hier nur, wann es gestoppt werden muss, der Startvorgang ist Ihnen überlassen. Der beste Weg ist ein systemd-Dienst unter Linux.
|
||||
'stop_threshold' wird ffplayout stoppen, wenn es zeitlich über diesem Wert liegt. Eine Zahl kleiner als 3 kann zu unerwarteten Fehlern führen.`,
|
||||
rpcText: 'Führe einen JSON-RPC-Server aus, um Informationen über die Wiedergabe und einige Kontrollfunktionen zu erhalten.',
|
||||
rpcText:
|
||||
'Führe einen JSON-RPC-Server aus, um Informationen über die Wiedergabe und einige Kontrollfunktionen zu erhalten.',
|
||||
mailText: `Senden Sie Fehlermeldungen an die E-Mail-Adresse, z. B. fehlende Playlist, ungültiges json-Format, fehlender Clip-Pfad. Lassen Sie den Empfänger leer, wenn Sie keine Benachrichtigung benötigen. 'mail_level' kann INFO, WARNING oder ERROR sein. 'interval' bedeutet Sekunden, bis eine neue E-Mail gesendet wird.`,
|
||||
logText: `Wenn 'log_to_file' true ist, wird in eine Datei protokolliert, wenn false, in die Konsole. 'backup_count' gibt an, wie lange die Log-Dateien in Tagen gespeichert werden. 'local_time' auf false setzt die Log-Zeitstempel auf UTC. Pfad zu /var/log/ nur, wenn Sie dies als Daemon ausführen.
|
||||
'level' kann DEBUG, INFO, WARNING, ERROR sein. 'ffmpeg_level' kann INFO, WARNING, ERROR sein. 'detect_silence' protokolliert eine Fehlermeldung, wenn die Audiospur während des Validierungsprozesses 15 Sekunden lang still ist.`,
|
||||
@ -180,5 +181,5 @@ export default {
|
||||
mismatch: 'Passwort stimmt nicht überein!',
|
||||
updateSuccess: 'Benutzerprofil erfolgreich aktualisiert!',
|
||||
updateFailed: 'Fehler beim Aktualisieren des Benutzerprofils!',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ffplayout-frontend",
|
||||
"version": "0.8.0",
|
||||
"version": "0.8.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ffplayout-frontend",
|
||||
"version": "0.8.0",
|
||||
"version": "0.8.1",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/color-mode": "^3.4.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ffplayout-frontend",
|
||||
"version": "0.8.0",
|
||||
"version": "0.8.1",
|
||||
"description": "Web GUI for ffplayout",
|
||||
"author": "Jonathan Baecker",
|
||||
"private": true,
|
||||
|
@ -6,13 +6,13 @@
|
||||
<NuxtLink :to="localePath({ name: 'player' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.player') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="localePath({ name: 'media' })" class="btn join-item btn-primary px-2">
|
||||
<NuxtLink :to="localePath({ name: 'media' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.media') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="localePath({ name: 'message' })" class="btn join-item btn-primary px-2">
|
||||
<NuxtLink :to="localePath({ name: 'message' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.message') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="localePath({ name: 'logging' })" class="btn join-item btn-primary px-2">
|
||||
<NuxtLink :to="localePath({ name: 'logging' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.logging') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
@ -38,7 +38,7 @@
|
||||
</option>
|
||||
</select>
|
||||
<label class="join-item btn btn-primary swap swap-rotate me-2">
|
||||
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" >
|
||||
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" />
|
||||
<SvgIcon name="swap-on" classes="w-5 h-5" />
|
||||
<SvgIcon name="swap-off" classes="w-5 h-5" />
|
||||
</label>
|
||||
@ -54,7 +54,7 @@
|
||||
:placeholder="$t('input.username')"
|
||||
class="input input-bordered w-full"
|
||||
required
|
||||
>
|
||||
/>
|
||||
|
||||
<input
|
||||
v-model="formPassword"
|
||||
@ -62,7 +62,7 @@
|
||||
:placeholder="$t('input.password')"
|
||||
class="input input-bordered w-full mt-5"
|
||||
required
|
||||
>
|
||||
/>
|
||||
|
||||
<div class="w-full mt-4 grid grid-flow-row-dense grid-cols-12 grid-rows-1 gap-2">
|
||||
<div class="col-span-3">
|
||||
|
@ -205,7 +205,7 @@
|
||||
>
|
||||
<div class="w-[1024px] max-w-full aspect-video">
|
||||
<VideoPlayer v-if="isVideo && previewOpt" reference="previewPlayer" :options="previewOpt" />
|
||||
<img v-else :src="previewUrl" class="img-fluid" :alt="previewName" >
|
||||
<img v-else :src="previewUrl" class="img-fluid" :alt="previewName" />
|
||||
</div>
|
||||
</GenericModal>
|
||||
|
||||
@ -214,7 +214,7 @@
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('media.newFile') }}</span>
|
||||
</div>
|
||||
<input v-model="renameNewName" type="text" class="input input-bordered w-full" >
|
||||
<input v-model="renameNewName" type="text" class="input input-bordered w-full" />
|
||||
</label>
|
||||
</GenericModal>
|
||||
|
||||
@ -223,7 +223,7 @@
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('media.foldername') }}</span>
|
||||
</div>
|
||||
<input v-model="folderName.name" type="text" class="input input-bordered w-full" >
|
||||
<input v-model="folderName.name" type="text" class="input input-bordered w-full" />
|
||||
</label>
|
||||
</GenericModal>
|
||||
|
||||
@ -236,7 +236,7 @@
|
||||
:accept="extensions"
|
||||
multiple
|
||||
@change="onFileChange"
|
||||
>
|
||||
/>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
@ -257,7 +257,7 @@
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('media.uploading') }}:</span>
|
||||
</div>
|
||||
<input v-model="uploadTask" type="text" class="input input-sm input-bordered w-full" disabled >
|
||||
<input v-model="uploadTask" type="text" class="input input-sm input-bordered w-full" disabled />
|
||||
</label>
|
||||
</div>
|
||||
</GenericModal>
|
||||
@ -313,7 +313,7 @@ const lastPath = ref('')
|
||||
const xhr = ref(new XMLHttpRequest())
|
||||
|
||||
onMounted(async () => {
|
||||
let config_extensions = configStore.configPlayout.storage.extensions
|
||||
let config_extensions = configStore.playout.storage.extensions
|
||||
let extra_extensions = configStore.configGui[configStore.configID].extra_extensions
|
||||
|
||||
if (typeof config_extensions === 'string') {
|
||||
@ -428,7 +428,7 @@ function setPreviewData(path: string) {
|
||||
? 'application/x-mpegURL'
|
||||
: `video/${ext}`
|
||||
|
||||
if (configStore.configPlayout.storage.extensions.includes(`${ext}`)) {
|
||||
if (configStore.playout.storage.extensions.includes(`${ext}`)) {
|
||||
isVideo.value = true
|
||||
previewOpt.value = {
|
||||
liveui: false,
|
||||
|
@ -58,7 +58,7 @@
|
||||
type="text"
|
||||
placeholder="X"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
type="text"
|
||||
placeholder="Y"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -86,7 +86,7 @@
|
||||
v-model="form.showBox"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-xs rounded-sm"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -99,7 +99,7 @@
|
||||
type="color"
|
||||
class="input input-sm input-bordered w-full p-1"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label class="form-control w-full xs:mt-[68px]">
|
||||
@ -114,7 +114,7 @@
|
||||
step="0.01"
|
||||
class="input input-sm input-bordered w-full"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid xs:grid-cols-[150px_150px_auto] gap-4 mt-2">
|
||||
@ -128,7 +128,7 @@
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-2">
|
||||
@ -140,7 +140,7 @@
|
||||
type="color"
|
||||
class="input input-sm input-bordered w-full p-1"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
@ -153,7 +153,7 @@
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
<label class="form-control w-full mt-2">
|
||||
<div class="label">
|
||||
@ -167,7 +167,7 @@
|
||||
max="1"
|
||||
step="0.01"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -181,7 +181,7 @@
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
<label class="form-control w-full xs:max-w-[150px] mt-2">
|
||||
<div class="label">
|
||||
@ -192,7 +192,7 @@
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
required
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -209,7 +209,7 @@
|
||||
<div class="label">
|
||||
<span class="label-text">{{ $t('message.name') }}</span>
|
||||
</div>
|
||||
<input v-model="newPresetName" type="text" class="input input-bordered w-full" >
|
||||
<input v-model="newPresetName" type="text" class="input input-bordered w-full" />
|
||||
</label>
|
||||
</GenericModal>
|
||||
|
||||
|
@ -44,7 +44,7 @@
|
||||
<i class="bi-files" />
|
||||
</button>
|
||||
<button
|
||||
v-if="!configStore.configPlayout.playlist.loop"
|
||||
v-if="!configStore.playout.playlist.loop"
|
||||
class="btn btn-sm btn-primary join-item"
|
||||
:title="$t('player.loop')"
|
||||
@click="loopClips()"
|
||||
@ -72,7 +72,11 @@
|
||||
>
|
||||
<i class="bi-sort-down-alt" />
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary join-item" :title="$t('player.reset')" @click=";(playlistStore.playlist.length = 0), getPlaylist()">
|
||||
<button
|
||||
class="btn btn-sm btn-primary join-item"
|
||||
:title="$t('player.reset')"
|
||||
@click=";(playlistStore.playlist.length = 0), getPlaylist()"
|
||||
>
|
||||
<i class="bi-arrow-counterclockwise" />
|
||||
</button>
|
||||
<button
|
||||
@ -99,7 +103,7 @@
|
||||
>
|
||||
<div class="w-[1024px] max-w-full aspect-video">
|
||||
<VideoPlayer v-if="isVideo && previewOpt" reference="previewPlayer" :options="previewOpt" />
|
||||
<img v-else :src="previewUrl" class="img-fluid" :alt="previewName" >
|
||||
<img v-else :src="previewUrl" class="img-fluid" :alt="previewName" />
|
||||
</div>
|
||||
</GenericModal>
|
||||
|
||||
@ -109,14 +113,14 @@
|
||||
<div class="label">
|
||||
<span class="label-text">In</span>
|
||||
</div>
|
||||
<input v-model.number="newSource.in" type="number" class="input input-sm input-bordered w-full" >
|
||||
<input v-model.number="newSource.in" type="number" class="input input-sm input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">Out</span>
|
||||
</div>
|
||||
<input v-model.number="newSource.out" type="number" class="input input-sm input-bordered w-full" >
|
||||
<input v-model.number="newSource.out" type="number" class="input input-sm input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
@ -127,34 +131,34 @@
|
||||
v-model.number="newSource.duration"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
>
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">Source</span>
|
||||
</div>
|
||||
<input v-model="newSource.source" type="text" class="input input-sm input-bordered w-full" >
|
||||
<input v-model="newSource.source" type="text" class="input input-sm input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">Audio</span>
|
||||
</div>
|
||||
<input v-model="newSource.audio" type="text" class="input input-sm input-bordered w-full" >
|
||||
<input v-model="newSource.audio" type="text" class="input input-sm input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-3">
|
||||
<div class="label">
|
||||
<span class="label-text">Custom Filter</span>
|
||||
</div>
|
||||
<input v-model="newSource.custom_filter" type="text" class="input input-sm input-bordered w-full" >
|
||||
<input v-model="newSource.custom_filter" type="text" class="input input-sm input-bordered w-full" />
|
||||
</label>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="label-text">Advertisement</span>
|
||||
<input type="checkbox" class="checkbox checkbox-sm" @click="isAd" >
|
||||
<input type="checkbox" class="checkbox checkbox-sm" @click="isAd" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -166,11 +170,11 @@
|
||||
class="file-input file-input-sm file-input-bordered w-full"
|
||||
multiple
|
||||
@change="onFileChange"
|
||||
>
|
||||
/>
|
||||
</GenericModal>
|
||||
|
||||
<GenericModal :show="showCopyModal" :title="`Copy Program ${listDate}`" :modal-action="savePlaylist">
|
||||
<input v-model="targetDate" type="date" class="input input-sm input-bordered w-full" >
|
||||
<input v-model="targetDate" type="date" class="input input-sm input-bordered w-full" />
|
||||
</GenericModal>
|
||||
|
||||
<GenericModal :show="showDeleteModal" title="Delete Program" :modal-action="deletePlaylist">
|
||||
@ -271,7 +275,7 @@ function closePlayer() {
|
||||
|
||||
function setPreviewData(path: string) {
|
||||
let fullPath = path
|
||||
const storagePath = configStore.configPlayout.storage.path
|
||||
const storagePath = configStore.playout.storage.path
|
||||
const lastIndex = storagePath.lastIndexOf('/')
|
||||
|
||||
if (!path.includes('/')) {
|
||||
@ -301,7 +305,7 @@ function setPreviewData(path: string) {
|
||||
? 'application/x-mpegURL'
|
||||
: `video/${ext}`
|
||||
|
||||
if (configStore.configPlayout.storage.extensions.includes(`${ext}`)) {
|
||||
if (configStore.playout.storage.extensions.includes(`${ext}`)) {
|
||||
isVideo.value = true
|
||||
previewOpt.value = {
|
||||
liveui: false,
|
||||
@ -327,20 +331,10 @@ function processSource(process: boolean) {
|
||||
if (process) {
|
||||
if (editId.value === -1) {
|
||||
playlistStore.playlist.push(newSource.value)
|
||||
playlistStore.playlist = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
playlistStore.playlist,
|
||||
false
|
||||
)
|
||||
playlistStore.playlist = processPlaylist(listDate.value, playlistStore.playlist, false)
|
||||
} else {
|
||||
playlistStore.playlist[editId.value] = newSource.value
|
||||
playlistStore.playlist = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
playlistStore.playlist,
|
||||
false
|
||||
)
|
||||
playlistStore.playlist = processPlaylist(listDate.value, playlistStore.playlist, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,7 +393,7 @@ function loopClips() {
|
||||
}
|
||||
}
|
||||
|
||||
playlistStore.playlist = processPlaylist(configStore.startInSec, configStore.playlistLength, tempList, false)
|
||||
playlistStore.playlist = processPlaylist(listDate.value, tempList, false)
|
||||
}
|
||||
|
||||
function onFileChange(evt: any) {
|
||||
@ -455,12 +449,7 @@ async function savePlaylist(save: boolean) {
|
||||
return
|
||||
}
|
||||
|
||||
playlistStore.playlist = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
playlistStore.playlist,
|
||||
true
|
||||
)
|
||||
playlistStore.playlist = processPlaylist(listDate.value, playlistStore.playlist, true)
|
||||
const saveList = playlistStore.playlist.map(({ begin, ...item }) => item)
|
||||
|
||||
await $fetch(`/api/playlist/${configStore.configGui[configStore.configID].id}/`, {
|
||||
|
@ -2,14 +2,14 @@ import lodash from 'lodash'
|
||||
import type { LoDashStatic } from 'lodash'
|
||||
|
||||
declare module '#app' {
|
||||
interface NuxtApp {
|
||||
$_: LoDashStatic;
|
||||
}
|
||||
interface NuxtApp {
|
||||
$_: LoDashStatic
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$_: LoDashStatic;
|
||||
}
|
||||
interface ComponentCustomProperties {
|
||||
$_: LoDashStatic
|
||||
}
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import VueDatePicker from '@vuepic/vue-datepicker';
|
||||
import VueDatePicker from '@vuepic/vue-datepicker'
|
||||
import '@vuepic/vue-datepicker/dist/main.css'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
|
@ -10,9 +10,8 @@ export const useConfig = defineStore('config', {
|
||||
contentType: { 'content-type': 'application/json;charset=UTF-8' },
|
||||
configGui: [] as GuiConfig[],
|
||||
configGuiRaw: [] as GuiConfig[],
|
||||
startInSec: 0,
|
||||
playlistLength: 86400.0,
|
||||
configPlayout: {} as any,
|
||||
playout: {} as any,
|
||||
currentUser: '',
|
||||
configUser: {} as User,
|
||||
utcOffset: 0,
|
||||
@ -41,7 +40,7 @@ export const useConfig = defineStore('config', {
|
||||
method: 'GET',
|
||||
headers: authStore.authHeader,
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
statusCode = response.status
|
||||
|
||||
return response
|
||||
@ -128,19 +127,14 @@ export const useConfig = defineStore('config', {
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.playlist.day_start) {
|
||||
this.startInSec = timeToSeconds(data.playlist.day_start)
|
||||
}
|
||||
|
||||
if (data.playlist.length) {
|
||||
this.playlistLength = timeToSeconds(data.playlist.length)
|
||||
}
|
||||
data.playlist.startInSec = timeToSeconds(data.playlist.day_start ?? 0)
|
||||
data.playlist.lengthInSec = timeToSeconds(data.playlist.length ?? this.playlistLength)
|
||||
|
||||
if (data.storage.extensions) {
|
||||
data.storage.extensions = data.storage.extensions.join(',')
|
||||
}
|
||||
|
||||
this.configPlayout = data
|
||||
this.playout = data
|
||||
})
|
||||
.catch(() => {
|
||||
indexStore.msgAlert('error', 'No playout config found!', 3)
|
||||
@ -151,8 +145,9 @@ export const useConfig = defineStore('config', {
|
||||
const authStore = useAuth()
|
||||
const channel = this.configGui[this.configID].id
|
||||
|
||||
this.startInSec = timeToSeconds(obj.playlist.day_start)
|
||||
this.playlistLength = timeToSeconds(obj.playlist.length)
|
||||
this.playout.playlist.startInSec = timeToSeconds(obj.playlist.day_start)
|
||||
this.playout.playlist.lengthInSec = timeToSeconds(obj.playlist.length)
|
||||
|
||||
if (typeof obj.storage.extensions === 'string') {
|
||||
obj.storage.extensions = obj.storage.extensions.replace(' ', '').split(/,|;/)
|
||||
|
@ -7,7 +7,6 @@ import { defineStore } from 'pinia'
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
// const { timeToSeconds } = stringFormatter()
|
||||
const { processPlaylist } = playlistOperations()
|
||||
|
||||
export const usePlaylist = defineStore('playlist', {
|
||||
@ -34,15 +33,8 @@ export const usePlaylist = defineStore('playlist', {
|
||||
const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const indexStore = useIndex()
|
||||
let statusCode = 0
|
||||
|
||||
// const timeInSec = timeToSeconds(dayjs().utcOffset(configStore.utcOffset).format('HH:mm:ss'))
|
||||
const channel = configStore.configGui[configStore.configID].id
|
||||
// let dateToday = dayjs().utcOffset(configStore.utcOffset).format('YYYY-MM-DD')
|
||||
|
||||
// if (configStore.startInSec > timeInSec) {
|
||||
// dateToday = dayjs(dateToday).utcOffset(configStore.utcOffset).subtract(1, 'day').format('YYYY-MM-DD')
|
||||
// }
|
||||
let statusCode = 0
|
||||
|
||||
await fetch(`/api/playlist/${channel}?date=${date}`, {
|
||||
method: 'GET',
|
||||
@ -55,32 +47,29 @@ export const usePlaylist = defineStore('playlist', {
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.program) {
|
||||
const programData = processPlaylist(
|
||||
configStore.startInSec,
|
||||
configStore.playlistLength,
|
||||
data.program,
|
||||
false
|
||||
)
|
||||
const programData = processPlaylist(date, data.program, false)
|
||||
|
||||
if (
|
||||
this.playlist.length > 0 &&
|
||||
programData.length > 0 &&
|
||||
this.playlist[0].date === date &&
|
||||
$_.differenceWith(this.playlist, programData, (a, b) => {
|
||||
return $_.isEqual($_.omit(a, ['uid']), $_.omit(b, ['uid']))
|
||||
}).length > 0
|
||||
) {
|
||||
indexStore.msgAlert('warning', $i18n.t('player.unsavedProgram'), 3)
|
||||
} else if (this.playlist.length === 0) {
|
||||
this.playlist = programData
|
||||
} else {
|
||||
this.playlist = programData ?? []
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (statusCode > 0 && statusCode !== 204) {
|
||||
if (statusCode >= 400) {
|
||||
indexStore.msgAlert('error', e, 3)
|
||||
}
|
||||
|
||||
if (this.playlist.length > 0) {
|
||||
} else if (this.playlist.length > 0 && this.playlist[0].date === date) {
|
||||
indexStore.msgAlert('warning', $i18n.t('player.unsavedProgram'), 3)
|
||||
} else {
|
||||
this.playlist = []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ module.exports = {
|
||||
},
|
||||
boxShadow: {
|
||||
'3xl': '0 1em 5em rgba(0, 0, 0, 0.3)',
|
||||
'glow': '0 0 10px rgba(0, 0, 0, 0.3)',
|
||||
glow: '0 0 10px rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
colors: {
|
||||
'my-gray': 'var(--my-gray)',
|
||||
|
5
types/index.d.ts
vendored
5
types/index.d.ts
vendored
@ -48,6 +48,7 @@ declare global {
|
||||
}
|
||||
|
||||
interface PlaylistItem {
|
||||
date?: string
|
||||
uid: string
|
||||
begin: number
|
||||
source: string
|
||||
@ -57,7 +58,7 @@ declare global {
|
||||
audio?: string
|
||||
category?: string
|
||||
custom_filter?: string
|
||||
class?: string
|
||||
overtime?: boolean
|
||||
}
|
||||
|
||||
interface FileObject {
|
||||
@ -112,6 +113,6 @@ declare global {
|
||||
network?: { name: string; current_in: number; current_out: number; total_in: number; total_out: number }
|
||||
storage?: { path: string; total: number; used: number }
|
||||
swap: { total: number; used: number; free: number }
|
||||
system: { name?: string; kernel?: string; version?: string, ffp_version?: string }
|
||||
system: { name?: string; kernel?: string; version?: string; ffp_version?: string }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user