mirror of
https://github.com/owntone/owntone-server.git
synced 2025-03-03 23:30:09 -05:00
[web] Refactor the heading title in the pages
This commit is contained in:
parent
ef3e64b9c9
commit
bdad6d61bf
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<button
|
||||
class="button is-small is-rounded"
|
||||
:disabled="disabled"
|
||||
@click="handler"
|
||||
:disabled="button.disabled"
|
||||
@click="button.handler"
|
||||
>
|
||||
<mdicon v-if="icon" class="icon" :name="icon" size="16" />
|
||||
<span v-if="label" v-text="$t(label)" />
|
||||
<mdicon v-if="button.icon" class="icon" :name="button.icon" size="16" />
|
||||
<span v-if="button.key" v-text="$t(button.key)" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
@ -13,10 +13,7 @@
|
||||
export default {
|
||||
name: 'ControlButton',
|
||||
props: {
|
||||
disabled: { default: false, type: Boolean },
|
||||
handler: { required: true, type: Function },
|
||||
icon: { default: '', type: String },
|
||||
label: { default: '', type: String }
|
||||
button: { required: true, type: Object }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,7 +21,7 @@
|
||||
/>
|
||||
<span
|
||||
:class="{ 'is-hidden-mobile': link.icon }"
|
||||
v-text="$t(link.label)"
|
||||
v-text="$t(link.key)"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
|
24
web-src/src/components/HeadingTitle.vue
Normal file
24
web-src/src/components/HeadingTitle.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div v-if="content.title" class="title is-4" v-text="content.title" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<template v-for="(part, index) in content.subtitle" :key="index">
|
||||
<a
|
||||
v-if="part.handler"
|
||||
@click="part.handler"
|
||||
v-text="$t(part.key, { count: $n(part.count) }, part.count)"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
v-text="$t(part.key, { count: $n(part.count) }, part.count)"
|
||||
/>
|
||||
<span v-if="index !== content.subtitle.length - 1"> | </span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HeadingTitle',
|
||||
props: { content: { required: true, type: Object } }
|
||||
}
|
||||
</script>
|
@ -100,16 +100,8 @@ export default {
|
||||
computed: {
|
||||
actions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('page.podcast.cancel'),
|
||||
handler: 'cancel',
|
||||
icon: 'cancel'
|
||||
},
|
||||
{
|
||||
label: this.$t('page.podcast.remove'),
|
||||
handler: 'remove',
|
||||
icon: 'delete'
|
||||
}
|
||||
{ key: 'page.podcast.cancel', handler: 'cancel', icon: 'cancel' },
|
||||
{ key: 'page.podcast.remove', handler: 'remove', icon: 'delete' }
|
||||
]
|
||||
},
|
||||
media_kind_resolved() {
|
||||
|
@ -13,10 +13,10 @@
|
||||
<slot v-if="$slots.buttons" name="buttons" />
|
||||
<div
|
||||
v-for="property in item.properties?.filter((p) => p.value)"
|
||||
:key="property.label"
|
||||
:key="property.key"
|
||||
class="mb-3"
|
||||
>
|
||||
<div class="is-size-7 is-uppercase" v-text="$t(property.label)" />
|
||||
<div class="is-size-7 is-uppercase" v-text="$t(property.key)" />
|
||||
<div class="title is-6">
|
||||
<a
|
||||
v-if="property.handler"
|
||||
|
@ -11,13 +11,13 @@
|
||||
<footer v-if="actions.length" class="card-footer">
|
||||
<a
|
||||
v-for="action in actions"
|
||||
:key="action.label"
|
||||
:key="action.key"
|
||||
class="card-footer-item"
|
||||
:class="{ 'is-disabled': action.disabled }"
|
||||
@click="action.handler"
|
||||
>
|
||||
<mdicon class="icon" :name="action.icon" size="16" />
|
||||
<span class="is-size-7" v-text="action.label" />
|
||||
<span class="is-size-7" v-text="$t(action.key)" />
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -37,16 +37,12 @@ export default {
|
||||
computed: {
|
||||
actions() {
|
||||
if (this.loading) {
|
||||
return [{ label: this.$t('dialog.add.rss.processing'), icon: 'web' }]
|
||||
return [{ key: 'dialog.add.rss.processing', icon: 'web' }]
|
||||
}
|
||||
return [
|
||||
{ key: 'dialog.add.rss.cancel', handler: this.cancel, icon: 'cancel' },
|
||||
{
|
||||
label: this.$t('dialog.add.rss.cancel'),
|
||||
handler: this.cancel,
|
||||
icon: 'cancel'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.add.rss.add'),
|
||||
key: 'dialog.add.rss.add',
|
||||
disabled: this.disabled,
|
||||
handler: this.add,
|
||||
icon: 'playlist-plus'
|
||||
|
@ -38,22 +38,22 @@ export default {
|
||||
computed: {
|
||||
actions() {
|
||||
if (this.loading) {
|
||||
return [{ label: this.$t('dialog.add.stream.processing'), icon: 'web' }]
|
||||
return [{ key: 'dialog.add.stream.processing', icon: 'web' }]
|
||||
}
|
||||
return [
|
||||
{
|
||||
label: this.$t('dialog.add.stream.cancel'),
|
||||
key: 'dialog.add.stream.cancel',
|
||||
handler: this.cancel,
|
||||
icon: 'cancel'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.add.stream.add'),
|
||||
key: 'dialog.add.stream.add',
|
||||
disabled: this.disabled,
|
||||
handler: this.add,
|
||||
icon: 'playlist-plus'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.add.stream.play'),
|
||||
key: 'dialog.add.stream.play',
|
||||
disabled: this.disabled,
|
||||
handler: this.play,
|
||||
icon: 'play'
|
||||
|
@ -25,15 +25,12 @@ export default {
|
||||
if (this.media_kind_resolved === 'podcast') {
|
||||
if (this.item.data_kind === 'url') {
|
||||
return [
|
||||
{ label: 'dialog.album.mark-as-played', handler: this.mark_played },
|
||||
{
|
||||
label: 'dialog.album.remove-podcast',
|
||||
handler: this.remove_podcast
|
||||
}
|
||||
{ key: 'dialog.album.mark-as-played', handler: this.mark_played },
|
||||
{ key: 'dialog.album.remove-podcast', handler: this.remove_podcast }
|
||||
]
|
||||
}
|
||||
return [
|
||||
{ label: 'dialog.album.mark-as-played', handler: this.mark_played }
|
||||
{ key: 'dialog.album.mark-as-played', handler: this.mark_played }
|
||||
]
|
||||
}
|
||||
return []
|
||||
@ -49,26 +46,26 @@ export default {
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.artist',
|
||||
key: 'property.artist',
|
||||
value: this.item.artist,
|
||||
handler: this.open_artist
|
||||
},
|
||||
{
|
||||
label: 'property.release-date',
|
||||
key: 'property.release-date',
|
||||
value: this.$filters.toDate(this.item.date_released)
|
||||
},
|
||||
{ label: 'property.year', value: this.item.year },
|
||||
{ label: 'property.tracks', value: this.item.track_count },
|
||||
{ key: 'property.year', value: this.item.year },
|
||||
{ key: 'property.tracks', value: this.item.track_count },
|
||||
{
|
||||
label: 'property.duration',
|
||||
key: 'property.duration',
|
||||
value: this.$filters.toTimecode(this.item.length_ms)
|
||||
},
|
||||
{
|
||||
label: 'property.type',
|
||||
key: 'property.type',
|
||||
value: `${this.$t(`media.kind.${this.item.media_kind}`)} - ${this.$t(`data.kind.${this.item.data_kind}`)}`
|
||||
},
|
||||
{
|
||||
label: 'property.added-on',
|
||||
key: 'property.added-on',
|
||||
value: this.$filters.toDateTime(this.item.time_added)
|
||||
}
|
||||
]
|
||||
|
@ -23,18 +23,15 @@ export default {
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.artist',
|
||||
key: 'property.artist',
|
||||
value: this.item?.artists?.[0]?.name,
|
||||
handler: this.open_artist
|
||||
},
|
||||
{
|
||||
label: 'property.release-date',
|
||||
key: 'property.release-date',
|
||||
value: this.$filters.toDate(this.item.release_date)
|
||||
},
|
||||
{
|
||||
label: 'property.type',
|
||||
value: this.item.album_type
|
||||
}
|
||||
{ key: 'property.type', value: this.item.album_type }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,14 @@ export default {
|
||||
handler: this.open,
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{ label: 'property.albums', value: this.item.album_count },
|
||||
{ label: 'property.tracks', value: this.item.track_count },
|
||||
{ key: 'property.albums', value: this.item.album_count },
|
||||
{ key: 'property.tracks', value: this.item.track_count },
|
||||
{
|
||||
label: 'property.type',
|
||||
key: 'property.type',
|
||||
value: this.$t(`data.kind.${this.item.data_kind}`)
|
||||
},
|
||||
{
|
||||
label: 'property.added-on',
|
||||
key: 'property.added-on',
|
||||
value: this.$filters.toDateTime(this.item.time_added)
|
||||
}
|
||||
]
|
||||
|
@ -22,15 +22,12 @@ export default {
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.popularity',
|
||||
key: 'property.popularity',
|
||||
value: [this.item.popularity, this.item.followers?.total].join(
|
||||
' / '
|
||||
)
|
||||
},
|
||||
{
|
||||
label: 'property.genres',
|
||||
value: this.item.genres?.join(', ')
|
||||
}
|
||||
{ key: 'property.genres', value: this.item.genres?.join(', ') }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -22,17 +22,17 @@ export default {
|
||||
expression: `composer is "${this.item.name}" and media_kind is music`,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.albums',
|
||||
key: 'property.albums',
|
||||
value: this.item.album_count,
|
||||
handler: this.open_albums
|
||||
},
|
||||
{
|
||||
label: 'property.tracks',
|
||||
key: 'property.tracks',
|
||||
value: this.item.track_count,
|
||||
handler: this.open_tracks
|
||||
},
|
||||
{
|
||||
label: 'property.duration',
|
||||
key: 'property.duration',
|
||||
value: this.$filters.toTimecode(this.item.length_ms)
|
||||
}
|
||||
]
|
||||
|
@ -25,16 +25,10 @@ export default {
|
||||
handler: this.open,
|
||||
expression: `genre is "${this.item.name}" and media_kind is ${this.media_kind}`,
|
||||
properties: [
|
||||
{ key: 'property.albums', value: this.item.album_count },
|
||||
{ key: 'property.tracks', value: this.item.track_count },
|
||||
{
|
||||
label: 'property.albums',
|
||||
value: this.item.album_count
|
||||
},
|
||||
{
|
||||
label: 'property.tracks',
|
||||
value: this.item.track_count
|
||||
},
|
||||
{
|
||||
label: 'property.duration',
|
||||
key: 'property.duration',
|
||||
value: this.$filters.toTimecode(this.item.length_ms)
|
||||
}
|
||||
]
|
||||
|
@ -6,9 +6,8 @@
|
||||
<div class="buttons">
|
||||
<control-button
|
||||
v-for="button in buttons"
|
||||
:key="button.label"
|
||||
:handler="button.handler"
|
||||
label="button.label"
|
||||
:key="button.key"
|
||||
:button="button"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -36,20 +35,16 @@ export default {
|
||||
actions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('dialog.playable.add'),
|
||||
key: 'dialog.playable.add',
|
||||
handler: this.queue_add,
|
||||
icon: 'playlist-plus'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.playable.add-next'),
|
||||
key: 'dialog.playable.add-next',
|
||||
handler: this.queue_add_next,
|
||||
icon: 'playlist-play'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.playable.play'),
|
||||
handler: this.play,
|
||||
icon: 'play'
|
||||
}
|
||||
{ key: 'dialog.playable.play', handler: this.play, icon: 'play' }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -26,12 +26,12 @@ export default {
|
||||
uri: this.item.uri,
|
||||
uris: this.uris,
|
||||
properties: [
|
||||
{ label: 'property.tracks', value: this.item.item_count },
|
||||
{ key: 'property.tracks', value: this.item.item_count },
|
||||
{
|
||||
label: 'property.type',
|
||||
key: 'property.type',
|
||||
value: this.$t(`playlist.type.${this.item.type}`)
|
||||
},
|
||||
{ label: 'property.path', value: this.item.path }
|
||||
{ key: 'property.path', value: this.item.path }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -47,16 +47,16 @@ export default {
|
||||
computed: {
|
||||
actions() {
|
||||
if (this.loading) {
|
||||
return [{ label: this.$t('dialog.playlist.save.saving'), icon: 'web' }]
|
||||
return [{ key: 'dialog.playlist.save.saving', icon: 'web' }]
|
||||
}
|
||||
return [
|
||||
{
|
||||
label: this.$t('dialog.playlist.save.cancel'),
|
||||
key: 'dialog.playlist.save.cancel',
|
||||
handler: this.cancel,
|
||||
icon: 'cancel'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.playlist.save.save'),
|
||||
key: 'dialog.playlist.save.save',
|
||||
disabled: this.disabled,
|
||||
handler: this.save,
|
||||
icon: 'download'
|
||||
|
@ -21,15 +21,9 @@ export default {
|
||||
handler: this.open,
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.owner',
|
||||
value: this.item.owner?.display_name
|
||||
},
|
||||
{
|
||||
label: 'property.tracks',
|
||||
value: this.item.tracks?.total
|
||||
},
|
||||
{ label: 'property.path', value: this.item.uri }
|
||||
{ key: 'property.owner', value: this.item.owner?.display_name },
|
||||
{ key: 'property.tracks', value: this.item.tracks?.total },
|
||||
{ key: 'property.path', value: this.item.uri }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -30,12 +30,12 @@ export default {
|
||||
actions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('dialog.queue-item.remove'),
|
||||
key: this.$t('dialog.queue-item.remove'),
|
||||
handler: this.remove,
|
||||
icon: 'delete'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.queue-item.play'),
|
||||
key: this.$t('dialog.queue-item.play'),
|
||||
handler: this.play,
|
||||
icon: 'play'
|
||||
}
|
||||
@ -47,37 +47,37 @@ export default {
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.album',
|
||||
key: 'property.album',
|
||||
value: this.item.album,
|
||||
handler: this.open_album
|
||||
},
|
||||
{
|
||||
label: 'property.album-artist',
|
||||
key: 'property.album-artist',
|
||||
value: this.item.album_artist,
|
||||
handler: this.open_album_artist
|
||||
},
|
||||
{ label: 'property.composer', value: this.item.composer },
|
||||
{ label: 'property.year', value: this.item.year },
|
||||
{ key: 'property.composer', value: this.item.composer },
|
||||
{ key: 'property.year', value: this.item.year },
|
||||
{
|
||||
label: 'property.genre',
|
||||
key: 'property.genre',
|
||||
value: this.item.genre,
|
||||
handler: this.open_genre
|
||||
},
|
||||
{
|
||||
label: 'property.position',
|
||||
key: 'property.position',
|
||||
value: [this.item.disc_number, this.item.track_number].join(' / ')
|
||||
},
|
||||
{
|
||||
label: 'property.duration',
|
||||
key: 'property.duration',
|
||||
value: this.$filters.toTimecode(this.item.length_ms)
|
||||
},
|
||||
{ label: 'property.path', value: this.item.path },
|
||||
{ key: 'property.path', value: this.item.path },
|
||||
{
|
||||
label: 'property.type',
|
||||
key: 'property.type',
|
||||
value: `${this.$t(`media.kind.${this.item.media_kind}`)} - ${this.$t(`data.kind.${this.item.data_kind}`)}`
|
||||
},
|
||||
{
|
||||
label: 'property.quality',
|
||||
key: 'property.quality',
|
||||
value: this.$t('dialog.track.quality-value', {
|
||||
format: this.item.type,
|
||||
bitrate: this.item.bitrate,
|
||||
|
@ -47,12 +47,12 @@ export default {
|
||||
actions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('dialog.remote-pairing.cancel'),
|
||||
key: 'dialog.remote-pairing.cancel',
|
||||
handler: this.cancel,
|
||||
icon: 'cancel'
|
||||
},
|
||||
{
|
||||
label: this.$t('dialog.remote-pairing.pair'),
|
||||
key: 'dialog.remote-pairing.pair',
|
||||
handler: this.pair,
|
||||
icon: 'cellphone'
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ export default {
|
||||
return []
|
||||
}
|
||||
return this.item.play_count > 0
|
||||
? [{ label: 'dialog.track.mark-as-new', handler: this.mark_new }]
|
||||
: [{ label: 'dialog.track.mark-as-played', handler: this.mark_played }]
|
||||
? [{ key: 'dialog.track.mark-as-new', handler: this.mark_new }]
|
||||
: [{ key: 'dialog.track.mark-as-played', handler: this.mark_played }]
|
||||
},
|
||||
playable() {
|
||||
return {
|
||||
@ -31,36 +31,36 @@ export default {
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.album',
|
||||
key: 'property.album',
|
||||
value: this.item.album,
|
||||
handler: this.open_album
|
||||
},
|
||||
{
|
||||
label: 'property.album-artist',
|
||||
key: 'property.album-artist',
|
||||
value: this.item.album_artist,
|
||||
handler: this.open_artist
|
||||
},
|
||||
{ label: 'property.composer', value: this.item.composer },
|
||||
{ key: 'property.composer', value: this.item.composer },
|
||||
{
|
||||
label: 'property.release-date',
|
||||
key: 'property.release-date',
|
||||
value: this.$filters.toDate(this.item.date_released)
|
||||
},
|
||||
{ label: 'property.year', value: this.item.year },
|
||||
{ label: 'property.genre', value: this.item.genre },
|
||||
{ key: 'property.year', value: this.item.year },
|
||||
{ key: 'property.genre', value: this.item.genre },
|
||||
{
|
||||
label: 'property.position',
|
||||
key: 'property.position',
|
||||
value: [this.item.disc_number, this.item.track_number].join(' / ')
|
||||
},
|
||||
{
|
||||
label: 'property.duration',
|
||||
key: 'property.duration',
|
||||
value: this.$filters.toTimecode(this.item.length_ms)
|
||||
},
|
||||
{
|
||||
label: 'property.type',
|
||||
key: 'property.type',
|
||||
value: `${this.$t(`media.kind.${this.item.media_kind}`)} - ${this.$t(`data.kind.${this.item.data_kind}`)}`
|
||||
},
|
||||
{
|
||||
label: 'property.quality',
|
||||
key: 'property.quality',
|
||||
value:
|
||||
this.item.data_kind !== 'spotify' &&
|
||||
this.$t('dialog.track.quality-value', {
|
||||
@ -71,17 +71,17 @@ export default {
|
||||
})
|
||||
},
|
||||
{
|
||||
label: 'property.added-on',
|
||||
key: 'property.added-on',
|
||||
value: this.$filters.toDateTime(this.item.time_added)
|
||||
},
|
||||
{
|
||||
label: 'property.rating',
|
||||
key: 'property.rating',
|
||||
value: this.$t('dialog.track.rating-value', {
|
||||
rating: Math.floor(this.item.rating / 10)
|
||||
})
|
||||
},
|
||||
{ label: 'property.comment', value: this.item.comment },
|
||||
{ label: 'property.path', value: this.item.path }
|
||||
{ key: 'property.comment', value: this.item.comment },
|
||||
{ key: 'property.path', value: this.item.path }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -24,28 +24,28 @@ export default {
|
||||
uri: this.item.uri,
|
||||
properties: [
|
||||
{
|
||||
label: 'property.album',
|
||||
key: 'property.album',
|
||||
value: this.item.album.name,
|
||||
handler: this.open_album
|
||||
},
|
||||
{
|
||||
label: 'property.album-artist',
|
||||
key: 'property.album-artist',
|
||||
value: this.item.artists[0]?.name,
|
||||
handler: this.open_artist
|
||||
},
|
||||
{
|
||||
label: 'property.release-date',
|
||||
key: 'property.release-date',
|
||||
value: this.$filters.toDate(this.item.album.release_date)
|
||||
},
|
||||
{
|
||||
label: 'property.position',
|
||||
key: 'property.position',
|
||||
value: [this.item.disc_number, this.item.track_number].join(' / ')
|
||||
},
|
||||
{
|
||||
label: 'property.duration',
|
||||
key: 'property.duration',
|
||||
value: this.$filters.toTimecode(this.item.duration_ms)
|
||||
},
|
||||
{ label: 'property.path', value: this.item.uri }
|
||||
{ key: 'property.path', value: this.item.uri }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -68,14 +68,14 @@ export default {
|
||||
actions() {
|
||||
const actions = [
|
||||
{
|
||||
label: this.$t('dialog.update.cancel'),
|
||||
key: 'dialog.update.cancel',
|
||||
handler: this.cancel,
|
||||
icon: 'cancel'
|
||||
}
|
||||
]
|
||||
if (!this.libraryStore.updating) {
|
||||
actions.push({
|
||||
label: this.$t('dialog.update.rescan'),
|
||||
key: 'dialog.update.rescan',
|
||||
handler: this.analyse,
|
||||
icon: 'check'
|
||||
})
|
||||
|
@ -33,7 +33,7 @@
|
||||
v-else-if="menu.action"
|
||||
class="dropdown-item"
|
||||
@click.stop.prevent="menu.action"
|
||||
v-text="$t(menu.label)"
|
||||
v-text="$t(menu.key)"
|
||||
/>
|
||||
<control-link
|
||||
v-else
|
||||
@ -48,7 +48,7 @@
|
||||
'pl-5': menu.sub,
|
||||
'has-text-weight-semibold': menu.icon
|
||||
}"
|
||||
v-text="$t(menu.label)"
|
||||
v-text="$t(menu.key)"
|
||||
/>
|
||||
</control-link>
|
||||
</template>
|
||||
@ -82,82 +82,82 @@ export default {
|
||||
return [
|
||||
{
|
||||
name: 'playlists',
|
||||
label: 'navigation.playlists',
|
||||
key: 'navigation.playlists',
|
||||
icon: 'music-box-multiple',
|
||||
show: this.settingsStore.show_menu_item_playlists
|
||||
},
|
||||
{
|
||||
name: 'music',
|
||||
label: 'navigation.music',
|
||||
key: 'navigation.music',
|
||||
icon: 'music',
|
||||
show: this.settingsStore.show_menu_item_music
|
||||
},
|
||||
{
|
||||
name: 'music-artists',
|
||||
label: 'navigation.artists',
|
||||
key: 'navigation.artists',
|
||||
show: true,
|
||||
sub: true
|
||||
},
|
||||
{
|
||||
name: 'music-albums',
|
||||
label: 'navigation.albums',
|
||||
key: 'navigation.albums',
|
||||
show: true,
|
||||
sub: true
|
||||
},
|
||||
{
|
||||
name: 'music-genres',
|
||||
label: 'navigation.genres',
|
||||
key: 'navigation.genres',
|
||||
show: true,
|
||||
sub: true
|
||||
},
|
||||
{
|
||||
name: 'music-spotify',
|
||||
label: 'navigation.spotify',
|
||||
key: 'navigation.spotify',
|
||||
show: this.servicesStore.spotify.webapi_token_valid,
|
||||
sub: true
|
||||
},
|
||||
{
|
||||
name: 'podcasts',
|
||||
label: 'navigation.podcasts',
|
||||
key: 'navigation.podcasts',
|
||||
icon: 'microphone',
|
||||
show: this.settingsStore.show_menu_item_podcasts
|
||||
},
|
||||
{
|
||||
name: 'audiobooks',
|
||||
label: 'navigation.audiobooks',
|
||||
key: 'navigation.audiobooks',
|
||||
icon: 'book-open-variant',
|
||||
show: this.settingsStore.show_menu_item_audiobooks
|
||||
},
|
||||
{
|
||||
name: 'radio',
|
||||
label: 'navigation.radio',
|
||||
key: 'navigation.radio',
|
||||
icon: 'radio',
|
||||
show: this.settingsStore.show_menu_item_radio
|
||||
},
|
||||
{
|
||||
name: 'files',
|
||||
label: 'navigation.files',
|
||||
key: 'navigation.files',
|
||||
icon: 'folder-open',
|
||||
show: this.settingsStore.show_menu_item_files
|
||||
},
|
||||
{
|
||||
name: this.searchStore.search_source,
|
||||
label: 'navigation.search',
|
||||
key: 'navigation.search',
|
||||
icon: 'magnify',
|
||||
show: this.settingsStore.show_menu_item_search
|
||||
},
|
||||
{ separator: true, show: true },
|
||||
{
|
||||
name: 'settings-webinterface',
|
||||
label: 'navigation.settings',
|
||||
key: 'navigation.settings',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
label: 'navigation.update-library',
|
||||
key: 'navigation.update-library',
|
||||
action: this.open_update_dialog,
|
||||
show: true
|
||||
},
|
||||
{ name: 'about', label: 'navigation.about', show: true }
|
||||
{ name: 'about', key: 'navigation.about', show: true }
|
||||
]
|
||||
},
|
||||
zindex() {
|
||||
|
@ -14,17 +14,17 @@ export default {
|
||||
{
|
||||
to: { name: 'audiobooks-artists' },
|
||||
icon: 'account-music',
|
||||
label: 'page.audiobooks.tabs.authors'
|
||||
key: 'page.audiobooks.tabs.authors'
|
||||
},
|
||||
{
|
||||
to: { name: 'audiobooks-albums' },
|
||||
icon: 'album',
|
||||
label: 'page.audiobooks.tabs.audiobooks'
|
||||
key: 'page.audiobooks.tabs.audiobooks'
|
||||
},
|
||||
{
|
||||
to: { name: 'audiobooks-genres' },
|
||||
icon: 'speaker',
|
||||
label: 'page.audiobooks.tabs.genres'
|
||||
key: 'page.audiobooks.tabs.genres'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -18,34 +18,34 @@ export default {
|
||||
{
|
||||
to: { name: 'music-history' },
|
||||
icon: 'history',
|
||||
label: 'page.music.tabs.history'
|
||||
key: 'page.music.tabs.history'
|
||||
},
|
||||
{
|
||||
to: { name: 'music-artists' },
|
||||
icon: 'account-music',
|
||||
label: 'page.music.tabs.artists'
|
||||
key: 'page.music.tabs.artists'
|
||||
},
|
||||
{
|
||||
to: { name: 'music-albums' },
|
||||
icon: 'album',
|
||||
label: 'page.music.tabs.albums'
|
||||
key: 'page.music.tabs.albums'
|
||||
},
|
||||
{
|
||||
to: { name: 'music-genres' },
|
||||
icon: 'speaker',
|
||||
label: 'page.music.tabs.genres'
|
||||
key: 'page.music.tabs.genres'
|
||||
},
|
||||
{
|
||||
to: { name: 'music-composers' },
|
||||
icon: 'book-open-page-variant',
|
||||
label: 'page.music.tabs.composers'
|
||||
key: 'page.music.tabs.composers'
|
||||
}
|
||||
]
|
||||
if (this.servicesStore.spotify.webapi_token_valid) {
|
||||
links.push({
|
||||
to: { name: 'music-spotify' },
|
||||
icon: 'spotify',
|
||||
label: 'page.music.tabs.spotify'
|
||||
key: 'page.music.tabs.spotify'
|
||||
})
|
||||
}
|
||||
return links
|
||||
|
@ -12,20 +12,20 @@ export default {
|
||||
links() {
|
||||
return [
|
||||
{
|
||||
to: { name: 'settings-webinterface' },
|
||||
label: 'page.settings.tabs.general'
|
||||
key: 'page.settings.tabs.general',
|
||||
to: { name: 'settings-webinterface' }
|
||||
},
|
||||
{
|
||||
to: { name: 'settings-remotes-outputs' },
|
||||
label: 'page.settings.tabs.remotes-and-outputs'
|
||||
key: 'page.settings.tabs.remotes-and-outputs',
|
||||
to: { name: 'settings-remotes-outputs' }
|
||||
},
|
||||
{
|
||||
to: { name: 'settings-artwork' },
|
||||
label: 'page.settings.tabs.artwork'
|
||||
key: 'page.settings.tabs.artwork',
|
||||
to: { name: 'settings-artwork' }
|
||||
},
|
||||
{
|
||||
to: { name: 'settings-online-services' },
|
||||
label: 'page.settings.tabs.online-services'
|
||||
key: 'page.settings.tabs.online-services',
|
||||
to: { name: 'settings-online-services' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,112 +1,100 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
<div class="content">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<p class="title is-4" v-text="$t('page.about.library')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<control-button
|
||||
:class="{ 'is-loading': libraryStore.updating }"
|
||||
:disabled="libraryStore.updating"
|
||||
:handler="showUpdateDialog"
|
||||
icon="refresh"
|
||||
label="page.about.update"
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
<div
|
||||
v-for="property in properties"
|
||||
:key="property.label"
|
||||
class="media is-align-items-center mb-0"
|
||||
>
|
||||
<div
|
||||
class="media-content has-text-weight-bold"
|
||||
v-text="$t(property.label)"
|
||||
/>
|
||||
<div class="media-right">
|
||||
<span v-text="property.value" />
|
||||
<span
|
||||
v-if="property.alternate"
|
||||
class="has-text-grey"
|
||||
v-text="` (${property.alternate})`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<heading-title :content="{ title: $t('page.about.library') }" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<control-button
|
||||
:class="{ 'is-loading': libraryStore.updating }"
|
||||
:button="{
|
||||
disabled: libraryStore.updating,
|
||||
handler: showUpdateDialog,
|
||||
icon: 'refresh',
|
||||
key: 'page.about.update'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<div
|
||||
v-for="property in properties"
|
||||
:key="property.key"
|
||||
class="media is-align-items-center mb-0"
|
||||
>
|
||||
<div
|
||||
class="media-content has-text-weight-bold"
|
||||
v-text="$t(property.key)"
|
||||
/>
|
||||
<div class="media-right">
|
||||
<span v-text="property.value" />
|
||||
<span
|
||||
v-if="property.alternate"
|
||||
class="has-text-grey"
|
||||
v-text="` (${property.alternate})`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
<div class="content has-text-centered-mobile">
|
||||
<p
|
||||
class="is-size-7"
|
||||
v-text="
|
||||
$t('page.about.version', {
|
||||
version: configurationStore.version
|
||||
})
|
||||
"
|
||||
</template>
|
||||
<template #footer>
|
||||
<div
|
||||
class="is-size-7 mt-6"
|
||||
v-text="
|
||||
$t('page.about.version', {
|
||||
version: configurationStore.version
|
||||
})
|
||||
"
|
||||
/>
|
||||
<div
|
||||
class="is-size-7"
|
||||
v-text="
|
||||
$t('page.about.compiled-with', {
|
||||
options: configurationStore.buildoptions.join(', ')
|
||||
})
|
||||
"
|
||||
/>
|
||||
<i18n-t
|
||||
tag="div"
|
||||
class="is-size-7"
|
||||
keypath="page.about.built-with"
|
||||
scope="global"
|
||||
>
|
||||
<template #bulma>
|
||||
<a href="https://bulma.io">Bulma</a>
|
||||
</template>
|
||||
<template #mdi>
|
||||
<a href="https://pictogrammers.com/library/mdi/">
|
||||
Material Design Icons
|
||||
</a>
|
||||
</template>
|
||||
<template #vuejs>
|
||||
<a href="https://vuejs.org/">Vue.js</a>
|
||||
</template>
|
||||
<template #axios>
|
||||
<a href="https://github.com/axios/axios">axios</a>
|
||||
</template>
|
||||
<template #others>
|
||||
<a
|
||||
href="https://github.com/owntone/owntone-server/network/dependencies"
|
||||
v-text="$t('page.about.more')"
|
||||
/>
|
||||
<p
|
||||
class="is-size-7"
|
||||
v-text="
|
||||
$t('page.about.compiled-with', {
|
||||
options: configurationStore.buildoptions.join(', ')
|
||||
})
|
||||
"
|
||||
/>
|
||||
<i18n-t
|
||||
tag="p"
|
||||
class="is-size-7"
|
||||
keypath="page.about.built-with"
|
||||
scope="global"
|
||||
>
|
||||
<template #bulma>
|
||||
<a href="https://bulma.io">Bulma</a>
|
||||
</template>
|
||||
<template #mdi>
|
||||
<a href="https://pictogrammers.com/library/mdi/">
|
||||
Material Design Icons
|
||||
</a>
|
||||
</template>
|
||||
<template #vuejs>
|
||||
<a href="https://vuejs.org/">Vue.js</a>
|
||||
</template>
|
||||
<template #axios>
|
||||
<a href="https://github.com/axios/axios">axios</a>
|
||||
</template>
|
||||
<template #others>
|
||||
<a
|
||||
href="https://github.com/owntone/owntone-server/network/dependencies"
|
||||
v-text="$t('page.about.more')"
|
||||
/>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import { useConfigurationStore } from '@/stores/configuration'
|
||||
import { useLibraryStore } from '@/stores/library'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
|
||||
export default {
|
||||
name: 'PageAbout',
|
||||
components: { ControlButton },
|
||||
components: { ContentWithHeading, ControlButton, HeadingTitle },
|
||||
setup() {
|
||||
return {
|
||||
configurationStore: useConfigurationStore(),
|
||||
@ -118,32 +106,32 @@ export default {
|
||||
properties() {
|
||||
return [
|
||||
{
|
||||
label: 'property.name',
|
||||
key: 'property.name',
|
||||
value: this.configurationStore.library_name
|
||||
},
|
||||
{
|
||||
label: 'property.artists',
|
||||
key: 'property.artists',
|
||||
value: this.$n(this.libraryStore.artists)
|
||||
},
|
||||
{
|
||||
label: 'property.albums',
|
||||
key: 'property.albums',
|
||||
value: this.$n(this.libraryStore.albums)
|
||||
},
|
||||
{
|
||||
label: 'property.tracks',
|
||||
key: 'property.tracks',
|
||||
value: this.$n(this.libraryStore.songs)
|
||||
},
|
||||
{
|
||||
label: 'property.playtime',
|
||||
key: 'property.playtime',
|
||||
value: this.$filters.toDuration(this.libraryStore.db_playtime)
|
||||
},
|
||||
{
|
||||
label: 'property.updated',
|
||||
key: 'property.updated',
|
||||
value: this.$filters.toRelativeDuration(this.libraryStore.updated_at),
|
||||
alternate: this.$filters.toDateTime(this.libraryStore.updated_at)
|
||||
},
|
||||
{
|
||||
label: 'property.uptime',
|
||||
key: 'property.uptime',
|
||||
value: this.$filters.toDurationToNow(this.libraryStore.started_at),
|
||||
alternate: this.$filters.toDateTime(this.libraryStore.started_at)
|
||||
}
|
||||
|
@ -12,11 +12,18 @@
|
||||
/>
|
||||
<div class="buttons is-centered-mobile mt-5">
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.album.shuffle"
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.album.shuffle'
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: showDetails,
|
||||
icon: 'dots-horizontal'
|
||||
}"
|
||||
/>
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-right>
|
||||
|
@ -12,11 +12,15 @@
|
||||
/>
|
||||
<div class="buttons is-centered-mobile mt-5">
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.spotify.album.shuffle"
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.spotify.album.shuffle'
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-right>
|
||||
|
@ -43,11 +43,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.albums.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.albums', { count: albums.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -61,6 +57,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import ControlSwitch from '@/components/ControlSwitch.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -72,7 +69,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_albums('music')
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums_list = new GroupedList(response.data)
|
||||
}
|
||||
@ -84,21 +80,19 @@ export default {
|
||||
ContentWithHeading,
|
||||
ControlDropdown,
|
||||
ControlSwitch,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListAlbums,
|
||||
TabsMusic
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { uiStore: useUIStore(), servicesStore: useServicesStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums_list: new GroupedList(),
|
||||
@ -149,7 +143,6 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
albums() {
|
||||
const { options } = this.groupings.find(
|
||||
@ -161,6 +154,12 @@ export default {
|
||||
]
|
||||
return this.albums_list.group(options)
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.albums.title'),
|
||||
subtitle: [{ key: 'count.albums', count: this.albums.count }]
|
||||
}
|
||||
},
|
||||
spotify_enabled() {
|
||||
return this.servicesStore.spotify.webapi_token_valid
|
||||
}
|
||||
|
@ -33,25 +33,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="artist.name" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<span v-text="$t('count.albums', { count: albums.count })" />
|
||||
<span> | </span>
|
||||
<a
|
||||
@click="open_tracks"
|
||||
v-text="$t('count.tracks', { count: track_count })"
|
||||
/>
|
||||
</div>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.artist.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.artist.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -71,6 +65,7 @@ import ControlButton from '@/components/ControlButton.vue'
|
||||
import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import ControlSwitch from '@/components/ControlSwitch.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '../components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
import { useServicesStore } from '@/stores/services'
|
||||
@ -84,7 +79,6 @@ const dataObject = {
|
||||
webapi.library_artist_albums(to.params.id)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.artist = response[0].data
|
||||
vm.albums_list = new GroupedList(response[1].data)
|
||||
@ -98,20 +92,18 @@ export default {
|
||||
ControlButton,
|
||||
ControlDropdown,
|
||||
ControlSwitch,
|
||||
HeadingTitle,
|
||||
ListAlbums,
|
||||
ModalDialogArtist
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore(), uiStore: useUIStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums_list: new GroupedList(),
|
||||
@ -131,7 +123,6 @@ export default {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
albums() {
|
||||
const { options } = this.groupings.find(
|
||||
@ -142,6 +133,19 @@ export default {
|
||||
]
|
||||
return this.albums_list.group(options)
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.artist.name,
|
||||
subtitle: [
|
||||
{ key: 'count.albums', count: this.albums.count },
|
||||
{
|
||||
handler: this.open_tracks,
|
||||
key: 'count.tracks',
|
||||
count: this.track_count
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
spotify_enabled() {
|
||||
return this.servicesStore.spotify.webapi_token_valid
|
||||
},
|
||||
@ -153,7 +157,6 @@ export default {
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_tracks() {
|
||||
this.$router.push({
|
||||
|
@ -2,21 +2,19 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="artist.name" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.albums', { count: total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.spotify.artist.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.spotify.artist.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums-spotify :items="albums" />
|
||||
@ -48,6 +46,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
|
||||
import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
@ -71,7 +70,6 @@ const dataObject = {
|
||||
})
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.artist = response.shift()
|
||||
vm.albums = []
|
||||
@ -86,21 +84,19 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
ListAlbumsSpotify,
|
||||
ModalDialogArtistSpotify,
|
||||
VueEternalLoading
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums: [],
|
||||
@ -110,7 +106,14 @@ export default {
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('artist.name'),
|
||||
subtitle: [{ key: 'count.albums', count: this.total }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
append_albums(data) {
|
||||
this.albums = this.albums.concat(data.items)
|
||||
|
@ -34,25 +34,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="artist.name" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<a
|
||||
@click="open_artist"
|
||||
v-text="$t('count.albums', { count: album_count })"
|
||||
/>
|
||||
<span> | </span>
|
||||
<span v-text="$t('count.tracks', { count: tracks.count })" />
|
||||
</div>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.artist.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.artist.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" :uris="track_uris" />
|
||||
@ -72,6 +66,7 @@ import ControlButton from '@/components/ControlButton.vue'
|
||||
import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import ControlSwitch from '@/components/ControlSwitch.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
@ -86,7 +81,6 @@ const dataObject = {
|
||||
webapi.library_artist_tracks(to.params.id)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.artist = response[0].data
|
||||
vm.tracks_list = new GroupedList(response[1].data.tracks)
|
||||
@ -100,21 +94,19 @@ export default {
|
||||
ControlButton,
|
||||
ControlDropdown,
|
||||
ControlSwitch,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListTracks,
|
||||
ModalDialogArtist
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore(), uiStore: useUIStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
artist: {},
|
||||
@ -137,7 +129,6 @@ export default {
|
||||
tracks_list: new GroupedList()
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
album_count() {
|
||||
return new Set(
|
||||
@ -146,6 +137,19 @@ export default {
|
||||
.map((track) => track.item.album_id)
|
||||
).size
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.artist.name,
|
||||
subtitle: [
|
||||
{
|
||||
handler: this.openArtist,
|
||||
key: 'count.albums',
|
||||
count: this.album_count
|
||||
},
|
||||
{ key: 'count.tracks', count: this.tracks.count }
|
||||
]
|
||||
}
|
||||
},
|
||||
spotify_enabled() {
|
||||
return this.servicesStore.spotify.webapi_token_valid
|
||||
},
|
||||
@ -162,9 +166,8 @@ export default {
|
||||
return this.tracks_list.group(options)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_artist() {
|
||||
openArtist() {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({
|
||||
name: 'music-artist',
|
||||
|
@ -42,11 +42,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.artists.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.artists', { count: artists.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-artists :items="artists" />
|
||||
@ -60,6 +56,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import ControlSwitch from '@/components/ControlSwitch.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListArtists from '@/components/ListArtists.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -83,21 +80,19 @@ export default {
|
||||
ContentWithHeading,
|
||||
ControlDropdown,
|
||||
ControlSwitch,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListArtists,
|
||||
TabsMusic
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore(), uiStore: useUIStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
artists_list: new GroupedList(),
|
||||
@ -118,7 +113,6 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
artists() {
|
||||
const { options } = this.groupings.find(
|
||||
@ -132,6 +126,12 @@ export default {
|
||||
]
|
||||
return this.artists_list.group(options)
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.artists.title'),
|
||||
subtitle: [{ key: 'count.artists', count: this.artists.count }]
|
||||
}
|
||||
},
|
||||
spotify_enabled() {
|
||||
return this.servicesStore.spotify.webapi_token_valid
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<template #heading-left>
|
||||
<div class="title is-5" v-text="album.name" />
|
||||
<div class="subtitle is-6">
|
||||
<a @click="open_artist" v-text="album.artist" />
|
||||
<a @click="openArtist" v-text="album.artist" />
|
||||
</div>
|
||||
<div
|
||||
class="is-size-7 is-uppercase has-text-centered-mobile"
|
||||
@ -12,11 +12,15 @@
|
||||
/>
|
||||
<div class="buttons is-centered-mobile mt-5">
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="play"
|
||||
label="page.audiobooks.album.play"
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'play',
|
||||
key: 'page.audiobooks.album.play'
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-right>
|
||||
@ -57,7 +61,6 @@ const dataObject = {
|
||||
webapi.library_album_tracks(to.params.id)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.album = response[0].data
|
||||
vm.tracks = new GroupedList(response[1].data)
|
||||
@ -86,7 +89,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open_artist() {
|
||||
openArtist() {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({
|
||||
name: 'audiobooks-artist',
|
||||
|
@ -6,11 +6,7 @@
|
||||
<index-button-list :indices="albums.indices" />
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.audiobooks.albums.title')" />
|
||||
<p
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.audiobooks', { count: albums.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -22,6 +18,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
|
||||
@ -31,7 +28,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_albums('audiobook')
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums = new GroupedList(response.data, {
|
||||
index: { field: 'name_sort', type: String }
|
||||
@ -45,19 +41,26 @@ export default {
|
||||
ContentWithHeading,
|
||||
IndexButtonList,
|
||||
ListAlbums,
|
||||
TabsAudiobooks
|
||||
TabsAudiobooks,
|
||||
HeadingTitle
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums: new GroupedList()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.audiobooks.albums.title'),
|
||||
subtitle: [{ key: 'count.audiobooks', count: this.albums.count }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,21 +2,19 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="artist.name" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.audiobooks', { count: artist.album_count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="play"
|
||||
label="page.audiobooks.artist.play"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'play',
|
||||
key: 'page.audiobooks.artist.play'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -34,6 +32,7 @@
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
import webapi from '@/webapi'
|
||||
@ -56,6 +55,7 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
ListAlbums,
|
||||
ModalDialogArtist
|
||||
},
|
||||
@ -71,6 +71,19 @@ export default {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
if (this.artist.name) {
|
||||
return {
|
||||
title: this.artist.name,
|
||||
subtitle: [
|
||||
{ key: 'count.audiobooks', count: this.artist.album_count }
|
||||
]
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
play() {
|
||||
webapi.player_play_uri(
|
||||
|
@ -6,11 +6,7 @@
|
||||
<index-button-list :indices="artists.indices" />
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.audiobooks.artists.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.authors', { count: artists.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-artists :items="artists" />
|
||||
@ -22,6 +18,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListArtists from '@/components/ListArtists.vue'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
|
||||
@ -31,7 +28,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_artists('audiobook')
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.artists = new GroupedList(response.data, {
|
||||
index: { field: 'name_sort', type: String }
|
||||
@ -43,21 +39,28 @@ export default {
|
||||
name: 'PageAudiobooksArtists',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListArtists,
|
||||
TabsAudiobooks
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
artists: new GroupedList()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.audiobooks.artists.title'),
|
||||
subtitle: [{ key: 'count.authors', count: this.artists.count }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -6,11 +6,7 @@
|
||||
<index-button-list :indices="genres.indices" />
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.genres.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.genres', { count: genres.total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-genres :items="genres" :media_kind="'audiobook'" />
|
||||
@ -22,6 +18,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListGenres from '@/components/ListGenres.vue'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
|
||||
@ -31,7 +28,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_genres('audiobook')
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.genres = new GroupedList(response.data.genres, {
|
||||
index: { field: 'name_sort', type: String }
|
||||
@ -43,21 +39,28 @@ export default {
|
||||
name: 'PageAudiobooksGenres',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListGenres,
|
||||
TabsAudiobooks
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
genres: new GroupedList()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.genres.title'),
|
||||
subtitle: [{ key: 'count.genres', count: this.genres.total }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,25 +2,19 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="composer.name" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<span v-text="$t('count.albums', { count: composer.album_count })" />
|
||||
<span> | </span>
|
||||
<a
|
||||
@click="open_tracks"
|
||||
v-text="$t('count.tracks', { count: composer.track_count })"
|
||||
/>
|
||||
</div>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.composer.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.composer.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -38,6 +32,7 @@
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
||||
import webapi from '@/webapi'
|
||||
@ -49,7 +44,6 @@ const dataObject = {
|
||||
webapi.library_composer_albums(to.params.name)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.composer = response[0].data
|
||||
vm.albums = new GroupedList(response[1].data.albums)
|
||||
@ -61,6 +55,7 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
ListAlbums,
|
||||
ModalDialogComposer
|
||||
},
|
||||
@ -79,6 +74,19 @@ export default {
|
||||
computed: {
|
||||
expression() {
|
||||
return `composer is "${this.composer.name}" and media_kind is music`
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.composer.name,
|
||||
subtitle: [
|
||||
{ key: 'count.albums', count: this.composer.album_count },
|
||||
{
|
||||
handler: this.open_tracks,
|
||||
key: 'count.tracks',
|
||||
count: this.composer.track_count
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -17,25 +17,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="composer.name" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<a
|
||||
@click="open_albums"
|
||||
v-text="$t('count.albums', { count: composer.album_count })"
|
||||
/>
|
||||
<span> | </span>
|
||||
<span v-text="$t('count.tracks', { count: composer.track_count })" />
|
||||
</div>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.composer.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.composer.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" :expression="expression" />
|
||||
@ -54,6 +48,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
||||
@ -80,6 +75,7 @@ export default {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
ControlDropdown,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListTracks,
|
||||
ModalDialogComposer
|
||||
@ -118,6 +114,19 @@ export default {
|
||||
expression() {
|
||||
return `composer is "${this.composer.name}" and media_kind is music`
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.composer.name,
|
||||
subtitle: [
|
||||
{
|
||||
handler: this.open_albums,
|
||||
key: 'count.albums',
|
||||
count: this.composer.album_count
|
||||
},
|
||||
{ key: 'count.tracks', count: composer.track_count }
|
||||
]
|
||||
}
|
||||
},
|
||||
tracks() {
|
||||
const { options } = this.groupings.find(
|
||||
(grouping) => grouping.id === this.uiStore.composer_tracks_sort
|
||||
|
@ -6,11 +6,7 @@
|
||||
<index-button-list :indices="composers.indices" />
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.composers.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.composers', { count: composers.total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-composers :items="composers" />
|
||||
@ -22,6 +18,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListComposers from '@/components/ListComposers.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -31,7 +28,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_composers('music')
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.composers = new GroupedList(response.data, {
|
||||
index: { field: 'name_sort', type: String }
|
||||
@ -41,18 +37,30 @@ const dataObject = {
|
||||
|
||||
export default {
|
||||
name: 'PageComposers',
|
||||
components: { ContentWithHeading, IndexButtonList, ListComposers, TabsMusic },
|
||||
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListComposers,
|
||||
TabsMusic
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
composers: new GroupedList()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.composers.title'),
|
||||
subtitle: [{ key: 'count.composers', count: this.composers.total }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,13 +2,15 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="name" />
|
||||
<heading-title :content="{ title: name }" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button :handler="play" icon="play" label="page.files.play" />
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{ handler: play, icon: 'play', key: 'page.files.play' }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-directories :items="directories" />
|
||||
@ -32,6 +34,7 @@
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListDirectories from '@/components/ListDirectories.vue'
|
||||
import ListPlaylists from '@/components/ListPlaylists.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
@ -72,6 +75,7 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
ListDirectories,
|
||||
ListPlaylists,
|
||||
ListTracks,
|
||||
@ -105,14 +109,14 @@ export default {
|
||||
current() {
|
||||
return this.$route.query?.directory || '/'
|
||||
},
|
||||
expression() {
|
||||
return `path starts with "${this.current}" order by path asc`
|
||||
},
|
||||
name() {
|
||||
if (this.current !== '/') {
|
||||
return this.current?.slice(this.current.lastIndexOf('/') + 1)
|
||||
}
|
||||
return this.$t('page.files.title')
|
||||
},
|
||||
expression() {
|
||||
return `path starts with "${this.current}" order by path asc`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -5,25 +5,19 @@
|
||||
<index-button-list :indices="albums.indices" />
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="genre.name" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<span v-text="$t('count.albums', { count: genre.album_count })" />
|
||||
<span> | </span>
|
||||
<a
|
||||
@click="open_tracks"
|
||||
v-text="$t('count.tracks', { count: genre.track_count })"
|
||||
/>
|
||||
</div>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.genre.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.genre.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -42,6 +36,7 @@
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||
@ -67,6 +62,7 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListAlbums,
|
||||
ModalDialogGenre
|
||||
@ -84,6 +80,21 @@ export default {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.genre.name,
|
||||
subtitle: [
|
||||
{ key: 'count.albums', count: this.genre.album_count },
|
||||
{
|
||||
handler: this.open_tracks,
|
||||
key: 'count.tracks',
|
||||
count: this.genre.track_count
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open_tracks() {
|
||||
this.show_details_modal = false
|
||||
|
@ -17,25 +17,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="genre.name" />
|
||||
<div class="is-size-7 is-uppercase">
|
||||
<a
|
||||
@click="open_genre"
|
||||
v-text="$t('count.albums', { count: genre.album_count })"
|
||||
/>
|
||||
<span> | </span>
|
||||
<span v-text="$t('count.tracks', { count: genre.track_count })" />
|
||||
</div>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.genre.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.genre.shuffle'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" :expression="expression" />
|
||||
@ -55,6 +49,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||
@ -68,7 +63,6 @@ const dataObject = {
|
||||
webapi.library_genre_tracks(to.params.name, to.query.media_kind)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.genre = response[0].data.genres.items.shift()
|
||||
vm.tracks_list = new GroupedList(response[1].data.tracks)
|
||||
@ -81,6 +75,7 @@ export default {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
ControlDropdown,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListTracks,
|
||||
ModalDialogGenre
|
||||
@ -120,6 +115,19 @@ export default {
|
||||
expression() {
|
||||
return `genre is "${this.genre.name}" and media_kind is ${this.media_kind}`
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.genre.name,
|
||||
subtitle: [
|
||||
{
|
||||
handler: this.openGenre,
|
||||
key: 'count.albums',
|
||||
count: this.genre.album_count
|
||||
},
|
||||
{ key: 'count.tracks', count: this.genre.track_count }
|
||||
]
|
||||
}
|
||||
},
|
||||
tracks() {
|
||||
const { options } = this.groupings.find(
|
||||
(grouping) => grouping.id === this.uiStore.genre_tracks_sort
|
||||
@ -128,7 +136,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open_genre() {
|
||||
openGenre() {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({
|
||||
name: 'genre-albums',
|
||||
|
@ -6,11 +6,7 @@
|
||||
<index-button-list :indices="genres.indices" />
|
||||
</template>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.genres.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.genres', { count: genres.total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-genres :items="genres" :media_kind="'music'" />
|
||||
@ -22,6 +18,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListGenres from '@/components/ListGenres.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -31,7 +28,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_genres('music')
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.genres = new GroupedList(response.data.genres, {
|
||||
index: { field: 'name_sort', type: String }
|
||||
@ -43,21 +39,28 @@ export default {
|
||||
name: 'PageGenres',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
IndexButtonList,
|
||||
ListGenres,
|
||||
TabsMusic
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
genres: new GroupedList()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.genres.title'),
|
||||
subtitle: [{ key: 'count.genres', count: this.genres.total }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -3,42 +3,38 @@
|
||||
<tabs-music />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.music.recently-added.title')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.music.recently-added.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<router-link
|
||||
class="button is-small is-rounded"
|
||||
:to="{ name: 'music-recently-added' }"
|
||||
>
|
||||
{{ $t('page.music.show-more') }}
|
||||
</router-link>
|
||||
</p>
|
||||
</nav>
|
||||
<router-link
|
||||
class="button is-small is-rounded"
|
||||
:to="{ name: 'music-recently-added' }"
|
||||
>
|
||||
{{ $t('page.music.show-more') }}
|
||||
</router-link>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.music.recently-played.title')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.music.recently-played.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<router-link
|
||||
class="button is-small is-rounded"
|
||||
:to="{ name: 'music-recently-played' }"
|
||||
>
|
||||
{{ $t('page.music.show-more') }}
|
||||
</router-link>
|
||||
</p>
|
||||
</nav>
|
||||
<router-link
|
||||
class="button is-small is-rounded"
|
||||
:to="{ name: 'music-recently-played' }"
|
||||
>
|
||||
{{ $t('page.music.show-more') }}
|
||||
</router-link>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
@ -47,6 +43,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -69,7 +66,6 @@ const dataObject = {
|
||||
})
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums = new GroupedList(response[0].data.albums)
|
||||
vm.tracks = new GroupedList(response[1].data.tracks)
|
||||
@ -78,14 +74,18 @@ const dataObject = {
|
||||
|
||||
export default {
|
||||
name: 'PageMusic',
|
||||
components: { ContentWithHeading, ListAlbums, ListTracks, TabsMusic },
|
||||
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
ListAlbums,
|
||||
ListTracks,
|
||||
TabsMusic
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums: [],
|
||||
|
@ -3,7 +3,9 @@
|
||||
<tabs-music />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.music.recently-added.title')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.music.recently-added.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums :items="albums" />
|
||||
@ -15,6 +17,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
@ -30,7 +33,6 @@ const dataObject = {
|
||||
type: 'album'
|
||||
})
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums = new GroupedList(response.data.albums, {
|
||||
criteria: [{ field: 'time_added', order: -1, type: Date }],
|
||||
@ -41,20 +43,17 @@ const dataObject = {
|
||||
|
||||
export default {
|
||||
name: 'PageMusicRecentlyAdded',
|
||||
components: { ContentWithHeading, ListAlbums, TabsMusic },
|
||||
|
||||
components: { ContentWithHeading, HeadingTitle, ListAlbums, TabsMusic },
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
settingsStore: useSettingsStore()
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums: new GroupedList()
|
||||
|
@ -3,7 +3,9 @@
|
||||
<tabs-music />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.music.recently-played.title')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.music.recently-played.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" />
|
||||
@ -15,6 +17,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import webapi from '@/webapi'
|
||||
@ -28,7 +31,6 @@ const dataObject = {
|
||||
type: 'track'
|
||||
})
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.tracks = new GroupedList(response.data.tracks)
|
||||
}
|
||||
@ -36,14 +38,12 @@ const dataObject = {
|
||||
|
||||
export default {
|
||||
name: 'PageMusicRecentlyPlayed',
|
||||
components: { ContentWithHeading, ListTracks, TabsMusic },
|
||||
|
||||
components: { ContentWithHeading, HeadingTitle, ListTracks, TabsMusic },
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
tracks: {}
|
||||
|
@ -3,45 +3,38 @@
|
||||
<tabs-music />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.spotify.music.new-releases')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.spotify.music.new-releases') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums-spotify :items="albums" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<router-link
|
||||
:to="{ name: 'music-spotify-new-releases' }"
|
||||
class="button is-small is-rounded"
|
||||
>
|
||||
{{ $t('page.spotify.music.show-more') }}
|
||||
</router-link>
|
||||
</p>
|
||||
</nav>
|
||||
<router-link
|
||||
:to="{ name: 'music-spotify-new-releases' }"
|
||||
class="button is-small is-rounded"
|
||||
>
|
||||
{{ $t('page.spotify.music.show-more') }}
|
||||
</router-link>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p
|
||||
class="title is-4"
|
||||
v-text="$t('page.spotify.music.featured-playlists')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.spotify.music.featured-playlists') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-playlists-spotify :items="playlists" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<router-link
|
||||
:to="{ name: 'music-spotify-featured-playlists' }"
|
||||
class="button is-small is-rounded"
|
||||
>
|
||||
{{ $t('page.spotify.music.show-more') }}
|
||||
</router-link>
|
||||
</p>
|
||||
</nav>
|
||||
<router-link
|
||||
:to="{ name: 'music-spotify-featured-playlists' }"
|
||||
class="button is-small is-rounded"
|
||||
>
|
||||
{{ $t('page.spotify.music.show-more') }}
|
||||
</router-link>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
@ -49,6 +42,7 @@
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
|
||||
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
@ -72,7 +66,6 @@ const dataObject = {
|
||||
])
|
||||
})
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums = response[0].albums.items
|
||||
vm.playlists = response[1].playlists.items
|
||||
@ -83,17 +76,16 @@ export default {
|
||||
name: 'PageMusicSpotify',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
ListAlbumsSpotify,
|
||||
ListPlaylistsSpotify,
|
||||
TabsMusic
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
playlists: [],
|
||||
|
@ -3,10 +3,7 @@
|
||||
<tabs-music />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p
|
||||
class="title is-4"
|
||||
v-text="$t('page.spotify.music.featured-playlists')"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-playlists-spotify :items="playlists" />
|
||||
@ -17,6 +14,7 @@
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -33,7 +31,6 @@ const dataObject = {
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.playlists = response.playlists.items
|
||||
}
|
||||
@ -43,20 +40,24 @@ export default {
|
||||
name: 'PageMusicSpotifyFeaturedPlaylists',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
ListPlaylistsSpotify,
|
||||
TabsMusic
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
playlists: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return { title: this.$t('page.spotify.music.featured-playlists') }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<tabs-music />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.spotify.music.new-releases')" />
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums-spotify :items="albums" />
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
@ -30,7 +31,6 @@ const dataObject = {
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums = response.albums.items
|
||||
}
|
||||
@ -40,20 +40,24 @@ export default {
|
||||
name: 'PageMusicSpotifyNewReleases',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
ListAlbumsSpotify,
|
||||
TabsMusic
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return { title: this.$t('page.spotify.music.new-releases') }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,16 +2,7 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div
|
||||
class="title is-4"
|
||||
v-text="
|
||||
playlist.id === 0 ? $t('page.playlists.title') : playlist.name
|
||||
"
|
||||
/>
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.playlists', { count: playlists.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-playlists :items="playlists" />
|
||||
@ -23,6 +14,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListPlaylists from '@/components/ListPlaylists.vue'
|
||||
import { useConfigurationStore } from '@/stores/configuration'
|
||||
import webapi from '@/webapi'
|
||||
@ -34,7 +26,6 @@ const dataObject = {
|
||||
webapi.library_playlist_folder(to.params.id)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.playlist = response[0].data
|
||||
vm.playlists_list = new GroupedList(response[1].data)
|
||||
@ -43,35 +34,39 @@ const dataObject = {
|
||||
|
||||
export default {
|
||||
name: 'PagePlaylistFolder',
|
||||
components: { ContentWithHeading, ListPlaylists },
|
||||
|
||||
components: { ContentWithHeading, ListPlaylists, HeadingTitle },
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
beforeRouteUpdate(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(this, response)
|
||||
next()
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
configurationStore: useConfigurationStore()
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
playlist: {},
|
||||
playlists_list: new GroupedList()
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title:
|
||||
this.playlists.count === 0
|
||||
? this.$t('page.playlists.title')
|
||||
: this.playlist.name,
|
||||
subtitle: [{ key: 'count.playlists', count: this.playlists.count }]
|
||||
}
|
||||
},
|
||||
playlists() {
|
||||
return this.playlists_list.group({
|
||||
filters: [
|
||||
|
@ -2,22 +2,20 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="playlist.name" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.tracks', { count: tracks.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:disabled="tracks.count === 0"
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.playlist.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.playlist.shuffle',
|
||||
disabled: tracks.count === 0
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" :uris="uris" />
|
||||
@ -36,6 +34,7 @@
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
|
||||
import webapi from '@/webapi'
|
||||
@ -47,7 +46,6 @@ const dataObject = {
|
||||
webapi.library_playlist_tracks(to.params.id)
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.playlist = response[0].data
|
||||
vm.tracks = new GroupedList(response[1].data)
|
||||
@ -59,16 +57,15 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
ListTracks,
|
||||
ModalDialogPlaylist
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
playlist: {},
|
||||
@ -76,8 +73,13 @@ export default {
|
||||
tracks: new GroupedList()
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.playlist.name,
|
||||
subtitle: [{ key: 'count.tracks', count: this.tracks.count }]
|
||||
}
|
||||
},
|
||||
uris() {
|
||||
if (this.playlist.random) {
|
||||
return this.tracks.map((item) => item.uri).join()
|
||||
@ -85,7 +87,6 @@ export default {
|
||||
return this.playlist.uri
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play() {
|
||||
webapi.player_play_uri(this.uris, true)
|
||||
|
@ -2,22 +2,20 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="playlist.name" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.playlists', { count: playlist.tracks.total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
<control-button
|
||||
:disabled="playlist.tracks.total === 0"
|
||||
:handler="play"
|
||||
icon="shuffle"
|
||||
label="page.spotify.playlist.shuffle"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: play,
|
||||
icon: 'shuffle',
|
||||
key: 'page.spotify.playlist.shuffle',
|
||||
disabled: playlist.tracks.total === 0
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks-spotify :items="tracks" :context_uri="playlist.uri" />
|
||||
@ -46,6 +44,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
|
||||
import ModalDialogPlaylistSpotify from '@/components/ModalDialogPlaylistSpotify.vue'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
@ -68,7 +67,6 @@ const dataObject = {
|
||||
})
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.playlist = response.shift()
|
||||
vm.tracks = []
|
||||
@ -85,19 +83,17 @@ export default {
|
||||
ControlButton,
|
||||
ListTracksSpotify,
|
||||
ModalDialogPlaylistSpotify,
|
||||
HeadingTitle,
|
||||
VueEternalLoading
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
offset: 0,
|
||||
@ -107,7 +103,16 @@ export default {
|
||||
tracks: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.playlist.name,
|
||||
subtitle: [
|
||||
{ key: 'count.playlists', count: this.playlist.tracks.total }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
append_tracks(data) {
|
||||
let position = Math.max(
|
||||
|
@ -12,11 +12,11 @@
|
||||
/>
|
||||
<div class="buttons is-centered-mobile mt-5">
|
||||
<control-button
|
||||
:handler="play"
|
||||
icon="play"
|
||||
label="page.podcast.play"
|
||||
:button="{ handler: play, icon: 'play', key: 'page.podcast.play' }"
|
||||
/>
|
||||
<control-button
|
||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
||||
/>
|
||||
<control-button :handler="showDetails" icon="dots-horizontal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #heading-right>
|
||||
@ -119,12 +119,12 @@ export default {
|
||||
actions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('page.podcast.cancel'),
|
||||
key: this.$t('page.podcast.cancel'),
|
||||
handler: 'cancel',
|
||||
icon: 'cancel'
|
||||
},
|
||||
{
|
||||
label: this.$t('page.podcast.remove'),
|
||||
key: this.$t('page.podcast.remove'),
|
||||
handler: 'remove',
|
||||
icon: 'delete'
|
||||
}
|
||||
|
@ -2,58 +2,56 @@
|
||||
<div>
|
||||
<content-with-heading v-if="tracks.items.length > 0">
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.podcasts.new-episodes')" />
|
||||
<heading-title :content="{ title: $t('page.podcasts.new-episodes') }" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button
|
||||
:handler="mark_all_played"
|
||||
icon="pencil"
|
||||
label="page.podcasts.mark-all-played"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: markAllAsPlayed,
|
||||
icon: 'pencil',
|
||||
key: 'page.podcasts.mark-all-played'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks
|
||||
:items="tracks"
|
||||
:show_progress="true"
|
||||
@play-count-changed="reload_new_episodes"
|
||||
@play-count-changed="reloadNewEpisodes"
|
||||
/>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.podcasts.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.podcasts', { count: albums.total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button
|
||||
v-if="rss.tracks > 0"
|
||||
:handler="update_rss"
|
||||
icon="refresh"
|
||||
label="page.podcasts.update"
|
||||
/>
|
||||
<control-button
|
||||
:handler="open_add_podcast_dialog"
|
||||
icon="rss"
|
||||
label="page.podcasts.add"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
v-if="rss.tracks > 0"
|
||||
:button="{
|
||||
handler: updateRss,
|
||||
icon: 'refresh',
|
||||
key: 'page.podcasts.update'
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: 'openAddPodcastDialog',
|
||||
icon: 'rss',
|
||||
key: 'page.podcasts.add'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<list-albums
|
||||
:items="albums"
|
||||
@play-count-changed="reload_new_episodes()"
|
||||
@podcast-deleted="reload_podcasts()"
|
||||
@play-count-changed="reloadNewEpisodes()"
|
||||
@podcast-deleted="reloadPodcasts()"
|
||||
/>
|
||||
<modal-dialog-add-rss
|
||||
:show="show_url_modal"
|
||||
@close="show_url_modal = false"
|
||||
@podcast-added="reload_podcasts()"
|
||||
@podcast-added="reloadPodcasts()"
|
||||
/>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@ -64,6 +62,7 @@
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogAddRss from '@/components/ModalDialogAddRss.vue'
|
||||
@ -78,7 +77,6 @@ const dataObject = {
|
||||
webapi.library_podcasts_new_episodes()
|
||||
])
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.albums = new GroupedList(response[0].data)
|
||||
vm.tracks = new GroupedList(response[1].data.tracks)
|
||||
@ -90,21 +88,19 @@ export default {
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlButton,
|
||||
HeadingTitle,
|
||||
ListAlbums,
|
||||
ListTracks,
|
||||
ModalDialogAddRss
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { libraryStore: useLibraryStore(), uiStore: useUIStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
albums: [],
|
||||
@ -112,39 +108,42 @@ export default {
|
||||
show_url_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
heading() {
|
||||
if (this.albums.total) {
|
||||
return {
|
||||
title: this.$t('page.podcasts.title'),
|
||||
subtitle: [{ key: 'count.podcasts', count: this.albums.count }]
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
rss() {
|
||||
return this.libraryStore.rss
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
mark_all_played() {
|
||||
markAllAsPlayed() {
|
||||
this.tracks.items.forEach((ep) => {
|
||||
webapi.library_track_update(ep.id, { play_count: 'increment' })
|
||||
})
|
||||
this.tracks.items = {}
|
||||
},
|
||||
|
||||
open_add_podcast_dialog() {
|
||||
openAddPodcastDialog() {
|
||||
this.show_url_modal = true
|
||||
},
|
||||
|
||||
reload_new_episodes() {
|
||||
reloadNewEpisodes() {
|
||||
webapi.library_podcasts_new_episodes().then(({ data }) => {
|
||||
this.tracks = new GroupedList(data.tracks)
|
||||
})
|
||||
},
|
||||
|
||||
reload_podcasts() {
|
||||
reloadPodcasts() {
|
||||
webapi.library_albums('podcast').then(({ data }) => {
|
||||
this.albums = new GroupedList(data)
|
||||
this.reload_new_episodes()
|
||||
this.reloadNewEpisodes()
|
||||
})
|
||||
},
|
||||
|
||||
update_rss() {
|
||||
updateRss() {
|
||||
this.libraryStore.update_dialog_scan_kind = 'rss'
|
||||
this.uiStore.show_update_dialog = true
|
||||
}
|
||||
|
@ -2,46 +2,50 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.queue.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.tracks', { count: queue.count })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<control-button
|
||||
:handler="update_show_next_items"
|
||||
:class="{ 'is-dark': show_only_next_items }"
|
||||
icon="eye-off-outline"
|
||||
label="page.queue.hide-previous"
|
||||
/>
|
||||
<control-button
|
||||
:handler="open_add_stream_dialog"
|
||||
icon="web"
|
||||
label="page.queue.add-stream"
|
||||
/>
|
||||
<control-button
|
||||
:class="{ 'is-dark': edit_mode }"
|
||||
:disabled="queue_items.length === 0"
|
||||
:handler="toggleEdit"
|
||||
icon="pencil"
|
||||
label="page.queue.edit"
|
||||
/>
|
||||
<control-button
|
||||
:disabled="queue_items.length === 0"
|
||||
:handler="queue_clear"
|
||||
icon="delete-empty"
|
||||
label="page.queue.clear"
|
||||
/>
|
||||
<control-button
|
||||
v-if="is_queue_save_allowed"
|
||||
:disabled="queue_items.length === 0"
|
||||
:handler="save_dialog"
|
||||
icon="download"
|
||||
label="page.queue.save"
|
||||
/>
|
||||
</div>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: update_show_next_items,
|
||||
icon: 'eye-off-outline',
|
||||
key: 'page.queue.hide-previous',
|
||||
class: { 'is-dark': show_only_next_items }
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: open_add_stream_dialog,
|
||||
icon: 'web',
|
||||
key: 'page.queue.add-stream'
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: toggleEdit,
|
||||
icon: 'pencil',
|
||||
key: 'page.queue.edit',
|
||||
disabled: queue_items.length === 0,
|
||||
class: { 'is-dark': edit_mode }
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
:button="{
|
||||
handler: queue_clear,
|
||||
icon: 'delete-empty',
|
||||
key: 'page.queue.clear',
|
||||
disabled: queue_items.length === 0
|
||||
}"
|
||||
/>
|
||||
<control-button
|
||||
v-if="is_queue_save_allowed"
|
||||
:button="{
|
||||
handler: save_dialog,
|
||||
icon: 'download',
|
||||
key: 'page.queue.save',
|
||||
disabled: queue_items.length === 0
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<draggable v-model="queue_items" item-key="id" @end="move_item">
|
||||
@ -93,6 +97,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlButton from '@/components/ControlButton.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListItemQueueItem from '@/components/ListItemQueueItem.vue'
|
||||
import ModalDialogAddStream from '@/components/ModalDialogAddStream.vue'
|
||||
import ModalDialogPlaylistSave from '@/components/ModalDialogPlaylistSave.vue'
|
||||
@ -113,9 +118,9 @@ export default {
|
||||
ModalDialogAddStream,
|
||||
ModalDialogPlaylistSave,
|
||||
ModalDialogQueueItem,
|
||||
HeadingTitle,
|
||||
draggable
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
configurationStore: useConfigurationStore(),
|
||||
@ -124,7 +129,6 @@ export default {
|
||||
uiStore: useUIStore()
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
edit_mode: false,
|
||||
@ -134,11 +138,16 @@ export default {
|
||||
show_url_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
current_position() {
|
||||
return this.queue.current?.position ?? -1
|
||||
},
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.queue.title'),
|
||||
subtitle: [{ key: 'count.tracks', count: this.queue.count }]
|
||||
}
|
||||
},
|
||||
is_queue_save_allowed() {
|
||||
return (
|
||||
this.configurationStore.allow_modifying_stored_playlists &&
|
||||
|
@ -2,11 +2,7 @@
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.radio.title')" />
|
||||
<div
|
||||
class="is-size-7 is-uppercase"
|
||||
v-text="$t('count.stations', { count: tracks.total })"
|
||||
/>
|
||||
<heading-title :content="heading" />
|
||||
</template>
|
||||
<template #content>
|
||||
<list-tracks :items="tracks" />
|
||||
@ -18,6 +14,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
@ -25,7 +22,6 @@ const dataObject = {
|
||||
load(to) {
|
||||
return webapi.library_radio_streams()
|
||||
},
|
||||
|
||||
set(vm, response) {
|
||||
vm.tracks = new GroupedList(response.data.tracks)
|
||||
}
|
||||
@ -33,18 +29,24 @@ const dataObject = {
|
||||
|
||||
export default {
|
||||
name: 'PageRadioStreams',
|
||||
components: { ContentWithHeading, ListTracks },
|
||||
|
||||
components: { ContentWithHeading, ListTracks, HeadingTitle },
|
||||
beforeRouteEnter(to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next((vm) => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
tracks: new GroupedList()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
heading() {
|
||||
return {
|
||||
title: this.$t('page.radio.title'),
|
||||
subtitle: [{ key: 'count.stations', count: this.tracks.total }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -51,7 +51,7 @@
|
||||
<template v-for="[type, items] in results" :key="type">
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t(`page.search.${type}s`)" />
|
||||
<heading-title :content="{ title: $t(`page.search.${type}s`) }" />
|
||||
</template>
|
||||
<template #content>
|
||||
<component :is="components[type]" :items="items" />
|
||||
@ -83,6 +83,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import { GroupedList } from '@/lib/GroupedList'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ListArtists from '@/components/ListArtists.vue'
|
||||
import ListComposers from '@/components/ListComposers.vue'
|
||||
@ -107,6 +108,7 @@ export default {
|
||||
name: 'PageSearchLibrary',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
ListAlbums,
|
||||
ListArtists,
|
||||
ListComposers,
|
||||
|
@ -34,7 +34,9 @@
|
||||
<template v-for="[type, items] in results" :key="type">
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t(`page.spotify.search.${type}s`)" />
|
||||
<heading-title
|
||||
:content="{ title: $t(`page.spotify.search.${type}s`) }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<component :is="components[type]" :items="items.items" />
|
||||
@ -77,6 +79,7 @@
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
|
||||
import ListArtistsSpotify from '@/components/ListArtistsSpotify.vue'
|
||||
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
|
||||
@ -95,6 +98,7 @@ export default {
|
||||
name: 'PageSearchSpotify',
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
HeadingTitle,
|
||||
ListAlbumsSpotify,
|
||||
ListArtistsSpotify,
|
||||
ListPlaylistsSpotify,
|
||||
@ -102,11 +106,9 @@ export default {
|
||||
TabsSearch,
|
||||
VueEternalLoading
|
||||
},
|
||||
|
||||
setup() {
|
||||
return { searchStore: useSearchStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
components: {
|
||||
@ -121,7 +123,6 @@ export default {
|
||||
search_types: SEARCH_TYPES
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
expanded() {
|
||||
return this.search_types.length === 1
|
||||
@ -132,20 +133,17 @@ export default {
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
search_query() {
|
||||
this.searchStore.search_query = this.search_query
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.searchStore.search_source = this.$route.name
|
||||
this.search_query = this.searchStore.search_query
|
||||
this.search_parameters.limit = PAGE_SIZE
|
||||
this.search()
|
||||
},
|
||||
|
||||
methods: {
|
||||
expand(type) {
|
||||
this.search_query = this.searchStore.search_query
|
||||
|
@ -3,7 +3,9 @@
|
||||
<tabs-settings />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.settings.artwork.title')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.artwork.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<div
|
||||
@ -79,17 +81,21 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlSettingSwitch from '@/components/ControlSettingSwitch.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import { useServicesStore } from '@/stores/services'
|
||||
|
||||
export default {
|
||||
name: 'PageSettingsArtwork',
|
||||
components: { ContentWithHeading, ControlSettingSwitch, TabsSettings },
|
||||
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ControlSettingSwitch,
|
||||
HeadingTitle,
|
||||
TabsSettings
|
||||
},
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore() }
|
||||
},
|
||||
|
||||
computed: {
|
||||
spotify() {
|
||||
return this.servicesStore.spotify
|
||||
|
@ -3,9 +3,8 @@
|
||||
<tabs-settings />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div
|
||||
class="title is-4"
|
||||
v-text="$t('page.settings.services.spotify.title')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.services.spotify.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
@ -59,9 +58,8 @@
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div
|
||||
class="title is-4"
|
||||
v-text="$t('page.settings.services.lastfm.title')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.services.lastfm.title') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
@ -127,18 +125,17 @@
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import { useServicesStore } from '@/stores/services'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PageSettingsOnlineServices',
|
||||
components: { ContentWithHeading, TabsSettings },
|
||||
|
||||
components: { ContentWithHeading, HeadingTitle, TabsSettings },
|
||||
setup() {
|
||||
return { servicesStore: useServicesStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
lastfm_login: {
|
||||
@ -148,7 +145,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
lastfm() {
|
||||
return this.servicesStore.lastfm
|
||||
@ -175,7 +171,6 @@ export default {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
login_lastfm() {
|
||||
webapi.lastfm_login(this.lastfm_login).then((response) => {
|
||||
|
@ -3,7 +3,9 @@
|
||||
<tabs-settings />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p class="title is-4" v-text="$t('page.settings.devices.pairing')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.devices.pairing') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<div v-if="pairing.active">
|
||||
@ -39,9 +41,8 @@
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<p
|
||||
class="title is-4"
|
||||
v-text="$t('page.settings.devices.speaker-pairing')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.devices.speaker-pairing') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
@ -91,6 +92,7 @@
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ControlSwitch from '@/components/ControlSwitch.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import { useOutputsStore } from '@/stores/outputs'
|
||||
import { useRemotesStore } from '@/stores/remotes'
|
||||
@ -98,19 +100,16 @@ import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PageSettingsRemotesOutputs',
|
||||
components: { ContentWithHeading, ControlSwitch, TabsSettings },
|
||||
|
||||
components: { ContentWithHeading, ControlSwitch, HeadingTitle, TabsSettings },
|
||||
setup() {
|
||||
return { outputsStore: useOutputsStore(), remotesStore: useRemotesStore() }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
pairing_req: { pin: '' },
|
||||
verification_req: { pin: '' }
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
outputs() {
|
||||
return this.outputsStore.outputs
|
||||
@ -119,7 +118,6 @@ export default {
|
||||
return this.remotesStore.pairing
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
kickoff_pairing() {
|
||||
webapi.pairing_kickoff(this.pairing_req)
|
||||
|
@ -3,7 +3,9 @@
|
||||
<tabs-settings />
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div class="title is-4" v-text="$t('page.settings.general.language')" />
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.general.language') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<control-dropdown v-model:value="locale" :options="locales" />
|
||||
@ -11,9 +13,8 @@
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div
|
||||
class="title is-4"
|
||||
v-text="$t('page.settings.general.navigation-items')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.general.navigation-items') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
@ -85,9 +86,8 @@
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div
|
||||
class="title is-4"
|
||||
v-text="$t('page.settings.general.now-playing-page')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.general.now-playing-page') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
@ -134,9 +134,8 @@
|
||||
</content-with-heading>
|
||||
<content-with-heading>
|
||||
<template #heading-left>
|
||||
<div
|
||||
class="title is-4"
|
||||
v-text="$t('page.settings.general.recently-added-page')"
|
||||
<heading-title
|
||||
:content="{ title: $t('page.settings.general.recently-added-page') }"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
@ -161,6 +160,7 @@ import ControlDropdown from '@/components/ControlDropdown.vue'
|
||||
import ControlSettingIntegerField from '@/components/ControlSettingIntegerField.vue'
|
||||
import ControlSettingSwitch from '@/components/ControlSettingSwitch.vue'
|
||||
import ControlSettingTextField from '@/components/ControlSettingTextField.vue'
|
||||
import HeadingTitle from '@/components/HeadingTitle.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
|
||||
@ -172,6 +172,7 @@ export default {
|
||||
ControlSettingIntegerField,
|
||||
ControlSettingSwitch,
|
||||
ControlSettingTextField,
|
||||
HeadingTitle,
|
||||
TabsSettings
|
||||
},
|
||||
|
||||
|
@ -24,14 +24,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right has-text-centered-mobile">
|
||||
<slot name="heading-right" />
|
||||
<div
|
||||
v-if="$slots['heading-right']"
|
||||
class="level-right has-text-centered-mobile"
|
||||
>
|
||||
<div class="buttons">
|
||||
<slot name="heading-right" />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<slot name="content" />
|
||||
<div class="mt-4">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
<nav v-if="$slots.footer" class="level mt-4">
|
||||
<div class="level-item">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user