revert ad color, fix path names, throttle buttons. work on channel creation

This commit is contained in:
Jonathan Baecker 2024-10-01 18:19:14 +02:00
parent c3da356bc7
commit 64150a8262
17 changed files with 102 additions and 83 deletions

View File

@ -50,10 +50,10 @@
</div>
<label class="form-control w-full mt-3">
<div class="label">
<span class="label-text">{{ t('config.hlsPath') }}</span>
<span class="label-text">{{ t('config.publicPath') }}</span>
</div>
<input
v-model="configStore.channels[configStore.id].hls_path"
v-model="configStore.channels[configStore.id].public"
type="text"
class="input input-bordered w-full"
/>
@ -64,7 +64,7 @@
<span class="label-text">{{ t('config.playlistPath') }}</span>
</div>
<input
v-model="configStore.channels[configStore.id].playlist_path"
v-model="configStore.channels[configStore.id].playlists"
type="text"
class="input input-bordered w-full"
/>
@ -75,7 +75,7 @@
<span class="label-text">{{ t('config.storagePath') }}</span>
</div>
<input
v-model="configStore.channels[configStore.id].storage_path"
v-model="configStore.channels[configStore.id].storage"
type="text"
class="input input-bordered w-full"
/>
@ -83,7 +83,7 @@
</template>
<div class="join my-4">
<button class="join-item btn btn-primary" @click="addUpdateChannel()">
<button class="join-item btn btn-primary" :class="saved ? 'btn-primary' : 'btn-error'" @click="addUpdateChannel()">
{{ t('config.save') }}
</button>
<button
@ -103,31 +103,36 @@
</template>
<script setup lang="ts">
const { $_ } = useNuxtApp()
import { cloneDeep } from 'lodash-es'
const { t } = useI18n()
const authStore = useAuth()
const configStore = useConfig()
const indexStore = useIndex()
const saved = ref(true)
function rmId(path: string) {
return path.replace(/\/\d+$/, '')
}
function newChannel() {
const channels = $_.cloneDeep(configStore.channels)
const newChannel = $_.cloneDeep(configStore.channels[configStore.channels.length - 1])
const channels = cloneDeep(configStore.channels)
const newChannel = cloneDeep(configStore.channels[configStore.channels.length - 1])
newChannel.id = channels.length + 1
newChannel.name = `Channel ${newChannel.id}`
newChannel.preview_url = `${window.location.protocol}//${window.location.host}/${newChannel.id}/live/stream.m3u8`
newChannel.hls_path = `${rmId(newChannel.hls_path)}/${newChannel.id}`
newChannel.playlist_path = `${rmId(newChannel.playlist_path)}/${newChannel.id}`
newChannel.storage_path = `${rmId(newChannel.storage_path)}/${newChannel.id}`
newChannel.public = `${rmId(newChannel.public)}/${newChannel.id}`
newChannel.playlists = `${rmId(newChannel.playlists)}/${newChannel.id}`
newChannel.storage = `${rmId(newChannel.storage)}/${newChannel.id}`
channels.push(newChannel)
configStore.channels = channels
configStore.id = configStore.channels.length - 1
saved.value = false
}
async function addUpdateChannel() {
@ -138,13 +143,14 @@ async function addUpdateChannel() {
if (update.status && update.status < 400) {
indexStore.msgAlert('success', t('config.updateChannelSuccess'), 2)
saved.value = true
} else {
indexStore.msgAlert('error', t('config.updateChannelFailed'), 2)
}
}
async function deleteChannel() {
const config = $_.cloneDeep(configStore.channels)
const config = cloneDeep(configStore.channels)
const id = config[configStore.id].id
if (id === 1) {

View File

@ -67,8 +67,8 @@
</div>
<div class="grow">
<strong>{{ t('player.duration') }}:</strong>
{{ secToHMS(playlistStore.current.duration) }} |
<strong>{{ t('player.in') }}:</strong> {{ secToHMS(playlistStore.current.in) }} |
{{ secToHMS(playlistStore.current.duration) }} | <strong>{{ t('player.in') }}:</strong>
{{ secToHMS(playlistStore.current.in) }} |
<strong>{{ t('player.out') }}:</strong>
{{ secToHMS(playlistStore.current.out) }}
@ -80,7 +80,11 @@
<div class="h-1/3">
<progress
class="progress progress-accent w-full"
:value="playlistStore.progressValue"
:value="
playlistStore.progressValue && playlistStore.progressValue <= 100
? playlistStore.progressValue
: 0
"
max="100"
/>
</div>
@ -165,6 +169,7 @@
</template>
<script setup lang="ts">
import { throttle } from 'lodash-es'
import { storeToRefs } from 'pinia'
import mpegts from 'mpegts.js'
@ -203,9 +208,7 @@ const mpegtsOptions = ref({
liveBufferLatencyChasing: true,
})
const streamUrl = ref(
`/data/event/${configStore.channels[configStore.id].id}?endpoint=playout&uuid=${authStore.uuid}`
)
const streamUrl = ref(`/data/event/${configStore.channels[configStore.id].id}?endpoint=playout&uuid=${authStore.uuid}`)
// 'http://127.0.0.1:8787/data/event/1?endpoint=playout&uuid=f2f8c29b-712a-48c5-8919-b535d3a05a3a'
const { status, data, error, close } = useEventSource(streamUrl, [], {
@ -256,9 +259,9 @@ watch([status, error], async () => {
if (errorCounter.value > 11) {
await authStore.obtainUuid()
streamUrl.value = `/data/event/${
configStore.channels[configStore.id].id
}?endpoint=playout&uuid=${authStore.uuid}`
streamUrl.value = `/data/event/${configStore.channels[configStore.id].id}?endpoint=playout&uuid=${
authStore.uuid
}`
errorCounter.value = 0
}
}
@ -280,9 +283,7 @@ watch([data], () => {
watch([id], () => {
resetStatus()
streamUrl.value = `/data/event/${configStore.channels[configStore.id].id}?endpoint=playout&uuid=${
authStore.uuid
}`
streamUrl.value = `/data/event/${configStore.channels[configStore.id].id}?endpoint=playout&uuid=${authStore.uuid}`
if (timer.value) {
clearTimeout(timer.value)
@ -313,20 +314,19 @@ function resetStatus() {
playlistStore.current = currentDefault
}
async function controlProcess(state: string) {
const controlProcess = throttle(async (state: string) => {
/*
Control playout (start, stop, restart)
*/
const channel = configStore.channels[configStore.id].id
await $fetch(`/api/control/${channel}/process/`, {
method: 'POST',
headers: { ...configStore.contentType, ...authStore.authHeader },
body: JSON.stringify({ command: state }),
})
}
}, 800)
async function controlPlayout(state: string) {
const controlPlayout = throttle(async (state: string) => {
/*
Control playout:
- jump to next clip
@ -340,5 +340,5 @@ async function controlPlayout(state: string) {
headers: { ...configStore.contentType, ...authStore.authHeader },
body: JSON.stringify({ control: state }),
})
}
}, 800)
</script>

View File

@ -347,7 +347,7 @@ function addFolderToTemplate(event: any, item: TemplateItem) {
event.item.remove()
const storagePath = configStore.channels[configStore.id].storage_path
const storagePath = configStore.channels[configStore.id].storage
const navPath = mediaStore.folderCrumbs[mediaStore.folderCrumbs.length - 1].path
const sourcePath = `${storagePath}/${navPath}/${mediaStore.folderList.folders[o].name}`.replace(/\/[/]+/g, '/')

View File

@ -78,7 +78,7 @@
'!bg-lime-500/30':
playlistStore.playoutIsRunning && listDate === todayDate && index === currentIndex,
'!bg-amber-600/40': element.overtime,
'text-blue-300': element.category === 'advertisement',
'text-base-content/60': element.category === 'advertisement',
}"
>
<td v-if="!configStore.playout.playlist.infinit" class="ps-4 py-2 text-left">
@ -259,7 +259,7 @@ function addClip(event: any) {
event.item?.remove()
const storagePath = configStore.channels[configStore.id].storage_path
const storagePath = configStore.channels[configStore.id].storage
const sourcePath = `${storagePath}/${mediaStore.folderTree.source}/${mediaStore.folderTree.files[o].name}`.replace(
/\/[/]+/g,
'/'

View File

@ -200,7 +200,7 @@ export default {
updatePlayoutFailed: 'Update playout config fehlgeschlagen!',
forbiddenPlaylistPath: 'Zugriff untersagt: Playlist-Ordner kann nicht geöffnet werden.',
noPlayoutConfig: 'Keine Playout-Konfiguration gefunden!',
hlsPath: 'HLS-Pfad',
publicPath: 'Public (HLS) Pfad',
playlistPath: 'Wiedergabelistenpfad',
storagePath: 'Speicherpfad',
sharedStorage: 'Gemeinsamer Speicher ist aktiviert, verwende denselben Speicherstamm für alle Kanäle!',

View File

@ -199,7 +199,7 @@ export default {
updatePlayoutFailed: 'Update playout config failed!',
forbiddenPlaylistPath: 'Access forbidden: Playlist folder cannot be opened.',
noPlayoutConfig: 'No playout config found!',
hlsPath: 'HLS Path',
publicPath: 'Public (HLS) Path',
playlistPath: 'Playlist Path',
storagePath: 'Storage Path',
sharedStorage: 'Shared storage is enabled, use the same storage root for all channels!',

View File

@ -199,7 +199,7 @@ export default {
updatePlayoutFailed: 'Falha na atualização da configuração do playout!',
forbiddenPlaylistPath: 'Acesso proibido: A pasta da lista de reprodução não pode ser aberta',
noPlayoutConfig: 'Nenhuma configuração de playout encontrada!',
hlsPath: 'HLS Path',
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',

View File

@ -199,7 +199,7 @@ export default {
updatePlayoutFailed: 'Обновление конфигурации воспроизведения не удалось!',
forbiddenPlaylistPath: 'Доступ запрещен: Папка плейлиста не может быть открыта.',
noPlayoutConfig: 'Конфигурация воспроизведения не найдена!',
hlsPath: 'HLS Path',
publicPath: 'Public (HLS) Path',
playlistPath: 'Playlist Path',
storagePath: 'Storage Path',
sharedStorage: 'Общее хранилище включено, используйте один и тот же корень хранилища для всех каналов!',

View File

@ -17,7 +17,7 @@
"bootstrap-icons": "^1.11.3",
"dayjs": "^1.11.13",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"mpegts.js": "^1.7.3",
"nuxt": "3.13.2",
"pinia": "^2.2.2",
@ -29,7 +29,7 @@
"@nuxt/eslint": "^0.5.7",
"@nuxtjs/i18n": "^8.5.5",
"@nuxtjs/tailwindcss": "^6.12.1",
"@types/lodash": "^4.17.9",
"@types/lodash-es": "^4.17.12",
"@types/video.js": "^7.3.58",
"daisyui": "^4.12.10",
"mini-svg-data-uri": "^1.4.4",
@ -3558,6 +3558,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/node": {
"version": "22.7.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
@ -8988,6 +8998,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",

View File

@ -21,7 +21,7 @@
"bootstrap-icons": "^1.11.3",
"dayjs": "^1.11.13",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"mpegts.js": "^1.7.3",
"nuxt": "3.13.2",
"pinia": "^2.2.2",
@ -33,7 +33,7 @@
"@nuxt/eslint": "^0.5.7",
"@nuxtjs/i18n": "^8.5.5",
"@nuxtjs/tailwindcss": "^6.12.1",
"@types/lodash": "^4.17.9",
"@types/lodash-es": "^4.17.12",
"@types/video.js": "^7.3.58",
"daisyui": "^4.12.10",
"mini-svg-data-uri": "^1.4.4",

View File

@ -329,6 +329,7 @@ watch([width], () => {
const horizontal = ref(false)
const deleteName = ref('')
const renameOldName = ref('')
const renameOldPath = ref('')
const renameNewName = ref('')
const previewName = ref('')
const previewUrl = ref('')
@ -513,8 +514,13 @@ async function deleteFileOrFolder(del: boolean) {
}
function setRenameValues(path: string) {
renameNewName.value = path
renameOldName.value = path
const index = path.lastIndexOf('/')
const dir = path.substring(0, index + 1) || '/'
const file = path.substring(index + 1)
renameOldName.value = file
renameOldPath.value = dir
renameNewName.value = file
}
async function renameFile(ren: boolean) {
@ -527,7 +533,10 @@ async function renameFile(ren: boolean) {
await fetch(`/api/file/${configStore.channels[configStore.id].id}/rename/`, {
method: 'POST',
headers: { ...configStore.contentType, ...authStore.authHeader },
body: JSON.stringify({ source: renameOldName.value, target: renameNewName.value }),
body: JSON.stringify({
source: `${renameOldPath.value}${renameOldName.value}`,
target: `${renameOldPath.value}${renameNewName.value}`,
}),
})
.then(async (res) => {
if (res.status >= 400) {
@ -542,6 +551,7 @@ async function renameFile(ren: boolean) {
}
renameOldName.value = ''
renameOldPath.value = ''
renameNewName.value = ''
}

View File

@ -174,7 +174,7 @@
v-model="newSource.source"
type="text"
class="input input-sm input-bordered w-auto"
:disabled="newSource.source.includes(configStore.channels[configStore.id].storage_path)"
:disabled="newSource.source.includes(configStore.channels[configStore.id].storage)"
/>
</label>
@ -230,9 +230,11 @@
</template>
<script setup lang="ts">
import { cloneDeep } from 'lodash-es'
const colorMode = useColorMode()
const { locale, t } = useI18n()
const { $_, $dayjs } = useNuxtApp()
const { $dayjs } = useNuxtApp()
const { width } = useWindowSize({ initialWidth: 800 })
const { mediaType } = stringFormatter()
const { processPlaylist, genUID } = playlistOperations()
@ -314,7 +316,7 @@ function closePlayer() {
function setPreviewData(path: string) {
let fullPath = path
const storagePath = configStore.channels[configStore.id].storage_path
const storagePath = configStore.channels[configStore.id].storage
const lastIndex = storagePath.lastIndexOf('/')
if (!path.includes('/')) {
@ -428,7 +430,7 @@ function loopClips() {
for (const item of playlistStore.playlist) {
if (length < configStore.playlistLength) {
item.uid = genUID()
tempList.push($_.cloneDeep(item))
tempList.push(cloneDeep(item))
length += item.out - item.in
} else {
break
@ -493,7 +495,7 @@ async function savePlaylist(save: boolean) {
return
}
const saveList = processPlaylist(listDate.value, $_.cloneDeep(playlistStore.playlist), true)
const saveList = processPlaylist(listDate.value, cloneDeep(playlistStore.playlist), true)
await $fetch(`/api/playlist/${configStore.channels[configStore.id].id}/`, {
method: 'POST',

View File

@ -1,17 +0,0 @@
import lodash from 'lodash'
import type { LoDashStatic } from 'lodash'
declare module '#app' {
interface NuxtApp {
$_: LoDashStatic
}
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$_: LoDashStatic
}
}
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.provide('_', lodash)
})

View File

@ -40,10 +40,10 @@ export const useAuth = defineStore('auth', {
password,
}
await $fetch<LoginObj>('/auth/login/', {
await $fetch('/auth/login/', {
method: 'POST',
body: JSON.stringify(payload),
async onResponse({ response }) {
async onResponse(response: LoginObj) {
code = response.status
},
})
@ -60,7 +60,7 @@ export const useAuth = defineStore('auth', {
},
async obtainUuid() {
await $fetch<DataAuth>('/api/generate-uuid', {
await $fetch('/api/generate-uuid', {
method: 'POST',
headers: this.authHeader,
})

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { cloneDeep } from 'lodash-es'
import { defineStore } from 'pinia'
export const useConfig = defineStore('config', {
@ -70,7 +70,7 @@ export const useConfig = defineStore('config', {
this.utcOffset = objs[0].utc_offset
this.channels = objs
this.channelsRaw = _.cloneDeep(objs)
this.channelsRaw = cloneDeep(objs)
this.configCount = objs.length
})
.catch((e) => {
@ -84,9 +84,9 @@ export const useConfig = defineStore('config', {
extra_extensions: '',
name: 'Channel 1',
preview_url: '',
hls_path: '',
playlist_path: '',
storage_path: '',
public: '',
playlists: '',
storage: '',
uts_offset: 0,
},
]
@ -97,7 +97,7 @@ export const useConfig = defineStore('config', {
async setChannelConfig(obj: Channel): Promise<any> {
const authStore = useAuth()
const stringObj = _.cloneDeep(obj)
const stringObj = cloneDeep(obj)
let response
if (this.channelsRaw.some((e) => e.id === stringObj.id)) {
@ -125,7 +125,7 @@ export const useConfig = defineStore('config', {
}
this.channels = guiConfigs
this.channelsRaw = _.cloneDeep(guiConfigs)
this.channelsRaw = cloneDeep(guiConfigs)
this.configCount = guiConfigs.length
}

View File

@ -1,4 +1,5 @@
import dayjs from 'dayjs'
import { differenceWith, isEqual, omit } from 'lodash-es'
import utc from 'dayjs/plugin/utc.js'
import timezone from 'dayjs/plugin/timezone.js'
@ -28,13 +29,13 @@ export const usePlaylist = defineStore('playlist', {
getters: {},
actions: {
async getPlaylist(date: string) {
const { $_, $i18n } = useNuxtApp()
const { $i18n } = useNuxtApp()
const authStore = useAuth()
const configStore = useConfig()
const indexStore = useIndex()
const channel = configStore.channels[configStore.id].id
await $fetch<Playlist>(`/api/playlist/${channel}?date=${date}`, {
await $fetch(`/api/playlist/${channel}?date=${date}`, {
method: 'GET',
headers: authStore.authHeader,
})
@ -47,8 +48,8 @@ export const usePlaylist = defineStore('playlist', {
this.playlist.length > 0 &&
programData.length > 0 &&
(this.playlist[0].date === date || configStore.playout.playlist.infinit) &&
$_.differenceWith(this.playlist, programData, (a, b) => {
return $_.isEqual($_.omit(a, ['uid']), $_.omit(b, ['uid']))
differenceWith(this.playlist, programData, (a, b) => {
return isEqual(omit(a, ['uid']), omit(b, ['uid']))
}).length > 0
) {
indexStore.msgAlert('warning', $i18n.t('player.unsavedProgram'), 3)

View File

@ -11,6 +11,7 @@ declare global {
interface LoginObj {
message: string
status: number
user?: {
id: number
mail: string
@ -28,9 +29,9 @@ declare global {
extra_extensions: string | string[]
name: string
preview_url: string
hls_path: string
playlist_path: string
storage_path: string
public: string
playlists: string
storage: string
uts_offset?: number
}