Newer
Older
<template>
<div>
<b-modal
ref="modalPlaylistSelector"
:title="$t('playlistSelector.title')"
:cancel-title="$t('cancel')"
size="lg"
>
<p v-if="loaded">
{{ $t('playlistSelector.currentPlaylistLabel') }}:
<span v-if="timeslot === null || timeslot.playlistId === null">
<i
><small>{{ $t('noneSetFeminine') }}</small></i
>
</span>
<span v-else>
{{ timeslot.playlistId }}<br />
<span v-if="currentPlaylistDescription">
{{ $t('showMeta.description') }}: <b>{{ currentPlaylistDescription }}</b></span
>
</span>
</p>
<div v-if="loaded">
<div v-if="playlists.length">
<b-table ref="playlistsTable" striped :fields="playlistsTableFields" :items="playlists">
<!-- Column: Entries
This column displays the number of entries of the playlist.
-->
<template #cell(entries)="data">
<span
v-b-tooltip.html="playlistToolTip(data.value)"
class="tw-underline hover:tw-no-underline tw-cursor-help"
>
{{ $t('playlistTable.items', { smart_count: data.value.length }) }}
</span>
</template>
<!-- Column: Duration
This column displays the number of entries of the playlist.
-->
<template #cell(duration)="data">
<span :class="{ 'is-mismatched': isMismatchedLength(data) }">
{{ playlistDuration(data.item) }}
<abbr v-if="isMismatchedLength(data)" :title="title(data)"> (?) </abbr>
</span>
</template>
<!-- Column: Actions
This column displays the available buttons for actions the user can
take on this playlist (e.g. editing and deleting).
-->
<template #cell(actions)="data">
<b-button-group size="sm">
<b-button
v-if="data.item.id !== timeslot.playlistId"
{{ $t('playlistTable.assign') }}
</b-button>
<b-button v-else variant="danger" @click="choose(null)">
{{ $t('playlistTable.unset') }}
</b-button>
</b-button-group>
</template>
</b-table>
</div>
<!-- If no playlists are available -->
<div v-else class="tw-mb-4">
{{ $t('playlistSelector.noPlaylistsAvailable') }}
</div>
</div>
<div v-else>
<img src="/assets/radio.gif" :alt="$t('loading')" />
</div>
<div>
<div class="tw-space-x-2">
<router-link :to="{ name: 'files' }" class="btn btn-default">
<b-button v-if="!audioUpload" v-model="audioFile" @click="toggleAudioUpload">
{{ $t('playlistSelector.uploadAudio') }}
</b-button>
</div>
<div v-if="audioUpload" class="tw-mt-4 tw-space-x-2 tw-flex">
<b-form-file v-model="audioFile" accept="audio/*" />
<b-button :disabled="!audioFile" @click="upload">
{{ $t('playlistSelector.upload') }}
</b-button>
</div>
</div>
</b-modal>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import prettyDate from '@/mixins/prettyDate'
import playlist from '@/mixins/playlist'
import { useTimeSlotStore } from '@/stores/timeslots'
import { calculateDurationSeconds } from '@/util'
export default {
mixins: [prettyDate, playlist],
props: {
timeslot: { type: Object, required: true },
},
data() {
return {
scheduleId: null,
audioFile: null,
audioUpload: false,
audioUploadError: '',
},
computed: {
loaded() {
return this.$store.state.playlists.loaded.playlists
},
playlistsTableFields() {
return [
{ key: 'id', label: this.$t('playlistTable.index') },
{ key: 'description', label: this.$t('playlistTable.description') },
{ key: 'entries', label: this.$t('playlistTable.entries') },
{ key: 'duration', label: this.$t('playlistTable.duration') },
{ key: 'actions', label: this.$t('playlistTable.actions'), class: 'text-right' },
]
},
timeslotDuration() {
return calculateDurationSeconds(this.timeslot.start, this.timeslot.end)
},
currentPlaylistDescription() {
let description = false
if (this.timeslot && this.timeslot.playlistId !== null) {
const choosenList = this.playlists.find((list) => list.id === this.timeslot.playlistId)
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
if (choosenList && choosenList.description.length > 0) {
description = choosenList.description
}
}
return description
},
...mapGetters({
selectedShow: 'shows/selectedShow',
timeslots: 'shows/timeslots',
playlists: 'playlists/playlists',
getTimeslotById: 'shows/getTimeslotById',
}),
},
methods: {
upload(event) {
event.preventDefault()
event.stopPropagation()
if (!this.audioFile) {
this.audioUploadError = this.$t('playlistSelector.missingFile')
return
}
this.audioUpload = false
this.$store.dispatch('files/addFile', {
showSlug: this.selectedShow.slug,
uploadSourceFile: this.audioFile,
callback: this.handleUploadedFile,
})
},
handleUploadedFile() {
this.$store.dispatch('files/fetchFiles', {
showSlug: this.selectedShow.slug,
callback: this.createPlaylistForUploadedFile,
})
},
createPlaylistForUploadedFile(files) {
const file = files.slice(-1)[0]
const { slug } = this.selectedShow
const playlist = {
description: 'Automatisch erstellt durch Dateiupload',
entries: [{ file }],
}
this.$store.dispatch('playlists/add', {
slug,
playlist,
})
},
toggleAudioUpload() {
this.audioUpload = !this.audioUpload
},
this.audioFile = null
this.audioUpload = false
this.scheduleId = scheduleId
this.$refs.modalPlaylistSelector.show()
},
const { item } = data || {}
const { id } = item || {}
let confirmed = true
if (data && this.isMismatchedLength(data)) {
confirmed = confirm(this.$t('playlistSelector.mismatchedLengthConfirmation'))
}
if (confirmed) {
const timeslotStore = useTimeSlotStore()
await timeslotStore.partialUpdate(this.timeslot.id, { playlistId: id ?? null })
}
},
title(data) {
if (this.isMismatchedLength(data)) {
return this.$t('playlistSelector.mismatchedLength')
}
return ''
},
playlistToolTip(entries) {
let text = '<div style="white-space: nowrap;" align="left">'
for (const i in entries) {
text += i + ': ' + entries[i].uri + '<br>'
}
text += '</div>'
return text
},
isMismatchedLength(playlist) {
const totalDuration = this.playlistDuration(playlist.item)
let delta = 0
const unknowns = (playlist.item.entries ?? []).filter((entry) => !entry.duration)
if (unknowns.length === 1) {
delta = this.timeslotDurationInNs - totalDuration
}
return this.timeslotDurationInNs !== totalDuration + delta
},
},
}
</script>