Fix play button in albums with multi-page volumes
This commit is contained in:
parent
b7355c9c95
commit
2c9327fefc
|
@ -0,0 +1 @@
|
||||||
|
Fix play button in albums with multi-page volumes (#1928)
|
|
@ -3,7 +3,7 @@ import type { Track, Album, Artist, Library } from '~/types'
|
||||||
|
|
||||||
import { momentFormat } from '~/utils/filters'
|
import { momentFormat } from '~/utils/filters'
|
||||||
import { useGettext } from 'vue3-gettext'
|
import { useGettext } from 'vue3-gettext'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, reactive, ref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { sum } from 'lodash-es'
|
import { sum } from 'lodash-es'
|
||||||
|
|
||||||
|
@ -29,9 +29,7 @@ const props = defineProps<Props>()
|
||||||
|
|
||||||
const object = ref<Album | null>(null)
|
const object = ref<Album | null>(null)
|
||||||
const artist = ref<Artist | null>(null)
|
const artist = ref<Artist | null>(null)
|
||||||
const discs = ref([] as Track[][])
|
|
||||||
const libraries = ref([] as Library[])
|
const libraries = ref([] as Library[])
|
||||||
const page = ref(1)
|
|
||||||
const paginateBy = ref(50)
|
const paginateBy = ref(50)
|
||||||
|
|
||||||
const totalTracks = computed(() => object.value?.tracks_count ?? 0)
|
const totalTracks = computed(() => object.value?.tracks_count ?? 0)
|
||||||
|
@ -51,18 +49,7 @@ const fetchData = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|
||||||
const albumResponse = await axios.get(`albums/${props.id}/`, { params: { refresh: 'true' } })
|
const albumResponse = await axios.get(`albums/${props.id}/`, { params: { refresh: 'true' } })
|
||||||
const [artistResponse, tracksResponse] = await Promise.all([
|
const artistResponse = await axios.get(`artists/${albumResponse.data.artist.id}/`)
|
||||||
axios.get(`artists/${albumResponse.data.artist.id}/`),
|
|
||||||
axios.get('tracks/', {
|
|
||||||
params: {
|
|
||||||
ordering: 'disc_number,position',
|
|
||||||
album: props.id,
|
|
||||||
page_size: paginateBy.value,
|
|
||||||
page: page.value,
|
|
||||||
include_channels: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
])
|
|
||||||
|
|
||||||
artist.value = artistResponse.data
|
artist.value = artistResponse.data
|
||||||
if (artist.value?.channel) {
|
if (artist.value?.channel) {
|
||||||
|
@ -71,20 +58,48 @@ const fetchData = async () => {
|
||||||
|
|
||||||
object.value = albumResponse.data
|
object.value = albumResponse.data
|
||||||
if (object.value) {
|
if (object.value) {
|
||||||
object.value.tracks = tracksResponse.data.results
|
object.value.tracks = []
|
||||||
discs.value = object.value.tracks.reduce((acc: Track[][], track: Track) => {
|
|
||||||
const discNumber = track.disc_number - (object.value?.tracks[0]?.disc_number ?? 1)
|
|
||||||
acc[discNumber] ??= []
|
|
||||||
acc[discNumber].push(track)
|
|
||||||
return acc
|
|
||||||
}, [])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchTracks()
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tracks = reactive([] as Track[])
|
||||||
|
watch(tracks, (tracks) => {
|
||||||
|
if (object.value) {
|
||||||
|
object.value.tracks = tracks
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const isLoadingTracks = ref(false)
|
||||||
|
const fetchTracks = async () => {
|
||||||
|
if (isLoadingTracks.value) return
|
||||||
|
isLoadingTracks.value = true
|
||||||
|
tracks.length = 0
|
||||||
|
let url = 'tracks/'
|
||||||
|
try {
|
||||||
|
while (url) {
|
||||||
|
const response = await axios.get(url, {
|
||||||
|
params: {
|
||||||
|
ordering: 'disc_number,position',
|
||||||
|
album: props.id,
|
||||||
|
page_size: paginateBy.value,
|
||||||
|
include_channels: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
url = response.data.next
|
||||||
|
tracks.push(...response.data.results)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
isLoadingTracks.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(() => props.id, fetchData, { immediate: true })
|
watch(() => props.id, fetchData, { immediate: true })
|
||||||
watch(page, fetchData)
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const remove = async () => {
|
const remove = async () => {
|
||||||
|
@ -337,15 +352,13 @@ const remove = async () => {
|
||||||
v-if="object"
|
v-if="object"
|
||||||
:key="$route.fullPath"
|
:key="$route.fullPath"
|
||||||
:paginate-by="paginateBy"
|
:paginate-by="paginateBy"
|
||||||
:page="page"
|
|
||||||
:total-tracks="totalTracks"
|
:total-tracks="totalTracks"
|
||||||
:is-serie="isSerie"
|
:is-serie="isSerie"
|
||||||
:artist="artist"
|
:artist="artist"
|
||||||
:discs="discs"
|
|
||||||
:object="object"
|
:object="object"
|
||||||
|
:is-loading-tracks="isLoadingTracks"
|
||||||
object-type="album"
|
object-type="album"
|
||||||
@libraries-loaded="libraries = $event"
|
@libraries-loaded="libraries = $event"
|
||||||
@page-changed="page = $event"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,32 +6,59 @@ import ChannelEntries from '~/components/audio/ChannelEntries.vue'
|
||||||
import TrackTable from '~/components/audio/track/Table.vue'
|
import TrackTable from '~/components/audio/track/Table.vue'
|
||||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||||
import Pagination from '~/components/vui/Pagination.vue'
|
import Pagination from '~/components/vui/Pagination.vue'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
interface Events {
|
interface Events {
|
||||||
(e: 'page-changed', page: number): void
|
|
||||||
(e: 'libraries-loaded', libraries: Library[]): void
|
(e: 'libraries-loaded', libraries: Library[]): void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
object: Album
|
object: Album
|
||||||
|
|
||||||
discs: Track[][]
|
isLoadingTracks: boolean
|
||||||
|
|
||||||
isSerie: boolean
|
isSerie: boolean
|
||||||
artist: Artist
|
artist: Artist
|
||||||
page: number
|
|
||||||
paginateBy: number
|
paginateBy: number
|
||||||
totalTracks: number
|
totalTracks: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
const emit = defineEmits<Events>()
|
||||||
defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const getDiscKey = (disc: Track[]) => disc?.map(track => track.id).join('|') ?? ''
|
||||||
|
const page = ref(1)
|
||||||
|
|
||||||
|
const discCount = computed(() => props.object.tracks.reduce((acc, track) => {
|
||||||
|
acc.add(track.disc_number)
|
||||||
|
return acc
|
||||||
|
}, new Set()).size)
|
||||||
|
|
||||||
|
const discs = computed(() => props.object.tracks
|
||||||
|
.reduce((acc: Track[][], track: Track) => {
|
||||||
|
const discNumber = track.disc_number - (props.object.tracks[0]?.disc_number ?? 1)
|
||||||
|
acc[discNumber].push(track)
|
||||||
|
return acc
|
||||||
|
}, Array(discCount.value).fill(undefined).map(() => []))
|
||||||
|
)
|
||||||
|
|
||||||
|
const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy * (page.value - 1), props.paginateBy * page.value)
|
||||||
|
.reduce((acc: Track[][], track: Track) => {
|
||||||
|
const discNumber = track.disc_number - (props.object.tracks[0]?.disc_number ?? 1)
|
||||||
|
acc[discNumber].push(track)
|
||||||
|
return acc
|
||||||
|
}, Array(discCount.value).fill(undefined).map(() => []))
|
||||||
|
)
|
||||||
|
|
||||||
const getDiscKey = (disc: Track[]) => disc.map(track => track.id).join('|')
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="object">
|
<div
|
||||||
|
v-if="isLoadingTracks"
|
||||||
|
class="ui vertical segment"
|
||||||
|
>
|
||||||
|
<div :class="['ui', 'centered', 'active', 'inline', 'loader']" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="object">
|
||||||
<h2 class="ui header">
|
<h2 class="ui header">
|
||||||
<translate
|
<translate
|
||||||
v-if="isSerie"
|
v-if="isSerie"
|
||||||
|
@ -46,67 +73,69 @@ const getDiscKey = (disc: Track[]) => disc.map(track => track.id).join('|')
|
||||||
Tracks
|
Tracks
|
||||||
</translate>
|
</translate>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<channel-entries
|
<channel-entries
|
||||||
v-if="artist.channel && isSerie"
|
v-if="artist.channel && isSerie"
|
||||||
:is-podcast="isSerie"
|
:is-podcast="isSerie"
|
||||||
:limit="50"
|
:limit="50"
|
||||||
:filters="{channel: artist.channel.uuid, album: object.id, ordering: '-creation_date'}"
|
:filters="{channel: artist.channel.uuid, album: object.id, ordering: '-creation_date'}"
|
||||||
/>
|
/>
|
||||||
<template v-else-if="discs.length > 1">
|
|
||||||
<div
|
<template v-else>
|
||||||
v-for="tracks in discs"
|
<template v-if="discCount > 1">
|
||||||
:key="getDiscKey(tracks)"
|
<div
|
||||||
>
|
v-for="tracks, index in paginatedDiscs"
|
||||||
<div class="ui hidden divider" />
|
:key="index + getDiscKey(tracks)"
|
||||||
<play-button
|
|
||||||
class="right floated mini inverted vibrant"
|
|
||||||
:tracks="tracks"
|
|
||||||
/>
|
|
||||||
<translate
|
|
||||||
tag="h3"
|
|
||||||
:translate-params="{number: tracks[0].disc_number}"
|
|
||||||
translate-context="Content/Album/"
|
|
||||||
>
|
>
|
||||||
Volume %{ number }
|
<template v-if="tracks.length > 0">
|
||||||
</translate>
|
<div class="ui hidden divider" />
|
||||||
|
<play-button
|
||||||
|
class="right floated mini inverted vibrant"
|
||||||
|
:tracks="discs[index]"
|
||||||
|
/>
|
||||||
|
<translate
|
||||||
|
tag="h3"
|
||||||
|
:translate-params="{number: tracks[0]?.disc_number ?? index + 1}"
|
||||||
|
translate-context="Content/Album/"
|
||||||
|
>
|
||||||
|
Volume %{ number }
|
||||||
|
</translate>
|
||||||
|
<track-table
|
||||||
|
:is-album="true"
|
||||||
|
:tracks="tracks"
|
||||||
|
:show-position="true"
|
||||||
|
:show-art="false"
|
||||||
|
:show-album="false"
|
||||||
|
:show-artist="false"
|
||||||
|
:paginate-results="false"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<track-table
|
<track-table
|
||||||
:is-album="true"
|
:is-album="true"
|
||||||
:tracks="tracks"
|
:tracks="object.tracks"
|
||||||
:show-position="true"
|
:show-position="true"
|
||||||
:show-art="false"
|
:show-art="false"
|
||||||
:show-album="false"
|
:show-album="false"
|
||||||
:show-artist="false"
|
:show-artist="false"
|
||||||
:paginate-results="false"
|
:paginate-results="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="totalTracks > paginateBy"
|
v-if="totalTracks > paginateBy"
|
||||||
class="ui center aligned basic segment tablet-and-below"
|
class="ui center aligned basic segment"
|
||||||
>
|
>
|
||||||
<pagination
|
<pagination
|
||||||
:current="page"
|
v-model:current="page"
|
||||||
:paginate-by="paginateBy"
|
:paginate-by="paginateBy"
|
||||||
:total="totalTracks"
|
:total="totalTracks"
|
||||||
:compact="true"
|
|
||||||
@update:current="(page: number) => emit('page-changed', page)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
|
||||||
<track-table
|
|
||||||
:is-album="true"
|
|
||||||
:tracks="object.tracks"
|
|
||||||
:show-position="true"
|
|
||||||
:show-art="false"
|
|
||||||
:show-album="false"
|
|
||||||
:show-artist="false"
|
|
||||||
:paginate-results="true"
|
|
||||||
:total="totalTracks"
|
|
||||||
:paginate-by="paginateBy"
|
|
||||||
:page="page"
|
|
||||||
@page-changed="(page) => emit('page-changed', page)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="!artist.channel && !isSerie">
|
<template v-if="!artist.channel && !isSerie">
|
||||||
<h2>
|
<h2>
|
||||||
<translate translate-context="Content/*/Title/Noun">
|
<translate translate-context="Content/*/Title/Noun">
|
||||||
|
|
Loading…
Reference in New Issue