translate: player, media, logging

This commit is contained in:
jb-alvarado 2024-04-14 00:01:45 +02:00
parent da911dc210
commit 044fab637b
9 changed files with 176 additions and 66 deletions

View File

@ -50,8 +50,8 @@
<div class="col-span-1 xs:col-span-2 p-1">
<div class="w-full h-full bg-base-100 rounded flex items-center p-3 shadow">
<div class="w-full h-full flex flex-col">
<div v-if="playlistStore.ingestRuns" class="h-1/3 font-bold truncate" title="Live Ingest">
Live Ingest
<div v-if="playlistStore.ingestRuns" class="h-1/3 font-bold truncate">
{{ $t('control.ingest') }}
</div>
<div
v-else
@ -61,8 +61,8 @@
{{ filename(playlistStore.currentClip) }}
</div>
<div class="grow">
<strong>Duration:</strong> {{ secToHMS(playlistStore.currentClipDuration) }} |
<strong>In:</strong> {{ secToHMS(playlistStore.currentClipIn) }} | <strong>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">
@ -83,7 +83,7 @@
<div class="text-center">
<div class="w-full h-1/2 aspect-square p-2">
<button
title="Start Playout Service"
:title="$t('control.start')"
class="btn btn-primary h-full w-full text-7xl text-lime-600"
:class="playlistStore.playoutIsRunning && 'shadow-glow shadow-lime-600'"
@click="controlProcess('start')"
@ -93,7 +93,7 @@
</div>
<div class="w-full h-1/2 aspect-square p-2">
<button
title="Jump to last Clip"
:title="$t('control.last')"
class="btn btn-primary h-full w-full text-7xl text-cyan-600"
@click="controlPlayout('back')"
>
@ -105,7 +105,7 @@
<div class="text-center">
<div class="w-full h-1/2 aspect-square p-2">
<button
title="Stop Playout Service"
:title="$t('control.stop')"
class="btn btn-primary h-full w-full text-7xl text-red-600"
@click="controlProcess('stop')"
>
@ -115,7 +115,7 @@
<div class="w-full h-1/2 aspect-square p-2">
<button
title="Reset Playout State"
:title="$t('control.reset')"
class="btn btn-primary h-full w-full text-6xl text-cyan-600"
@click="controlPlayout('reset')"
>
@ -127,7 +127,7 @@
<div class="text-center">
<div class="w-full h-1/2 aspect-square p-2">
<button
title="Restart Playout Service"
:title="$t('control.restart')"
class="btn btn-primary h-full w-full text-6xl text-yellow-500"
@click="controlProcess('restart')"
>
@ -137,7 +137,7 @@
<div class="w-full h-1/2 aspect-square p-2">
<button
title="Jump to next Clip"
:title="$t('control.next')"
class="btn btn-primary h-full w-full text-7xl text-cyan-600"
@click="controlPlayout('next')"
>
@ -164,6 +164,7 @@ const { filename, secToHMS, timeToSeconds } = stringFormatter()
const { configID } = storeToRefs(useConfig())
const contentType = { 'content-type': 'application/json;charset=UTF-8' }
playlistStore.currentClip = 'Es wird kein Clip abgespielt'
const breakStatusCheck = ref(false)
const timeStr = ref('00:00:00')
const timer = ref()

View File

@ -37,4 +37,55 @@ export default {
size: 'Größe',
used: 'Genutzt',
},
control: {
noClip: 'Es wird kein Clip abgespielt',
ingest: 'Live-Übertragung',
start: 'Playout-Dienst starten',
last: 'Zum letzten Clip springen',
stop: 'Playout-Dienst stoppen',
reset: 'Playout-Zustand zurücksetzen',
restart: 'Playout-Dienst neu starten',
next: 'Zum nächsten Clip springen',
},
player: {
start: 'Start',
file: 'Datei',
play: 'Abspielen',
duration: 'Dauer',
in: 'Eingang',
out: 'Ausgang',
ad: 'Werbung',
edit: 'Bearbeiten',
delete: 'Löschen',
copy: 'Wiedergabeliste kopieren',
loop: 'Clips in Wiedergabeliste wiederholen',
remote: 'Externe Quelle zur Wiedergabeliste hinzufügen',
import: 'Text-/m3u-Datei importieren',
generate: 'Einfacher und erweiterter Wiedergabelisten-Generator',
reset: 'Wiedergabeliste zurücksetzen',
save: 'Wiedergabeliste speichern',
deletePlaylist: 'Wiedergabeliste löschen',
},
media: {
notExists: 'Speicher existiert nicht!',
create: 'Ordner erstellen',
upload: 'Dateien hochladen',
deleteTitle: 'Datei/Ordner löschen',
deleteQuestion: 'Sind Sie sicher, dass Sie löschen möchten',
preview: 'Vorschau',
rename: 'Datei umbenennen',
newFile: 'Neuer Dateiname',
createFolder: 'Ordner erstellen',
foldername: 'Ordnername',
current: 'Aktuell',
overall: 'Insgesamt',
uploading: 'Hochladen',
moveError: 'Fehler beim Verschieben',
deleteError: 'Löschfehler',
folderExists: 'Ordner existiert bereits',
folderCreate: 'Ordner erstellen abgeschlossen...',
folderError: 'Fehler beim Erstellen des Ordners',
uploadError: 'Fehler beim Hochladen',
fileExists: 'Datei existiert bereits!',
},
}

View File

@ -37,4 +37,55 @@ export default {
size: 'Size',
used: 'Used',
},
control: {
noClip: 'No clip is playing',
ingest: 'Live Ingest',
start: 'Start Playout Service',
last: 'Jump to last Clip',
stop: 'Stop Playout Service',
reset: 'Reset Playout State',
restart: 'Restart Playout Service',
next: 'Jump to next Clip',
},
player: {
start: 'Start',
file: 'File',
play: 'Play',
duration: 'Duration',
in: 'In',
out: 'Out',
ad: 'Ad',
edit: 'Edit',
delete: 'Delete',
copy: 'Copy Playlist',
loop: 'Loop Clips in Playlist',
remote: 'Add (remote) Source to Playlist',
import: 'Import text/m3u file',
generate: 'Simple and advanced playlist generator',
reset: 'Reset Playlist',
save: 'Save Playlist',
deletePlaylist: 'Delete Playlist',
},
media: {
notExists: 'Storage not exist!',
create: 'Create Folder',
upload: 'Upload Files',
deleteTitle: 'Delete File/Folder',
deleteQuestion: 'Are you sure that you want to delete',
preview: 'Preview',
rename: 'Rename File',
newFile: 'New filename',
createFolder: 'Create Folder',
foldername: 'Foldername',
current: 'Current',
overall: 'Overall',
uploading: 'Uploading',
moveError: 'Move error',
deleteError: 'Delete error',
folderExists: 'Folder exists already',
folderCreate: 'Folder create done...',
folderError: 'Folder create error',
uploadError: 'Upload error',
fileExists: 'File exists already!',
},
}

View File

@ -9,8 +9,9 @@
:format="calendarFormat"
model-type="yyyy-MM-dd"
auto-apply
:locale="locale"
:dark="colorMode.value === 'dark'"
input-class-name="input input-sm !input-bordered !w-[230px] text-right !pe-3"
input-class-name="input input-sm !input-bordered !w-[250px] text-right !pe-3"
required
/>
</div>
@ -24,6 +25,7 @@
import { storeToRefs } from 'pinia'
const colorMode = useColorMode()
const { locale } = useI18n()
useHead({
title: 'Logging | ffplayout',
@ -47,7 +49,7 @@ watch([listDate, configID], () => {
})
const calendarFormat = (date: Date) => {
return $dayjs(date).format('dddd DD. MMM YYYY')
return $dayjs(date).locale(locale.value).format('dddd LL')
}
async function getLog() {

View File

@ -158,10 +158,10 @@
<div class="flex justify-end py-4 pe-2">
<div class="join">
<button class="btn btn-sm btn-primary join-item" title="Create Folder" @click="showCreateModal = true">
<button class="btn btn-sm btn-primary join-item" :title="$t('media.create')" @click="showCreateModal = true">
<i class="bi-folder-plus" />
</button>
<button class="btn btn-sm btn-primary join-item" title="Upload File" @click="showUploadModal = true">
<button class="btn btn-sm btn-primary join-item" :title="$t('media.upload')" @click="showUploadModal = true">
<i class="bi-upload" />
</button>
</div>
@ -170,37 +170,37 @@
<Modal
:show="showDeleteModal"
title="Delete File/Folder"
:text="`Are you sure that you want to delete:<br /><strong>${deleteName}</strong>`"
:title="$t('media.deleteTitle')"
:text="`${$t('media.deleteQuestion')}:<br /><strong>${deleteName}</strong>`"
:modal-action="deleteFileOrFolder"
/>
<Modal :show="showPreviewModal" :title="`Preview: ${previewName}`" :modal-action="closePlayer">
<Modal :show="showPreviewModal" :title="`${$t('media.preview')}: ${previewName}`" :modal-action="closePlayer">
<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" />
</div>
</Modal>
<Modal :show="showRenameModal" title="Rename File" :modal-action="renameFile">
<Modal :show="showRenameModal" :title="$t('media.rename')" :modal-action="renameFile">
<label class="form-control w-full max-w-md">
<div class="label">
<span class="label-text">New filename</span>
<span class="label-text">{{ $t('media.newFile') }}</span>
</div>
<input type="text" class="input input-bordered w-full" v-model="renameNewName" />
</label>
</Modal>
<Modal :show="showCreateModal" title="Create Folder" :modal-action="createFolder">
<Modal :show="showCreateModal" :title="$t('media.createFolder')" :modal-action="createFolder">
<label class="form-control w-full max-w-md">
<div class="label">
<span class="label-text">Foldername</span>
<span class="label-text">{{ $t('media.foldername') }}</span>
</div>
<input type="text" class="input input-bordered w-full" v-model="folderName.name" />
</label>
</Modal>
<Modal :show="showUploadModal" title="Upload Files" :modal-action="uploadFiles">
<Modal :show="showUploadModal" :title="$t('media.upload')" :modal-action="uploadFiles">
<div class="w-[700px] max-w-full">
<input
type="file"
@ -213,20 +213,20 @@
<label class="form-control w-full mt-3">
<div class="label">
<span class="label-text">Current:</span>
<span class="label-text">{{ $t('media.current') }}:</span>
</div>
<progress class="progress progress-accent" :value="currentProgress" max="100" />
</label>
<label class="form-control w-full mt-1">
<div class="label">
<span class="label-text">Overall ({{ currentNumber }}/{{ inputFiles.length }}):</span>
<span class="label-text">{{ $t('media.overall') }} ({{ currentNumber }}/{{ inputFiles.length }}):</span>
</div>
<progress class="progress progress-accent" :value="overallProgress" max="100" />
</label>
<label class="form-control w-full mt-1">
<div class="label">
<span class="label-text">Uploading:</span>
<span class="label-text">{{ $t('media.uploading') }}:</span>
</div>
<input type="text" class="input input-sm input-bordered w-full" v-model="uploadTask" disabled />
</label>
@ -237,6 +237,7 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
const { t } = useI18n()
const { width } = useWindowSize({ initialWidth: 800 })
const authStore = useAuth()
const configStore = useConfig()
@ -367,7 +368,7 @@ async function handleDrop(event: any, targetFolder: any, isParent: boolean | nul
mediaStore.getTree(mediaStore.folderTree.source)
})
.catch((e) => {
indexStore.msgAlert('error', `Delete error: ${e}`, 3)
indexStore.msgAlert('error', `${t('media.moveError')}: ${e}`, 3)
})
}
}
@ -434,7 +435,7 @@ async function deleteFileOrFolder(del: boolean) {
mediaStore.getTree(mediaStore.folderTree.source)
})
.catch((e) => {
indexStore.msgAlert('error', `Delete error: ${e}`, 5)
indexStore.msgAlert('error', `${t('media.deleteError')}: ${e}`, 5)
})
}
@ -462,7 +463,7 @@ async function renameFile(ren: boolean) {
mediaStore.getTree(mediaStore.folderTree.source)
})
.catch((e) => {
indexStore.msgAlert('error', `Delete error: ${e}`, 3)
indexStore.msgAlert('error', `${t('media.moveError')}: ${e}`, 3)
})
}
@ -483,7 +484,7 @@ async function createFolder(create: boolean) {
lastPath.value = mediaStore.folderTree.source
if (mediaStore.folderTree.folders.includes(folderName.value)) {
indexStore.msgAlert('warning', `Folder "${folderName.value.name}" exists already!`, 2)
indexStore.msgAlert('warning', `${t('media.folderExists')}! "${folderName.value.name}"`, 2)
return
}
@ -494,10 +495,10 @@ async function createFolder(create: boolean) {
body: JSON.stringify({ source: path }),
})
.then(() => {
indexStore.msgAlert('success', 'Folder create done...', 2)
indexStore.msgAlert('success', t('media.folderCreate'), 2)
})
.catch((e: string) => {
indexStore.msgAlert('error', `Folder create error: ${e}`, 3)
indexStore.msgAlert('error', `${t('media.folderError')}: ${e}`, 3)
indexStore.alertVariant = 'error'
})
@ -537,7 +538,7 @@ async function upload(file: any): Promise<null | undefined> {
}
xhr.value.upload.onerror = () => {
indexStore.msgAlert('error', `Upload error: ${xhr.value.status}`, 3)
indexStore.msgAlert('error', `${t('media.folderError')}: ${xhr.value.status}`, 3)
resolve(undefined)
}
@ -564,7 +565,7 @@ async function uploadFiles(upl: boolean) {
currentNumber.value = i + 1
if (mediaStore.folderTree.files.find((f) => f.name === file.name)) {
indexStore.msgAlert('warning', 'File exists already!', 3)
indexStore.msgAlert('warning', t('media.fileExists'), 3)
} else {
await upload(file)
}

View File

@ -11,8 +11,9 @@
:format="calendarFormat"
model-type="yyyy-MM-dd"
auto-apply
:locale="locale"
:dark="colorMode.value === 'dark'"
input-class-name="input input-sm !input-bordered !w-[230px] text-right !pe-3"
input-class-name="input input-sm !input-bordered !w-[250px] text-right !pe-3"
required
/>
</div>
@ -102,17 +103,17 @@
<span class="loading loading-spinner loading-lg" />
</div>
<div
class="grid grid-cols-[70px_auto_50px_70px_45px] md:grid-cols-[70px_auto_50px_70px_70px_70px_30px_45px_50px] bg-base-100 rounded-tr-lg py-2 px-3 border-b border-my-gray"
class="grid grid-cols-[70px_auto_75px_70px_70px] md:grid-cols-[70px_auto_75px_70px_70px_70px_70px_80px_60px] bg-base-100 rounded-tr-lg py-2 px-3 border-b border-my-gray"
>
<div>Start</div>
<div>File</div>
<div class="text-center">Play</div>
<div class="">Duration</div>
<div class="hidden md:flex">In</div>
<div class="hidden md:flex">Out</div>
<div class="hidden md:flex justify-center">Ad</div>
<div class="text-center">Edit</div>
<div class="hidden md:flex justify-center">Delete</div>
<div>{{ $t('player.start') }}</div>
<div>{{ $t('player.file') }}</div>
<div class="text-center">{{ $t('player.play') }}</div>
<div class="">{{ $t('player.duration') }}</div>
<div class="hidden md:flex">{{ $t('player.in') }}</div>
<div class="hidden md:flex">{{ $t('player.out') }}</div>
<div class="hidden md:flex justify-center">{{ $t('player.ad') }}</div>
<div class="text-center">{{ $t('player.edit') }}</div>
<div class="hidden md:flex justify-center">{{ $t('player.delete') }}</div>
</div>
<div id="scroll-container" class="h-[calc(100%-44px)] overflow-auto">
<Sortable
@ -129,7 +130,7 @@
<template #item="{ element, index }">
<li
:id="`clip_${index}`"
class="draggable grid grid-cols-[70px_auto_50px_70px_45px] md:grid-cols-[70px_auto_50px_70px_70px_70px_30px_45px_50px] h-[38px] px-3 py-[8px]"
class="draggable grid grid-cols-[70px_auto_75px_70px_70px] md:grid-cols-[70px_auto_75px_70px_70px_70px_70px_80px_60px] h-[38px] px-3 py-[8px]"
:class="
index === playlistStore.currentClipIndex && listDate === todayDate
? 'bg-lime-500/30'
@ -183,48 +184,49 @@
</div>
<div class="h-16 join flex justify-end p-3">
<button class="btn btn-sm btn-primary join-item" title="Copy Playlist" @click="showCopyModal = true">
<button class="btn btn-sm btn-primary join-item" :title="$t('player.copy')" @click="showCopyModal = true">
<i class="bi-files" />
</button>
<button
v-if="!configStore.configPlayout.playlist.loop"
class="btn btn-sm btn-primary join-item"
title="Loop Clips in Playlist"
:title="$t('player.loop')"
@click="loopClips()"
>
<i class="bi-view-stacked" />
</button>
<button
class="btn btn-sm btn-primary join-item"
title="Add (remote) Source to Playlist"
:title="$t('player.remote')"
@click="showSourceModal = true"
>
<i class="bi-file-earmark-plus" />
</button>
<button
class="btn btn-sm btn-primary join-item"
title="Import text/m3u file"
:title="$t('player.import')"
@click="showImportModal = true"
>
<i class="bi-file-text" />
</button>
<button
class="btn btn-sm btn-primary join-item"
:title="$t('player.generate')"
@click="mediaStore.getTree('', true), (showPlaylistGenerator = true)"
>
<i class="bi-sort-down-alt" />
</button>
<button class="btn btn-sm btn-primary join-item" title="Reset Playlist" @click="getPlaylist()">
<button class="btn btn-sm btn-primary join-item" :title="$t('player.reset')" @click="getPlaylist()">
<i class="bi-arrow-counterclockwise" />
</button>
<button
class="btn btn-sm btn-primary join-item"
title="Save Playlist"
:title="$t('player.save')"
@click=";(targetDate = listDate), savePlaylist(true)"
>
<i class="bi-download" />
</button>
<button class="btn btn-sm btn-primary join-item" title="Delete Playlist" @click="showDeleteModal = true">
<button class="btn btn-sm btn-primary join-item" :title="$t('player.deletePlaylist')" @click="showDeleteModal = true">
<i class="bi-trash" />
</button>
</div>
@ -326,6 +328,7 @@
import { storeToRefs } from 'pinia'
const colorMode = useColorMode()
const { locale } = useI18n()
const { $_, $dayjs } = useNuxtApp()
const { width } = useWindowSize({ initialWidth: 800 })
const { secToHMS, filename, secondsToTime, toMin, mediaType } = stringFormatter()
@ -410,7 +413,7 @@ function scrollTo(index: number) {
}
const calendarFormat = (date: Date) => {
return $dayjs(date).format('dddd DD. MMM YYYY')
return $dayjs(date).locale(locale.value).format('dddd LL')
}
async function getPlaylist() {
@ -640,9 +643,9 @@ async function importPlaylist(imp: boolean) {
playlistStore.isLoading = true
await $fetch(
`/api/file/${configStore.configGui[configStore.configID].id}/import/?file=${
textFile.value[0].name
}&date=${listDate.value}`,
`/api/file/${configStore.configGui[configStore.configID].id}/import/?file=${textFile.value[0].name}&date=${
listDate.value
}`,
{
method: 'PUT',
headers: authStore.authHeader,

View File

@ -4,11 +4,11 @@ import LocalizedFormat from 'dayjs/plugin/localizedFormat.js'
import timezone from 'dayjs/plugin/timezone.js'
import utc from 'dayjs/plugin/utc.js'
// import 'dayjs/locale/de'
// import 'dayjs/locale/en'
// import 'dayjs/locale/es'
// import 'dayjs/locale/pt'
// import 'dayjs/locale/ru'
import 'dayjs/locale/de'
import 'dayjs/locale/en'
import 'dayjs/locale/es'
import 'dayjs/locale/pt'
import 'dayjs/locale/ru'
declare module '#app' {
interface NuxtApp {

View File

@ -19,6 +19,7 @@ export const useMedia = defineStore('media', {
this.isLoading = true
}
const { t } = useI18n()
const authStore = useAuth()
const configStore = useConfig()
const indexStore = useIndex()
@ -36,7 +37,7 @@ export const useMedia = defineStore('media', {
if (response.status === 200) {
return response.json()
} else {
indexStore.msgAlert('error', 'Storage not exist!', 3)
indexStore.msgAlert('error', t('media.notExists'), 3)
return {
source: '',
@ -47,12 +48,12 @@ export const useMedia = defineStore('media', {
}
})
.then((data) => {
const pathStr = 'Home/' + data.source
const pathStr = `${data.parent}/` + data.source
const pathArr = pathStr.split('/')
if (path && path !== '/') {
for (const crumb of pathArr) {
if (crumb === 'Home') {
if (crumb === data.parent) {
crumbs.push({ text: crumb, path: root })
} else if (crumb) {
root += crumb + '/'
@ -60,7 +61,7 @@ export const useMedia = defineStore('media', {
}
}
} else {
crumbs.push({ text: 'Home', path: '' })
crumbs.push({ text: data.parent, path: '' })
}
if (foldersOnly) {

View File

@ -16,7 +16,7 @@ export const usePlaylist = defineStore('playlist', {
isLoading: false,
listDate: dayjs().format('YYYY-MM-DD'),
progressValue: 0,
currentClip: 'No clip is playing',
currentClip: '',
currentClipIndex: 0,
currentClipStart: 0,
currentClipDuration: 0,