207 lines
6.4 KiB
Vue
207 lines
6.4 KiB
Vue
<script setup lang="ts">
|
|
import type { Channel, Upload } from '~/types'
|
|
|
|
import { computed, ref, reactive, watch } from 'vue'
|
|
import { whenever } from '@vueuse/core'
|
|
import { useStore } from '~/store'
|
|
|
|
import axios from 'axios'
|
|
|
|
import ChannelEntries from '~/components/audio/ChannelEntries.vue'
|
|
import ChannelSeries from '~/components/audio/ChannelSeries.vue'
|
|
import AlbumModal from '~/components/channels/AlbumModal.vue'
|
|
|
|
import useWebSocketHandler from '~/composables/useWebSocketHandler'
|
|
|
|
interface Props {
|
|
object: Channel
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const store = useStore()
|
|
|
|
const isPodcast = computed(() => props.object.artist?.content_category === 'podcast')
|
|
const isOwner = computed(() => store.state.auth.authenticated && props.object.attributed_to.full_username === store.state.auth.fullUsername)
|
|
|
|
const seriesFilters = computed(() => ({
|
|
artist: props.object.artist?.id,
|
|
ordering: '-creation_date',
|
|
playable: isOwner.value
|
|
? undefined
|
|
: true
|
|
}))
|
|
|
|
const pendingUploads = reactive([] as Upload[])
|
|
const processedUploads = computed(() => pendingUploads.filter(upload => upload.import_status !== 'pending'))
|
|
const finishedUploads = computed(() => pendingUploads.filter(upload => upload.import_status === 'finished'))
|
|
const erroredUploads = computed(() => pendingUploads.filter(upload => upload.import_status === 'errored'))
|
|
const skippedUploads = computed(() => pendingUploads.filter(upload => upload.import_status === 'skipped'))
|
|
|
|
const pendingUploadsById = computed(() => pendingUploads.reduce((acc, upload) => {
|
|
acc[upload.uuid] = upload
|
|
return acc
|
|
}, {} as Record<string, Upload>))
|
|
|
|
const isOver = computed(() => pendingUploads.length === processedUploads.value.length)
|
|
const isSuccessfull = computed(() => pendingUploads.length === finishedUploads.value.length)
|
|
|
|
watch(() => store.state.channels.latestPublication, (value) => {
|
|
if (value?.channel.uuid === props.object.uuid && value.uploads.length > 0) {
|
|
pendingUploads.push(...value.uploads)
|
|
}
|
|
})
|
|
|
|
const episodesKey = ref(new Date())
|
|
const seriesKey = ref(new Date())
|
|
whenever(isOver, () => {
|
|
episodesKey.value = new Date()
|
|
seriesKey.value = new Date()
|
|
})
|
|
|
|
const fetchPendingUploads = async () => {
|
|
try {
|
|
const response = await axios.get('uploads/', {
|
|
params: { channel: props.object.uuid, import_status: ['pending', 'skipped', 'errored'], include_channels: 'true' },
|
|
paramsSerializer: {
|
|
indexes: null
|
|
}
|
|
})
|
|
|
|
pendingUploads.length = 0
|
|
pendingUploads.push(...response.data.results)
|
|
} catch (error) {
|
|
|
|
}
|
|
}
|
|
|
|
if (isOwner.value) {
|
|
fetchPendingUploads()
|
|
.then(() => {
|
|
useWebSocketHandler('import.status_updated', (event) => {
|
|
if (!pendingUploadsById.value[event.upload.uuid]) return
|
|
Object.assign(pendingUploadsById.value[event.upload.uuid], event.upload)
|
|
})
|
|
})
|
|
}
|
|
|
|
const albumModal = ref()
|
|
</script>
|
|
|
|
<template>
|
|
<section>
|
|
<div
|
|
v-if="pendingUploads.length > 0"
|
|
class="ui info message"
|
|
>
|
|
<template v-if="isSuccessfull">
|
|
<i
|
|
role="button"
|
|
class="close icon"
|
|
@click="pendingUploads.length = 0"
|
|
/>
|
|
<h3 class="ui header">
|
|
{{ $t('views.channels.DetailOverview.uploadsSuccessHeader') }}
|
|
</h3>
|
|
<p>
|
|
{{ $t('views.channels.DetailOverview.uploadsProgress', {finished: processedUploads.length, total: pendingUploads.length}) }}
|
|
</p>
|
|
</template>
|
|
<template v-else-if="isOver">
|
|
<h3 class="ui header">
|
|
{{ $t('views.channels.DetailOverview.uploadsFailureHeader') }}
|
|
</h3>
|
|
<div class="ui hidden divider" />
|
|
<router-link
|
|
v-if="skippedUploads.length > 0"
|
|
class="ui basic button"
|
|
:to="{name: 'content.libraries.files', query: {q: 'status:skipped'}}"
|
|
>
|
|
{{ $t('views.channels.DetailOverview.skippedUploadsLink') }}
|
|
</router-link>
|
|
<router-link
|
|
v-if="erroredUploads.length > 0"
|
|
class="ui basic button"
|
|
:to="{name: 'content.libraries.files', query: {q: 'status:errored'}}"
|
|
>
|
|
{{ $t('views.channels.DetailOverview.erroredUploadsLink') }}
|
|
</router-link>
|
|
</template>
|
|
<template v-else>
|
|
<div class="ui inline right floated active loader" />
|
|
<h3 class="ui header">
|
|
{{ $t('views.channels.DetailOverview.uploadsProcessingHeader') }}
|
|
</h3>
|
|
<p>
|
|
{{ $t('views.channels.DetailOverview.uploadsProcessingMessage') }}
|
|
</p>
|
|
<p>
|
|
{{ $t('views.channels.DetailOverview.uploadsProgress', {finished: processedUploads.length, total: pendingUploads.length}) }}
|
|
</p>
|
|
</template>
|
|
</div>
|
|
<div v-if="$store.getters['ui/layoutVersion'] === 'small'">
|
|
<rendered-description
|
|
:content="object.artist?.description"
|
|
:update-url="`channels/${object.uuid}/`"
|
|
:can-update="false"
|
|
/>
|
|
<div class="ui hidden divider" />
|
|
</div>
|
|
<channel-entries
|
|
:key="String(episodesKey) + 'entries'"
|
|
:is-podcast="isPodcast"
|
|
:default-cover="object.artist?.cover"
|
|
:limit="25"
|
|
:filters="{channel: object.uuid, ordering: '-creation_date', page_size: '25'}"
|
|
>
|
|
<h2 class="ui header">
|
|
<span
|
|
v-if="isPodcast"
|
|
>
|
|
{{ $t('views.channels.DetailOverview.latestEpisodes') }}
|
|
</span>
|
|
<span
|
|
v-else
|
|
>
|
|
{{ $t('views.channels.DetailOverview.latestTracks') }}
|
|
</span>
|
|
</h2>
|
|
</channel-entries>
|
|
<div class="ui hidden divider" />
|
|
<channel-series
|
|
:key="String(seriesKey) + 'series'"
|
|
:filters="seriesFilters"
|
|
:is-podcast="isPodcast"
|
|
>
|
|
<h2 class="ui with-actions header">
|
|
<span
|
|
v-if="isPodcast"
|
|
>
|
|
{{ $t('views.channels.DetailOverview.seriesHeader') }}
|
|
</span>
|
|
<span
|
|
v-else
|
|
>
|
|
{{ $t('views.channels.DetailOverview.albumsHeader') }}
|
|
</span>
|
|
<div
|
|
v-if="isOwner"
|
|
class="actions"
|
|
>
|
|
<a @click.stop.prevent="albumModal.show = true">
|
|
<i class="plus icon" />
|
|
{{ $t('views.channels.DetailOverview.addAlbumLink') }}
|
|
</a>
|
|
</div>
|
|
</h2>
|
|
</channel-series>
|
|
<album-modal
|
|
v-if="isOwner"
|
|
ref="albumModal"
|
|
:channel="object"
|
|
@created="albumModal.show = false; seriesKey = new Date()"
|
|
/>
|
|
</section>
|
|
</template>
|