mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-10 22:10:15 -05:00
[web] Migration to Vue 3 and Vite
This commit is contained in:
@@ -34,27 +34,27 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Artists</th>
|
||||
<td class="has-text-right">{{ library.artists | number }}</td>
|
||||
<td class="has-text-right">{{ $filters.number(library.artists) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Albums</th>
|
||||
<td class="has-text-right">{{ library.albums | number }}</td>
|
||||
<td class="has-text-right">{{ $filters.number(library.albums) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Tracks</th>
|
||||
<td class="has-text-right">{{ library.songs | number }}</td>
|
||||
<td class="has-text-right">{{ $filters.number(library.songs) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total playtime</th>
|
||||
<td class="has-text-right">{{ library.db_playtime * 1000 | duration('y [years], d [days], h [hours], m [minutes]') }}</td>
|
||||
<td class="has-text-right">{{ $filters.duration(library.db_playtime * 1000, 'y [years], d [days], h [hours], m [minutes]') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Library updated</th>
|
||||
<td class="has-text-right">{{ library.updated_at | timeFromNow }} <span class="has-text-grey">({{ library.updated_at | time('lll') }})</span></td>
|
||||
<td class="has-text-right">{{ $filters.timeFromNow(library.updated_at) }} <span class="has-text-grey">({{ $filters.time(library.updated_at, 'lll') }})</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Uptime</th>
|
||||
<td class="has-text-right">{{ library.started_at | timeFromNow(true) }} <span class="has-text-grey">({{ library.started_at | time('ll') }})</span></td>
|
||||
<td class="has-text-right">{{ $filters.timeFromNow(library.started_at, true) }} <span class="has-text-grey">({{ $filters.time(library.started_at, 'll') }})</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -68,7 +68,7 @@
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
<div class="content has-text-centered-mobile">
|
||||
<p class="is-size-7">Compiled with support for {{ config.buildoptions | join }}.</p>
|
||||
<p class="is-size-7">Compiled with support for {{ config.buildoptions.join(', ') }}.</p>
|
||||
<p class="is-size-7">Web interface built with <a href="http://bulma.io">Bulma</a>, <a href="https://materialdesignicons.com/">Material Design Icons</a>, <a href="https://vuejs.org/">Vue.js</a>, <a href="https://github.com/mzabriskie/axios">axios</a> and <a href="https://github.com/owntone/owntone-server/network/dependencies">more</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,12 +107,6 @@ export default {
|
||||
showUpdateDialog () {
|
||||
this.$store.commit(types.SHOW_UPDATE_DIALOG, true)
|
||||
}
|
||||
},
|
||||
|
||||
filters: {
|
||||
join: function (array) {
|
||||
return array.join(', ')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<content-with-hero>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<h1 class="title is-5">{{ album.name }}</h1>
|
||||
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artist }}</a></h2>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<p class="image is-square fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="album.artwork_url"
|
||||
@@ -22,7 +22,7 @@
|
||||
@click="show_album_details_modal = true" />
|
||||
</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.track_count }} tracks</p>
|
||||
<list-tracks :tracks="tracks" :uris="album.uri"></list-tracks>
|
||||
<modal-dialog-album :show="show_album_details_modal" :album="album" @close="show_album_details_modal = false" />
|
||||
@@ -31,14 +31,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHero from '@/templates/ContentWithHero'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHero from '@/templates/ContentWithHero.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_album(to.params.album_id),
|
||||
@@ -54,7 +53,6 @@ const albumData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAlbum',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
||||
components: { ContentWithHero, ListTracks, ModalDialogAlbum, CoverArtwork },
|
||||
|
||||
data () {
|
||||
@@ -75,6 +73,19 @@ export default {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.album.uri, true)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="albums_list.indexList"></index-button-list>
|
||||
|
||||
<div class="columns">
|
||||
@@ -30,13 +30,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Albums</p>
|
||||
<p class="heading">{{ albums_list.sortedAndFiltered.length }} Albums</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="albums_list"></list-albums>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -44,17 +44,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import DropdownMenu from '@/components/DropdownMenu'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import DropdownMenu from '@/components/DropdownMenu.vue'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import Albums from '@/lib/Albums'
|
||||
|
||||
const albumsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_albums('music')
|
||||
},
|
||||
@@ -69,7 +68,6 @@ const albumsData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAlbums',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumsData)],
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListAlbums, DropdownMenu },
|
||||
|
||||
data () {
|
||||
@@ -125,6 +123,23 @@ export default {
|
||||
scrollToTop: function () {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
if (this.albums.items.length > 0) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<p class="heading" style="margin-bottom: 24px;">Sort by</p>
|
||||
@@ -8,10 +8,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ artist.name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -21,7 +21,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ artist.album_count }} albums | <a class="has-text-link" @click="open_tracks">{{ artist.track_count }} tracks</a></p>
|
||||
<list-albums :albums="albums_list"></list-albums>
|
||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
||||
@@ -30,16 +30,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||
import DropdownMenu from '@/components/DropdownMenu'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
import DropdownMenu from '@/components/DropdownMenu.vue'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import Albums from '@/lib/Albums'
|
||||
|
||||
const artistData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_artist(to.params.artist_id),
|
||||
@@ -55,7 +54,6 @@ const artistData = {
|
||||
|
||||
export default {
|
||||
name: 'PageArtist',
|
||||
mixins: [LoadDataBeforeEnterMixin(artistData)],
|
||||
components: { ContentWithHeading, ListAlbums, ModalDialogArtist, DropdownMenu },
|
||||
|
||||
data () {
|
||||
@@ -94,6 +92,19 @@ export default {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.albums.items.map(a => a.uri).join(','), true)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ artist.name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -17,7 +17,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_artist">{{ artist.album_count }} albums</a> | {{ artist.track_count }} tracks</p>
|
||||
<list-tracks :tracks="tracks.items" :uris="track_uris"></list-tracks>
|
||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
||||
@@ -27,14 +27,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const tracksData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_artist(to.params.artist_id),
|
||||
@@ -50,7 +49,6 @@ const tracksData = {
|
||||
|
||||
export default {
|
||||
name: 'PageArtistTracks',
|
||||
mixins: [LoadDataBeforeEnterMixin(tracksData)],
|
||||
components: { ContentWithHeading, ListTracks, IndexButtonList, ModalDialogArtist },
|
||||
|
||||
data () {
|
||||
@@ -82,6 +80,19 @@ export default {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.tracks.items.map(a => a.uri).join(','), true)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="artists_list.indexList"></index-button-list>
|
||||
|
||||
<div class="columns">
|
||||
@@ -30,13 +30,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Artists</p>
|
||||
<p class="heading">{{ artists_list.sortedAndFiltered.length }} Artists</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-artists :artists="artists_list"></list-artists>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -44,17 +44,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListArtists from '@/components/ListArtists'
|
||||
import DropdownMenu from '@/components/DropdownMenu'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListArtists from '@/components/ListArtists.vue'
|
||||
import DropdownMenu from '@/components/DropdownMenu.vue'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import Artists from '@/lib/Artists'
|
||||
|
||||
const artistsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_artists('music')
|
||||
},
|
||||
@@ -66,7 +65,6 @@ const artistsData = {
|
||||
|
||||
export default {
|
||||
name: 'PageArtists',
|
||||
mixins: [LoadDataBeforeEnterMixin(artistsData)],
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListArtists, DropdownMenu },
|
||||
|
||||
data () {
|
||||
@@ -122,6 +120,23 @@ export default {
|
||||
scrollToTop: function () {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
if (this.artists.items.length > 0) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<content-with-hero>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<h1 class="title is-5">{{ album.name }}</h1>
|
||||
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artist }}</a></h2>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<p class="image is-square fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="album.artwork_url"
|
||||
@@ -22,7 +22,7 @@
|
||||
@click="show_album_details_modal = true" />
|
||||
</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.track_count }} tracks</p>
|
||||
<list-tracks :tracks="tracks" :uris="album.uri"></list-tracks>
|
||||
<modal-dialog-album :show="show_album_details_modal" :album="album" :media_kind="'audiobook'" @close="show_album_details_modal = false" />
|
||||
@@ -31,14 +31,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHero from '@/templates/ContentWithHero'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHero from '@/templates/ContentWithHero.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_album(to.params.album_id),
|
||||
@@ -54,7 +53,6 @@ const albumData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAudiobooksAlbum',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
||||
components: { ContentWithHero, ListTracks, ModalDialogAlbum, CoverArtwork },
|
||||
|
||||
data () {
|
||||
@@ -84,6 +82,19 @@ export default {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-audiobooks></tabs-audiobooks>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="albums_list.indexList"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Audiobooks</p>
|
||||
<p class="heading">{{ albums_list.sortedAndFiltered.length }} Audiobooks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="albums_list"></list-albums>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -18,15 +18,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import webapi from '@/webapi'
|
||||
import Albums from '@/lib/Albums'
|
||||
|
||||
const albumsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_albums('audiobook')
|
||||
},
|
||||
@@ -38,7 +37,6 @@ const albumsData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAudiobooksAlbums',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumsData)],
|
||||
components: { TabsAudiobooks, ContentWithHeading, IndexButtonList, ListAlbums },
|
||||
|
||||
data () {
|
||||
@@ -57,6 +55,19 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ artist.name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -13,7 +13,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ artist.album_count }} albums</p>
|
||||
<list-albums :albums="albums.items"></list-albums>
|
||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
||||
@@ -22,13 +22,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const artistData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_artist(to.params.artist_id),
|
||||
@@ -44,7 +43,6 @@ const artistData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAudiobooksArtist',
|
||||
mixins: [LoadDataBeforeEnterMixin(artistData)],
|
||||
components: { ContentWithHeading, ListAlbums, ModalDialogArtist },
|
||||
|
||||
data () {
|
||||
@@ -60,6 +58,19 @@ export default {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.albums.items.map(a => a.uri).join(','), false)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-audiobooks></tabs-audiobooks>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="artists_list.indexList"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Authors</p>
|
||||
<p class="heading">{{ artists_list.sortedAndFiltered.length }} Authors</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-artists :artists="artists_list"></list-artists>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -20,15 +20,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListArtists from '@/components/ListArtists'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListArtists from '@/components/ListArtists.vue'
|
||||
import webapi from '@/webapi'
|
||||
import Artists from '@/lib/Artists'
|
||||
|
||||
const artistsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_artists('audiobook')
|
||||
},
|
||||
@@ -40,7 +39,6 @@ const artistsData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAudiobooksArtists',
|
||||
mixins: [LoadDataBeforeEnterMixin(artistsData)],
|
||||
components: { ContentWithHeading, TabsAudiobooks, IndexButtonList, ListArtists },
|
||||
|
||||
data () {
|
||||
@@ -59,6 +57,19 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<!-- Recently added -->
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Recently added</p>
|
||||
<p class="heading">albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="recently_added.items"></list-albums>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_browse('recently_added')">Show more</a>
|
||||
@@ -22,14 +22,14 @@
|
||||
|
||||
<!-- Recently played -->
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Recently played</p>
|
||||
<p class="heading">tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-tracks :tracks="recently_played.items"></list-tracks>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_browse('recently_played')">Show more</a>
|
||||
@@ -41,14 +41,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const browseData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.search({ type: 'album', expression: 'time_added after 8 weeks ago and media_kind is music having track_count > 3 order by time_added desc', limit: 3 }),
|
||||
@@ -64,7 +63,6 @@ const browseData = {
|
||||
|
||||
export default {
|
||||
name: 'PageBrowse',
|
||||
mixins: [LoadDataBeforeEnterMixin(browseData)],
|
||||
components: { ContentWithHeading, TabsMusic, ListAlbums, ListTracks },
|
||||
|
||||
data () {
|
||||
@@ -81,6 +79,19 @@ export default {
|
||||
open_browse: function (type) {
|
||||
this.$router.push({ path: '/music/browse/' + type })
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Recently added</p>
|
||||
<p class="heading">albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="albums_list"></list-albums>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -15,15 +15,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import webapi from '@/webapi'
|
||||
import store from '@/store'
|
||||
import Albums from '@/lib/Albums'
|
||||
|
||||
const browseData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
const limit = store.getters.settings_option_recently_added_limit
|
||||
return webapi.search({
|
||||
@@ -40,7 +39,6 @@ const browseData = {
|
||||
|
||||
export default {
|
||||
name: 'PageBrowseType',
|
||||
mixins: [LoadDataBeforeEnterMixin(browseData)],
|
||||
components: { ContentWithHeading, TabsMusic, ListAlbums },
|
||||
|
||||
data () {
|
||||
@@ -58,6 +56,19 @@ export default {
|
||||
group: true
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Recently played</p>
|
||||
<p class="heading">tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-tracks :tracks="recently_played.items"></list-tracks>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -15,13 +15,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const browseData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.search({
|
||||
type: 'track',
|
||||
@@ -37,13 +36,25 @@ const browseData = {
|
||||
|
||||
export default {
|
||||
name: 'PageBrowseType',
|
||||
mixins: [LoadDataBeforeEnterMixin(browseData)],
|
||||
components: { ContentWithHeading, TabsMusic, ListTracks },
|
||||
|
||||
data () {
|
||||
return {
|
||||
recently_played: {}
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -14,10 +17,10 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ composer_albums.total }} albums | <a class="has-text-link" @click="open_tracks">tracks</a></p>
|
||||
<list-item-albums v-for="album in composer_albums.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<template slot="actions">
|
||||
<template slot:actions>
|
||||
<a @click="open_dialog(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -31,14 +34,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemAlbums from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListItemAlbums from '@/components/ListItemAlbum.vue'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const composerData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_composer(to.params.composer)
|
||||
},
|
||||
@@ -51,7 +53,6 @@ const composerData = {
|
||||
|
||||
export default {
|
||||
name: 'PageComposer',
|
||||
mixins: [LoadDataBeforeEnterMixin(composerData)],
|
||||
components: { ContentWithHeading, ListItemAlbums, ModalDialogAlbum, ModalDialogComposer },
|
||||
|
||||
data () {
|
||||
@@ -90,6 +91,19 @@ export default {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ composer }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
</a>
|
||||
<a class="button is-small is-dark is-rounded" @click="play">
|
||||
@@ -14,11 +17,11 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_albums">albums</a> | {{ tracks.total }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in rated_tracks" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<template v-slot:actions>
|
||||
<a @click.prevent.stop="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
@@ -31,14 +34,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListItemTrack from '@/components/ListItemTrack.vue'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const tracksData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_composer_tracks(to.params.composer)
|
||||
},
|
||||
@@ -51,7 +53,6 @@ const tracksData = {
|
||||
|
||||
export default {
|
||||
name: 'PageComposerTracks',
|
||||
mixins: [LoadDataBeforeEnterMixin(tracksData)],
|
||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack, ModalDialogComposer },
|
||||
|
||||
data () {
|
||||
@@ -104,6 +105,19 @@ export default {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="composers_list.indexList"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ heading }}</p>
|
||||
<p class="heading">{{ composers.total }} composers</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-composers :composers="composers_list"></list-composers>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -18,15 +18,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListComposers from '@/components/ListComposers'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListComposers from '@/components/ListComposers.vue'
|
||||
import webapi from '@/webapi'
|
||||
import Composers from '@/lib/Composers'
|
||||
|
||||
const composersData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_composers()
|
||||
},
|
||||
@@ -44,7 +43,6 @@ const composersData = {
|
||||
|
||||
export default {
|
||||
name: 'PageComposers',
|
||||
mixins: [LoadDataBeforeEnterMixin(composersData)],
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListComposers },
|
||||
|
||||
data () {
|
||||
@@ -80,6 +78,19 @@ export default {
|
||||
this.selected_composer = composer
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Files</p>
|
||||
<p class="title is-7 has-text-grey">{{ current_directory }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="open_directory_dialog({ 'path': current_directory })">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -15,7 +15,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<div class="media" v-if="$route.query.directory" @click="open_parent_directory()">
|
||||
<figure class="media-left fd-has-action">
|
||||
<span class="icon">
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
|
||||
<list-item-directory v-for="directory in files.directories" :key="directory.path" :directory="directory" @click="open_directory(directory)">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_directory_dialog(directory)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -39,12 +39,12 @@
|
||||
</list-item-directory>
|
||||
|
||||
<list-item-playlist v-for="playlist in files.playlists.items" :key="playlist.id" :playlist="playlist" @click="open_playlist(playlist)">
|
||||
<template slot="icon">
|
||||
<template v-slot:icon>
|
||||
<span class="icon">
|
||||
<i class="mdi mdi-library-music"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_playlist_dialog(playlist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -52,12 +52,12 @@
|
||||
</list-item-playlist>
|
||||
|
||||
<list-item-track v-for="(track, index) in files.tracks.items" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="icon">
|
||||
<template v-slot:icon>
|
||||
<span class="icon">
|
||||
<i class="mdi mdi-file-outline"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_track_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -73,17 +73,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemDirectory from '@/components/ListItemDirectory'
|
||||
import ListItemPlaylist from '@/components/ListItemPlaylist'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogDirectory from '@/components/ModalDialogDirectory'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListItemDirectory from '@/components/ListItemDirectory.vue'
|
||||
import ListItemPlaylist from '@/components/ListItemPlaylist.vue'
|
||||
import ListItemTrack from '@/components/ListItemTrack.vue'
|
||||
import ModalDialogDirectory from '@/components/ModalDialogDirectory.vue'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const filesData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
if (to.query.directory) {
|
||||
return webapi.library_files(to.query.directory)
|
||||
@@ -106,7 +105,6 @@ const filesData = {
|
||||
|
||||
export default {
|
||||
name: 'PageFiles',
|
||||
mixins: [LoadDataBeforeEnterMixin(filesData)],
|
||||
components: { ContentWithHeading, ListItemDirectory, ListItemPlaylist, ListItemTrack, ModalDialogDirectory, ModalDialogPlaylist, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
@@ -173,6 +171,19 @@ export default {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_genre_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -17,7 +17,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ genre_albums.total }} albums | <a class="has-text-link" @click="open_tracks">tracks</a></p>
|
||||
<list-albums :albums="genre_albums.items"></list-albums>
|
||||
<modal-dialog-genre :show="show_genre_details_modal" :genre="{ 'name': name }" @close="show_genre_details_modal = false" />
|
||||
@@ -27,14 +27,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const genreData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_genre(to.params.genre)
|
||||
},
|
||||
@@ -47,7 +46,6 @@ const genreData = {
|
||||
|
||||
export default {
|
||||
name: 'PageGenre',
|
||||
mixins: [LoadDataBeforeEnterMixin(genreData)],
|
||||
components: { ContentWithHeading, IndexButtonList, ListAlbums, ModalDialogGenre },
|
||||
|
||||
data () {
|
||||
@@ -80,6 +78,19 @@ export default {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ genre }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_genre_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -17,7 +17,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_genre">albums</a> | {{ tracks.total }} tracks</p>
|
||||
<list-tracks :tracks="tracks.items" :expression="expression"></list-tracks>
|
||||
<modal-dialog-genre :show="show_genre_details_modal" :genre="{ 'name': genre }" @close="show_genre_details_modal = false" />
|
||||
@@ -27,14 +27,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const tracksData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_genre_tracks(to.params.genre)
|
||||
},
|
||||
@@ -47,7 +46,6 @@ const tracksData = {
|
||||
|
||||
export default {
|
||||
name: 'PageGenreTracks',
|
||||
mixins: [LoadDataBeforeEnterMixin(tracksData)],
|
||||
components: { ContentWithHeading, ListTracks, IndexButtonList, ModalDialogGenre },
|
||||
|
||||
data () {
|
||||
@@ -79,6 +77,19 @@ export default {
|
||||
play: function () {
|
||||
webapi.player_play_expression(this.expression, true)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<template v-slot:options>
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Genres</p>
|
||||
<p class="heading">{{ genres.total }} genres</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-item-genre v-for="genre in genres.items" :key="genre.name" :genre="genre" @click="open_genre(genre)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(genre)">
|
||||
<template v-slot:actions>
|
||||
<a @click.prevent.stop="open_dialog(genre)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
@@ -25,15 +25,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListItemGenre from '@/components/ListItemGenre'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import IndexButtonList from '@/components/IndexButtonList.vue'
|
||||
import ListItemGenre from '@/components/ListItemGenre.vue'
|
||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const genresData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_genres()
|
||||
},
|
||||
@@ -45,7 +44,6 @@ const genresData = {
|
||||
|
||||
export default {
|
||||
name: 'PageGenres',
|
||||
mixins: [LoadDataBeforeEnterMixin(genresData)],
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemGenre, ModalDialogGenre },
|
||||
|
||||
data () {
|
||||
@@ -73,6 +71,19 @@ export default {
|
||||
this.selected_genre = genre
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -11,7 +11,15 @@
|
||||
<div class="fd-has-padding-left-right">
|
||||
<div class="container has-text-centered">
|
||||
<p class="control has-text-centered fd-progress-now-playing">
|
||||
<range-slider
|
||||
<Slider v-model="item_progress_ms"
|
||||
:min="0"
|
||||
:max="state.item_length_ms"
|
||||
:step="1000"
|
||||
:tooltips="false"
|
||||
:disabled="state.state === 'stop'"
|
||||
@change="seek"
|
||||
:classes="{ target: 'seek-slider'}" />
|
||||
<!--range-slider
|
||||
class="seek-slider fd-has-action"
|
||||
min="0"
|
||||
:max="state.item_length_ms"
|
||||
@@ -19,10 +27,10 @@
|
||||
:disabled="state.state === 'stop'"
|
||||
step="1000"
|
||||
@change="seek" >
|
||||
</range-slider>
|
||||
</range-slider-->
|
||||
</p>
|
||||
<p class="content">
|
||||
<span>{{ item_progress_ms | duration }} / {{ now_playing.length_ms | duration }}</span>
|
||||
<span>{{ $filters.duration(item_progress_ms) }} / {{ $filters.duration(now_playing.length_ms) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,15 +68,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||
import RangeSlider from 'vue-range-slider'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem.vue'
|
||||
//import RangeSlider from 'vue-range-slider'
|
||||
import Slider from '@vueform/slider'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
export default {
|
||||
name: 'PageNowPlaying',
|
||||
components: { ModalDialogQueueItem, RangeSlider, CoverArtwork },
|
||||
components: {
|
||||
ModalDialogQueueItem,
|
||||
// RangeSlider,
|
||||
Slider,
|
||||
CoverArtwork
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">{{ playlist.name }}</div>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_playlist_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -13,7 +13,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ tracks.length }} tracks</p>
|
||||
<list-tracks :tracks="tracks" :uris="uris"></list-tracks>
|
||||
<modal-dialog-playlist :show="show_playlist_details_modal" :playlist="playlist" :uris="uris" @close="show_playlist_details_modal = false" />
|
||||
@@ -22,13 +22,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const playlistData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_playlist(to.params.playlist_id),
|
||||
@@ -44,7 +43,6 @@ const playlistData = {
|
||||
|
||||
export default {
|
||||
name: 'PagePlaylist',
|
||||
mixins: [LoadDataBeforeEnterMixin(playlistData)],
|
||||
components: { ContentWithHeading, ListTracks, ModalDialogPlaylist },
|
||||
|
||||
data () {
|
||||
@@ -69,6 +67,19 @@ export default {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.uris, true)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ playlist.name }}</p>
|
||||
<p class="heading">{{ playlists.total }} playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-playlists :playlists="playlists.items"></list-playlists>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListPlaylists from '@/components/ListPlaylists'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListPlaylists from '@/components/ListPlaylists.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const playlistsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_playlist(to.params.playlist_id),
|
||||
@@ -32,7 +31,6 @@ const playlistsData = {
|
||||
|
||||
export default {
|
||||
name: 'PagePlaylists',
|
||||
mixins: [LoadDataBeforeEnterMixin(playlistsData)],
|
||||
components: { ContentWithHeading, ListPlaylists },
|
||||
|
||||
data () {
|
||||
@@ -40,6 +38,19 @@ export default {
|
||||
playlist: {},
|
||||
playlists: {}
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">{{ album.name }}
|
||||
</div>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -17,21 +17,14 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
||||
<list-item-track v-for="track in tracks" :key="track.id" :track="track" @click="play_track(track)">
|
||||
<template slot="progress">
|
||||
<range-slider
|
||||
class="track-progress"
|
||||
min="0"
|
||||
:max="track.length_ms"
|
||||
step="1"
|
||||
:disabled="true"
|
||||
:value="track.seek_ms" >
|
||||
</range-slider>
|
||||
<template v-slot:progress>
|
||||
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<template v-slot:actions>
|
||||
<a @click.prevent.stop="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
@@ -55,7 +48,7 @@
|
||||
delete_action="Remove"
|
||||
@close="show_remove_podcast_modal = false"
|
||||
@delete="remove_podcast">
|
||||
<template slot="modal-content">
|
||||
<template v-slot:modal-content>
|
||||
<p>Permanently remove this podcast from your library?</p>
|
||||
<p class="is-size-7">(This will also remove the RSS playlist <b>{{ rss_playlist_to_remove.name }}</b>.)</p>
|
||||
</template>
|
||||
@@ -65,16 +58,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import RangeSlider from 'vue-range-slider'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListItemTrack from '@/components/ListItemTrack.vue'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
||||
import ModalDialog from '@/components/ModalDialog.vue'
|
||||
import ProgressBar from '@/components/ProgressBar.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_album(to.params.album_id),
|
||||
@@ -90,8 +82,14 @@ const albumData = {
|
||||
|
||||
export default {
|
||||
name: 'PagePodcast',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack, RangeSlider, ModalDialogAlbum, ModalDialog },
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ListItemTrack,
|
||||
ModalDialogTrack,
|
||||
ModalDialogAlbum,
|
||||
ModalDialog,
|
||||
ProgressBar
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
@@ -154,6 +152,19 @@ export default {
|
||||
this.tracks = data.tracks.items
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading v-if="new_episodes.items.length > 0">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">New episodes</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small" @click="mark_all_played">
|
||||
<span class="icon">
|
||||
@@ -14,19 +14,12 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-item-track v-for="track in new_episodes.items" :key="track.id" :track="track" @click="play_track(track)">
|
||||
<template slot="progress">
|
||||
<range-slider
|
||||
class="track-progress"
|
||||
min="0"
|
||||
:max="track.length_ms"
|
||||
step="1"
|
||||
:disabled="true"
|
||||
:value="track.seek_ms" >
|
||||
</range-slider>
|
||||
<template v-slot:progress>
|
||||
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_track_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -37,11 +30,11 @@
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Podcasts</p>
|
||||
<p class="heading">{{ albums.total }} podcasts</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a v-if="rss.tracks > 0" class="button is-small" @click="update_rss">
|
||||
<span class="icon">
|
||||
@@ -57,7 +50,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="albums.items"
|
||||
@play-count-changed="reload_new_episodes()"
|
||||
@podcast-deleted="reload_podcasts()">
|
||||
@@ -72,17 +65,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ModalDialogAddRss from '@/components/ModalDialogAddRss'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListItemTrack from '@/components/ListItemTrack.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
||||
import ModalDialogAddRss from '@/components/ModalDialogAddRss.vue'
|
||||
import ProgressBar from '@/components/ProgressBar.vue'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import RangeSlider from 'vue-range-slider'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return Promise.all([
|
||||
webapi.library_albums('podcast'),
|
||||
@@ -98,8 +90,14 @@ const albumsData = {
|
||||
|
||||
export default {
|
||||
name: 'PagePodcasts',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumsData)],
|
||||
components: { ContentWithHeading, ListItemTrack, ListAlbums, ModalDialogTrack, ModalDialogAddRss, RangeSlider },
|
||||
components: {
|
||||
ContentWithHeading,
|
||||
ListItemTrack,
|
||||
ListAlbums,
|
||||
ModalDialogTrack,
|
||||
ModalDialogAddRss,
|
||||
ProgressBar
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
@@ -157,6 +155,19 @@ export default {
|
||||
this.$store.commit(types.UPDATE_DIALOG_SCAN_KIND, 'rss')
|
||||
this.$store.commit(types.SHOW_UPDATE_DIALOG, true)
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="heading">{{ queue.count }} tracks</p>
|
||||
<p class="title is-4">Queue</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small" :class="{ 'is-info': show_only_next_items }" @click="update_show_next_items">
|
||||
<span class="icon">
|
||||
@@ -38,22 +38,25 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<draggable v-model="queue_items" handle=".handle" @end="move_item">
|
||||
<list-item-queue-item v-for="(item, index) in queue_items"
|
||||
:key="item.id" :item="item" :position="index"
|
||||
:current_position="current_position"
|
||||
:show_only_next_items="show_only_next_items"
|
||||
:edit_mode="edit_mode">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(item)" v-if="!edit_mode">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<a @click="remove(item)" v-if="item.id !== state.item_id && edit_mode">
|
||||
<span class="icon has-text-grey"><i class="mdi mdi-delete mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-queue-item>
|
||||
<template v-slot:content>
|
||||
<draggable v-model="queue_items" handle=".handle" item-key="id" @end="move_item">
|
||||
<template #item="{ element, index }">
|
||||
<list-item-queue-item
|
||||
:item="element"
|
||||
:position="index"
|
||||
:current_position="current_position"
|
||||
:show_only_next_items="show_only_next_items"
|
||||
:edit_mode="edit_mode">
|
||||
<template v-slot:actions>
|
||||
<a @click.prevent.stop="open_dialog(element)" v-if="!edit_mode">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<a @click.prevent.stop="remove(element)" v-if="element.id !== state.item_id && edit_mode">
|
||||
<span class="icon has-text-grey"><i class="mdi mdi-delete mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-queue-item>
|
||||
</template>
|
||||
</draggable>
|
||||
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
||||
<modal-dialog-add-url-stream :show="show_url_modal" @close="show_url_modal = false" />
|
||||
@@ -63,11 +66,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemQueueItem from '@/components/ListItemQueueItem'
|
||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||
import ModalDialogAddUrlStream from '@/components/ModalDialogAddUrlStream'
|
||||
import ModalDialogPlaylistSave from '@/components/ModalDialogPlaylistSave'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListItemQueueItem from '@/components/ListItemQueueItem.vue'
|
||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem.vue'
|
||||
import ModalDialogAddUrlStream from '@/components/ModalDialogAddUrlStream.vue'
|
||||
import ModalDialogPlaylistSave from '@/components/ModalDialogPlaylistSave.vue'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Radio</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ tracks.total }} tracks</p>
|
||||
<list-tracks :tracks="tracks.items"></list-tracks>
|
||||
</template>
|
||||
@@ -13,12 +13,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const streamsData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
return webapi.library_radio_streams()
|
||||
},
|
||||
@@ -30,13 +29,25 @@ const streamsData = {
|
||||
|
||||
export default {
|
||||
name: 'PageRadioStreams',
|
||||
mixins: [LoadDataBeforeEnterMixin(streamsData)],
|
||||
components: { ContentWithHeading, ListTracks },
|
||||
|
||||
data () {
|
||||
return {
|
||||
tracks: { items: [] }
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
|
||||
<!-- Tracks -->
|
||||
<content-with-heading v-if="show_tracks && tracks.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-tracks :tracks="tracks.items"></list-tracks>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_tracks_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_tracks">Show all {{ tracks.total.toLocaleString() }} tracks</a>
|
||||
@@ -45,20 +45,20 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_tracks && !tracks.total" class="mt-6">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No tracks found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Artists -->
|
||||
<content-with-heading v-if="show_artists && artists.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Artists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-artists :artists="artists.items"></list-artists>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_artists_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_artists">Show all {{ artists.total.toLocaleString() }} artists</a>
|
||||
@@ -67,20 +67,20 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_artists && !artists.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No artists found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Albums -->
|
||||
<content-with-heading v-if="show_albums && albums.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="albums.items"></list-albums>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_albums_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_albums">Show all {{ albums.total.toLocaleString() }} albums</a>
|
||||
@@ -89,20 +89,20 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_albums && !albums.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No albums found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Composers -->
|
||||
<content-with-heading v-if="show_composers && composers.total">
|
||||
<template slot="heading-left">
|
||||
<template slot:heading-left>
|
||||
<p class="title is-4">Composers</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template slot:content>
|
||||
<list-composers :composers="composers.items"></list-composers>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template slot:footer>
|
||||
<nav v-if="show_all_composers_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_composers">Show all {{ composers.total }} composers</a>
|
||||
@@ -111,20 +111,20 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_composers && !composers.total">
|
||||
<template slot="content">
|
||||
<template slot:content>
|
||||
<p><i>No composers found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Playlists -->
|
||||
<content-with-heading v-if="show_playlists && playlists.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-playlists :playlists="playlists.items"></list-playlists>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_playlists_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_playlists">Show all {{ playlists.total.toLocaleString() }} playlists</a>
|
||||
@@ -133,20 +133,20 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_playlists && !playlists.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No playlists found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Podcasts -->
|
||||
<content-with-heading v-if="show_podcasts && podcasts.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Podcasts</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="podcasts.items"></list-albums>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_podcasts_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_podcasts">Show all {{ podcasts.total.toLocaleString() }} podcasts</a>
|
||||
@@ -155,20 +155,20 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_podcasts && !podcasts.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No podcasts found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Audiobooks -->
|
||||
<content-with-heading v-if="show_audiobooks && audiobooks.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Audiobooks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<list-albums :albums="audiobooks.items"></list-albums>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_audiobooks_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_audiobooks">Show all {{ audiobooks.total.toLocaleString() }} audiobooks</a>
|
||||
@@ -177,7 +177,7 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_audiobooks && !audiobooks.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No audiobooks found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
@@ -185,14 +185,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ContentText from '@/templates/ContentText'
|
||||
import TabsSearch from '@/components/TabsSearch'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ListArtists from '@/components/ListArtists'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ListComposers from '@/components/ListComposers'
|
||||
import ListPlaylists from '@/components/ListPlaylists'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ContentText from '@/templates/ContentText.vue'
|
||||
import TabsSearch from '@/components/TabsSearch.vue'
|
||||
import ListTracks from '@/components/ListTracks.vue'
|
||||
import ListArtists from '@/components/ListArtists.vue'
|
||||
import ListAlbums from '@/components/ListAlbums.vue'
|
||||
import ListComposers from '@/components/ListComposers.vue'
|
||||
import ListPlaylists from '@/components/ListPlaylists.vue'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Artwork</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<div class="content">
|
||||
<p>
|
||||
OwnTone supports PNG and JPEG artwork which is either placed as separate image files in the library,
|
||||
@@ -16,13 +16,13 @@
|
||||
<p>In addition to that, you can enable fetching artwork from the following artwork providers:</p>
|
||||
</div>
|
||||
<settings-checkbox category_name="artwork" option_name="use_artwork_source_spotify" v-if="spotify.libspotify_logged_in">
|
||||
<template slot="label"> Spotify</template>
|
||||
<template v-slot:label> Spotify</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="artwork" option_name="use_artwork_source_discogs">
|
||||
<template slot="label"> Discogs (<a href="https://www.discogs.com/">https://www.discogs.com/</a>)</template>
|
||||
<template v-slot:label> Discogs (<a href="https://www.discogs.com/">https://www.discogs.com/</a>)</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="artwork" option_name="use_artwork_source_coverartarchive">
|
||||
<template slot="label"> Cover Art Archive (<a href="https://coverartarchive.org/">https://coverartarchive.org/</a>)</template>
|
||||
<template v-slot:label> Cover Art Archive (<a href="https://coverartarchive.org/">https://coverartarchive.org/</a>)</template>
|
||||
</settings-checkbox>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -30,9 +30,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import SettingsCheckbox from '@/components/SettingsCheckbox'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import SettingsCheckbox from '@/components/SettingsCheckbox.vue'
|
||||
|
||||
export default {
|
||||
name: 'SettingsPageArtwork',
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Spotify</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<div class="notification is-size-7" v-if="!spotify.spotify_installed">
|
||||
<p>OwnTone was either built without support for Spotify or libspotify is not installed.</p>
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@
|
||||
</p>
|
||||
<p class="help is-danger" v-if="spotify_missing_scope.length > 0">
|
||||
Please reauthorize Web API access to grant OwnTone the following additional access rights:
|
||||
<b><code>{{ spotify_missing_scope | join }}</code></b>
|
||||
<b><code>{{ spotify_missing_scope.join() }}</code></b>
|
||||
</p>
|
||||
<div class="field fd-has-margin-top ">
|
||||
<div class="control">
|
||||
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
<p class="help">
|
||||
Access to the Spotify Web API enables scanning of your Spotify library. Required scopes are
|
||||
<code>{{ spotify_required_scope | join }}</code>.
|
||||
<code>{{ spotify_required_scope.join() }}</code>.
|
||||
</p>
|
||||
<div v-if="spotify.webapi_token_valid" class="field fd-has-margin-top ">
|
||||
<div class="control">
|
||||
@@ -78,11 +78,11 @@
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Last.fm</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<div class="notification is-size-7" v-if="!lastfm.enabled">
|
||||
<p>OwnTone was built without support for Last.fm.</p>
|
||||
</div>
|
||||
@@ -121,8 +121,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Remote Pairing</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<!-- Paring request active -->
|
||||
<div class="notification" v-if="pairing.active">
|
||||
<form v-on:submit.prevent="kickoff_pairing">
|
||||
@@ -32,11 +32,11 @@
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Speaker pairing and device verification</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="content">
|
||||
If your speaker requires pairing then activate it below and enter the PIN that it displays.
|
||||
</p>
|
||||
@@ -66,8 +66,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Navbar items</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="content">
|
||||
Select the top navigation bar menu items
|
||||
</p>
|
||||
@@ -15,56 +15,56 @@
|
||||
If you select more items than can be shown on your screen then the burger menu will disappear.
|
||||
</div>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_playlists">
|
||||
<template slot="label"> Playlists</template>
|
||||
<template v-slot:label> Playlists</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_music">
|
||||
<template slot="label"> Music</template>
|
||||
<template v-slot:label> Music</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_podcasts">
|
||||
<template slot="label"> Podcasts</template>
|
||||
<template v-slot:label> Podcasts</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_audiobooks">
|
||||
<template slot="label"> Audiobooks</template>
|
||||
<template v-slot:label> Audiobooks</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_radio">
|
||||
<template slot="label"> Radio</template>
|
||||
<template v-slot:label> Radio</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_files">
|
||||
<template slot="label"> Files</template>
|
||||
<template v-slot:label> Files</template>
|
||||
</settings-checkbox>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_menu_item_search">
|
||||
<template slot="label"> Search</template>
|
||||
<template v-slot:label> Search</template>
|
||||
</settings-checkbox>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Album lists</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_cover_artwork_in_album_lists">
|
||||
<template slot="label"> Show cover artwork in album list</template>
|
||||
<template v-slot:label> Show cover artwork in album list</template>
|
||||
</settings-checkbox>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Now playing page</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<settings-checkbox category_name="webinterface" option_name="show_composer_now_playing">
|
||||
<template slot="label"> Show composer</template>
|
||||
<template slot="info">If enabled the composer of the current playing track is shown on the "now playing page"</template>
|
||||
<template v-slot:label> Show composer</template>
|
||||
<template v-slot:info>If enabled the composer of the current playing track is shown on the "now playing page"</template>
|
||||
</settings-checkbox>
|
||||
<settings-textfield category_name="webinterface" option_name="show_composer_for_genre"
|
||||
:disabled="!settings_option_show_composer_now_playing"
|
||||
placeholder="Genres">
|
||||
<template slot="label">Show composer only for listed genres</template>
|
||||
<template slot="info">
|
||||
<template v-slot:label>Show composer only for listed genres</template>
|
||||
<template v-slot:info>
|
||||
<p class="help">
|
||||
Comma separated list of genres the composer should be displayed on the "now playing page".
|
||||
</p>
|
||||
@@ -82,13 +82,13 @@
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">Recently added page</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<settings-intfield category_name="webinterface" option_name="recently_added_limit">
|
||||
<template slot="label">Limit the number of albums shown on the "Recently Added" page</template>
|
||||
<template v-slot:label>Limit the number of albums shown on the "Recently Added" page</template>
|
||||
</settings-intfield>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
@@ -96,11 +96,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import SettingsCheckbox from '@/components/SettingsCheckbox'
|
||||
import SettingsTextfield from '@/components/SettingsTextfield'
|
||||
import SettingsIntfield from '@/components/SettingsIntfield'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsSettings from '@/components/TabsSettings.vue'
|
||||
import SettingsCheckbox from '@/components/SettingsCheckbox.vue'
|
||||
import SettingsTextfield from '@/components/SettingsTextfield.vue'
|
||||
import SettingsIntfield from '@/components/SettingsIntfield.vue'
|
||||
|
||||
export default {
|
||||
name: 'SettingsPageWebinterface',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<content-with-hero>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<h1 class="title is-5">{{ album.name }}</h1>
|
||||
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artists[0].name }}</a></h2>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<p class="image is-square fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="artwork_url"
|
||||
@@ -24,10 +24,10 @@
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.tracks.total }} tracks</p>
|
||||
<spotify-list-item-track v-for="(track, index) in album.tracks.items" :key="track.id" :track="track" :position="index" :album="album" :context_uri="album.uri">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_track_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -40,17 +40,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHero from '@/templates/ContentWithHero'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHero from '@/templates/ContentWithHero.vue'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack.vue'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack.vue'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import store from '@/store'
|
||||
import webapi from '@/webapi'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
||||
const albumData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
const spotifyApi = new SpotifyWebApi()
|
||||
spotifyApi.setAccessToken(store.state.spotify.webapi_token)
|
||||
@@ -64,7 +63,6 @@ const albumData = {
|
||||
|
||||
export default {
|
||||
name: 'PageAlbum',
|
||||
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
||||
components: { ContentWithHero, SpotifyListItemTrack, SpotifyModalDialogTrack, SpotifyModalDialogAlbum, CoverArtwork },
|
||||
|
||||
data () {
|
||||
@@ -101,6 +99,19 @@ export default {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">{{ artist.name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -13,13 +13,13 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ total }} albums</p>
|
||||
<spotify-list-item-album v-for="album in albums"
|
||||
:key="album.id"
|
||||
:album="album"
|
||||
@click="open_album(album)">
|
||||
<template slot="artwork" v-if="is_visible_artwork">
|
||||
<template v-slot:artwork v-if="is_visible_artwork">
|
||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="artwork_url(album)"
|
||||
@@ -29,13 +29,13 @@
|
||||
:maxheight="64" />
|
||||
</p>
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(album)">
|
||||
<template v-slot:actions>
|
||||
<a @click.prevent.stop="open_dialog(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-album>
|
||||
<infinite-loading v-if="offset < total" @infinite="load_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<VueEternalLoading v-if="offset < total" :load="load_next"><template #no-more>.</template></VueEternalLoading>
|
||||
<spotify-modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
<spotify-modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
||||
</template>
|
||||
@@ -43,24 +43,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||
import SpotifyModalDialogArtist from '@/components/SpotifyModalDialogArtist'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum.vue'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum.vue'
|
||||
import SpotifyModalDialogArtist from '@/components/SpotifyModalDialogArtist.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import store from '@/store'
|
||||
import webapi from '@/webapi'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
import { VueEternalLoading } from '@ts-pro/vue-eternal-loading'
|
||||
|
||||
const artistData = {
|
||||
const PAGE_SIZE = 50
|
||||
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
const spotifyApi = new SpotifyWebApi()
|
||||
spotifyApi.setAccessToken(store.state.spotify.webapi_token)
|
||||
return Promise.all([
|
||||
spotifyApi.getArtist(to.params.artist_id),
|
||||
spotifyApi.getArtistAlbums(to.params.artist_id, { limit: 50, offset: 0, include_groups: 'album,single', market: store.state.spotify.webapi_country })
|
||||
spotifyApi.getArtistAlbums(to.params.artist_id, { limit: PAGE_SIZE, offset: 0, include_groups: 'album,single', market: store.state.spotify.webapi_country })
|
||||
])
|
||||
},
|
||||
|
||||
@@ -76,8 +77,7 @@ const artistData = {
|
||||
|
||||
export default {
|
||||
name: 'SpotifyPageArtist',
|
||||
mixins: [LoadDataBeforeEnterMixin(artistData)],
|
||||
components: { ContentWithHeading, SpotifyListItemAlbum, SpotifyModalDialogAlbum, SpotifyModalDialogArtist, InfiniteLoading, CoverArtwork },
|
||||
components: { ContentWithHeading, SpotifyListItemAlbum, SpotifyModalDialogAlbum, SpotifyModalDialogArtist, VueEternalLoading, CoverArtwork },
|
||||
|
||||
data () {
|
||||
return {
|
||||
@@ -100,25 +100,19 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
load_next: function ($state) {
|
||||
load_next: function ({ loaded }) {
|
||||
const spotifyApi = new SpotifyWebApi()
|
||||
spotifyApi.setAccessToken(this.$store.state.spotify.webapi_token)
|
||||
spotifyApi.getArtistAlbums(this.artist.id, { limit: 50, offset: this.offset, include_groups: 'album,single' }).then(data => {
|
||||
this.append_albums(data, $state)
|
||||
spotifyApi.getArtistAlbums(this.artist.id, { limit: PAGE_SIZE, offset: this.offset, include_groups: 'album,single' }).then(data => {
|
||||
this.append_albums(data)
|
||||
loaded(data.items.length, PAGE_SIZE)
|
||||
})
|
||||
},
|
||||
|
||||
append_albums: function (data, $state) {
|
||||
append_albums: function (data) {
|
||||
this.albums = this.albums.concat(data.items)
|
||||
this.total = data.total
|
||||
this.offset += data.limit
|
||||
|
||||
if ($state) {
|
||||
$state.loaded()
|
||||
if (this.offset >= this.total) {
|
||||
$state.complete()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
play: function () {
|
||||
@@ -141,6 +135,19 @@ export default {
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<!-- New Releases -->
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">New Releases</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-album v-for="album in new_releases"
|
||||
:key="album.id"
|
||||
:album="album"
|
||||
@click="open_album(album)">
|
||||
<template slot="artwork" v-if="is_visible_artwork">
|
||||
<template v-slot:artwork v-if="is_visible_artwork">
|
||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="artwork_url(album)"
|
||||
@@ -22,7 +22,7 @@
|
||||
:maxheight="64" />
|
||||
</p>
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_album_dialog(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -30,7 +30,7 @@
|
||||
</spotify-list-item-album>
|
||||
<spotify-modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<router-link to="/music/spotify/new-releases" class="button is-light is-small is-rounded">
|
||||
@@ -43,12 +43,12 @@
|
||||
|
||||
<!-- Featured Playlists -->
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Featured Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-playlist v-for="playlist in featured_playlists" :key="playlist.id" :playlist="playlist">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_playlist_dialog(playlist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -56,7 +56,7 @@
|
||||
</spotify-list-item-playlist>
|
||||
<spotify-modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav class="level">
|
||||
<p class="level-item">
|
||||
<router-link to="/music/spotify/featured-playlists" class="button is-light is-small is-rounded">
|
||||
@@ -70,19 +70,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum.vue'
|
||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist.vue'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum.vue'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import store from '@/store'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
||||
const browseData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
if (store.state.spotify_new_releases.length > 0 && store.state.spotify_featured_playlists.length > 0) {
|
||||
return Promise.resolve()
|
||||
@@ -106,7 +105,6 @@ const browseData = {
|
||||
|
||||
export default {
|
||||
name: 'SpotifyPageBrowse',
|
||||
mixins: [LoadDataBeforeEnterMixin(browseData)],
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum, SpotifyListItemPlaylist, SpotifyModalDialogAlbum, SpotifyModalDialogPlaylist, CoverArtwork },
|
||||
|
||||
data () {
|
||||
@@ -155,6 +153,19 @@ export default {
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Featured Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-playlist v-for="playlist in featured_playlists" :key="playlist.id" :playlist="playlist">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_playlist_dialog(playlist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -21,16 +21,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist.vue'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist.vue'
|
||||
import store from '@/store'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
||||
const browseData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
if (store.state.spotify_featured_playlists.length > 0) {
|
||||
return Promise.resolve()
|
||||
@@ -50,7 +49,6 @@ const browseData = {
|
||||
|
||||
export default {
|
||||
name: 'SpotifyPageBrowseFeaturedPlaylists',
|
||||
mixins: [LoadDataBeforeEnterMixin(browseData)],
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemPlaylist, SpotifyModalDialogPlaylist },
|
||||
|
||||
data () {
|
||||
@@ -71,6 +69,19 @@ export default {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fd-page-with-tabs">
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">New Releases</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-album v-for="album in new_releases"
|
||||
:key="album.id"
|
||||
:album="album"
|
||||
@click="open_album(album)">
|
||||
<template slot="artwork" v-if="is_visible_artwork">
|
||||
<template v-slot:artwork v-if="is_visible_artwork">
|
||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="artwork_url(album)"
|
||||
@@ -21,7 +21,7 @@
|
||||
:maxheight="64" />
|
||||
</p>
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_album_dialog(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
@@ -34,17 +34,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import TabsMusic from '@/components/TabsMusic.vue'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum.vue'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import store from '@/store'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
||||
const browseData = {
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
if (store.state.spotify_new_releases.length > 0) {
|
||||
return Promise.resolve()
|
||||
@@ -64,7 +63,6 @@ const browseData = {
|
||||
|
||||
export default {
|
||||
name: 'SpotifyPageBrowseNewReleases',
|
||||
mixins: [LoadDataBeforeEnterMixin(browseData)],
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum, SpotifyModalDialogAlbum, CoverArtwork },
|
||||
|
||||
data () {
|
||||
@@ -101,6 +99,19 @@ export default {
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<div class="title is-4">{{ playlist.name }}</div>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<template v-slot:heading-right>
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_playlist_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
@@ -13,16 +13,16 @@
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p class="heading has-text-centered-mobile">{{ playlist.tracks.total }} tracks</p>
|
||||
<spotify-list-item-track v-for="(item, index) in tracks" :key="item.track.id" :track="item.track" :album="item.track.album" :position="index" :context_uri="playlist.uri">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_track_dialog(item.track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-track>
|
||||
<infinite-loading v-if="offset < total" @infinite="load_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<VueEternalLoading v-if="offset < total" :load="load_next"><template #no-more>.</template></VueEternalLoading>
|
||||
<spotify-modal-dialog-track :show="show_track_details_modal" :track="selected_track" :album="selected_track.album" @close="show_track_details_modal = false" />
|
||||
<spotify-modal-dialog-playlist :show="show_playlist_details_modal" :playlist="playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
@@ -30,23 +30,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack.vue'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack.vue'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist.vue'
|
||||
import store from '@/store'
|
||||
import webapi from '@/webapi'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
import { VueEternalLoading } from '@ts-pro/vue-eternal-loading'
|
||||
|
||||
const playlistData = {
|
||||
const PAGE_SIZE = 50
|
||||
|
||||
const dataObject = {
|
||||
load: function (to) {
|
||||
const spotifyApi = new SpotifyWebApi()
|
||||
spotifyApi.setAccessToken(store.state.spotify.webapi_token)
|
||||
return Promise.all([
|
||||
spotifyApi.getPlaylist(to.params.playlist_id),
|
||||
spotifyApi.getPlaylistTracks(to.params.playlist_id, { limit: 50, offset: 0 })
|
||||
spotifyApi.getPlaylistTracks(to.params.playlist_id, { limit: PAGE_SIZE, offset: 0 })
|
||||
])
|
||||
},
|
||||
|
||||
@@ -61,8 +62,7 @@ const playlistData = {
|
||||
|
||||
export default {
|
||||
name: 'SpotifyPagePlaylist',
|
||||
mixins: [LoadDataBeforeEnterMixin(playlistData)],
|
||||
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack, SpotifyModalDialogPlaylist, InfiniteLoading },
|
||||
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack, SpotifyModalDialogPlaylist, VueEternalLoading },
|
||||
|
||||
data () {
|
||||
return {
|
||||
@@ -79,25 +79,19 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
load_next: function ($state) {
|
||||
load_next: function ({ loaded }) {
|
||||
const spotifyApi = new SpotifyWebApi()
|
||||
spotifyApi.setAccessToken(this.$store.state.spotify.webapi_token)
|
||||
spotifyApi.getPlaylistTracks(this.playlist.id, { limit: 50, offset: this.offset }).then(data => {
|
||||
this.append_tracks(data, $state)
|
||||
spotifyApi.getPlaylistTracks(this.playlist.id, { limit: PAGE_SIZE, offset: this.offset }).then(data => {
|
||||
this.append_tracks(data)
|
||||
loaded(data.items.length, PAGE_SIZE)
|
||||
})
|
||||
},
|
||||
|
||||
append_tracks: function (data, $state) {
|
||||
append_tracks: function (data) {
|
||||
this.tracks = this.tracks.concat(data.items)
|
||||
this.total = data.total
|
||||
this.offset += data.limit
|
||||
|
||||
if ($state) {
|
||||
$state.loaded()
|
||||
if (this.offset >= this.total) {
|
||||
$state.complete()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
play: function () {
|
||||
@@ -109,6 +103,19 @@ export default {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -27,21 +27,21 @@
|
||||
|
||||
<!-- Tracks -->
|
||||
<content-with-heading v-if="show_tracks && tracks.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-track v-for="track in tracks.items" :key="track.id" :track="track" :album="track.album" :position="0" :context_uri="track.uri">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_track_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-track>
|
||||
<infinite-loading v-if="query.type === 'track'" @infinite="search_tracks_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<VueEternalLoading v-if="query.type === 'track'" :load="search_tracks_next"><template #no-more>.</template></VueEternalLoading>
|
||||
<spotify-modal-dialog-track :show="show_track_details_modal" :track="selected_track" :album="selected_track.album" @close="show_track_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_tracks_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_tracks">Show all {{ tracks.total.toLocaleString() }} tracks</a>
|
||||
@@ -50,28 +50,28 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_tracks && !tracks.total" class="mt-6">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No tracks found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Artists -->
|
||||
<content-with-heading v-if="show_artists && artists.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Artists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-artist v-for="artist in artists.items" :key="artist.id" :artist="artist">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_artist_dialog(artist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-artist>
|
||||
<infinite-loading v-if="query.type === 'artist'" @infinite="search_artists_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<VueEternalLoading v-if="query.type === 'artist'" :load="search_artists_next"><template #no-more>.</template></VueEternalLoading>
|
||||
<spotify-modal-dialog-artist :show="show_artist_details_modal" :artist="selected_artist" @close="show_artist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_artists_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_artists">Show all {{ artists.total.toLocaleString() }} artists</a>
|
||||
@@ -80,22 +80,22 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_artists && !artists.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No artists found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Albums -->
|
||||
<content-with-heading v-if="show_albums && albums.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-album v-for="album in albums.items"
|
||||
:key="album.id"
|
||||
:album="album"
|
||||
@click="open_album(album)">
|
||||
<template slot="artwork" v-if="is_visible_artwork">
|
||||
<template v-slot:artwork v-if="is_visible_artwork">
|
||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
||||
<cover-artwork
|
||||
:artwork_url="artwork_url(album)"
|
||||
@@ -105,16 +105,16 @@
|
||||
:maxheight="64" />
|
||||
</p>
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_album_dialog(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-album>
|
||||
<infinite-loading v-if="query.type === 'album'" @infinite="search_albums_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<VueEternalLoading v-if="query.type === 'album'" :load="search_albums_next"><template #no-more>.</template></VueEternalLoading>
|
||||
<spotify-modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_albums_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_albums">Show all {{ albums.total.toLocaleString() }} albums</a>
|
||||
@@ -123,28 +123,28 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_albums && !albums.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No albums found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Playlists -->
|
||||
<content-with-heading v-if="show_playlists && playlists.total">
|
||||
<template slot="heading-left">
|
||||
<template v-slot:heading-left>
|
||||
<p class="title is-4">Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<spotify-list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist">
|
||||
<template slot="actions">
|
||||
<template v-slot:actions>
|
||||
<a @click="open_playlist_dialog(playlist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-playlist>
|
||||
<infinite-loading v-if="query.type === 'playlist'" @infinite="search_playlists_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<VueEternalLoading v-if="query.type === 'playlist'" :load="search_playlists_next"><template #no-more>.</template></VueEternalLoading>
|
||||
<spotify-modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<template v-slot:footer>
|
||||
<nav v-if="show_all_playlists_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_playlists">Show all {{ playlists.total.toLocaleString() }} playlists</a>
|
||||
@@ -153,7 +153,7 @@
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_playlists && !playlists.total">
|
||||
<template slot="content">
|
||||
<template v-slot:content>
|
||||
<p><i>No playlists found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
@@ -161,26 +161,28 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ContentText from '@/templates/ContentText'
|
||||
import TabsSearch from '@/components/TabsSearch'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||
import SpotifyListItemArtist from '@/components/SpotifyListItemArtist'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||
import SpotifyModalDialogArtist from '@/components/SpotifyModalDialogArtist'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist'
|
||||
import CoverArtwork from '@/components/CoverArtwork'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
|
||||
import ContentText from '@/templates/ContentText.vue'
|
||||
import TabsSearch from '@/components/TabsSearch.vue'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack.vue'
|
||||
import SpotifyListItemArtist from '@/components/SpotifyListItemArtist.vue'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum.vue'
|
||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist.vue'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack.vue'
|
||||
import SpotifyModalDialogArtist from '@/components/SpotifyModalDialogArtist.vue'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum.vue'
|
||||
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist.vue'
|
||||
import CoverArtwork from '@/components/CoverArtwork.vue'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
import { VueEternalLoading } from '@ts-pro/vue-eternal-loading'
|
||||
|
||||
const PAGE_SIZE = 50
|
||||
|
||||
export default {
|
||||
name: 'SpotifyPageSearch',
|
||||
components: { ContentWithHeading, ContentText, TabsSearch, SpotifyListItemTrack, SpotifyListItemArtist, SpotifyListItemAlbum, SpotifyListItemPlaylist, SpotifyModalDialogTrack, SpotifyModalDialogArtist, SpotifyModalDialogAlbum, SpotifyModalDialogPlaylist, InfiniteLoading, CoverArtwork },
|
||||
components: { ContentWithHeading, ContentText, TabsSearch, SpotifyListItemTrack, SpotifyListItemArtist, SpotifyListItemAlbum, SpotifyListItemPlaylist, SpotifyModalDialogTrack, SpotifyModalDialogArtist, SpotifyModalDialogAlbum, SpotifyModalDialogPlaylist, VueEternalLoading, CoverArtwork },
|
||||
|
||||
data () {
|
||||
return {
|
||||
@@ -266,7 +268,7 @@ export default {
|
||||
}
|
||||
|
||||
this.search_query = this.query.query
|
||||
this.search_param.limit = this.query.limit ? this.query.limit : 50
|
||||
this.search_param.limit = this.query.limit ? this.query.limit : PAGE_SIZE
|
||||
this.search_param.offset = this.query.offset ? this.query.offset : 0
|
||||
|
||||
this.$store.commit(types.ADD_RECENT_SEARCH, this.query.query)
|
||||
@@ -295,55 +297,43 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
search_tracks_next: function ($state) {
|
||||
search_tracks_next: function ({ loaded }) {
|
||||
this.spotify_search().then(data => {
|
||||
this.tracks.items = this.tracks.items.concat(data.tracks.items)
|
||||
this.tracks.total = data.tracks.total
|
||||
this.search_param.offset += data.tracks.limit
|
||||
|
||||
$state.loaded()
|
||||
if (this.search_param.offset >= this.tracks.total) {
|
||||
$state.complete()
|
||||
}
|
||||
|
||||
loaded(data.tracks.items.length, PAGE_SIZE)
|
||||
})
|
||||
},
|
||||
|
||||
search_artists_next: function ($state) {
|
||||
search_artists_next: function ({ loaded }) {
|
||||
this.spotify_search().then(data => {
|
||||
this.artists.items = this.artists.items.concat(data.artists.items)
|
||||
this.artists.total = data.artists.total
|
||||
this.search_param.offset += data.artists.limit
|
||||
|
||||
$state.loaded()
|
||||
if (this.search_param.offset >= this.artists.total) {
|
||||
$state.complete()
|
||||
}
|
||||
|
||||
loaded(data.artists.items.length, PAGE_SIZE)
|
||||
})
|
||||
},
|
||||
|
||||
search_albums_next: function ($state) {
|
||||
search_albums_next: function ({ loaded }) {
|
||||
this.spotify_search().then(data => {
|
||||
this.albums.items = this.albums.items.concat(data.albums.items)
|
||||
this.albums.total = data.albums.total
|
||||
this.search_param.offset += data.albums.limit
|
||||
|
||||
$state.loaded()
|
||||
if (this.search_param.offset >= this.albums.total) {
|
||||
$state.complete()
|
||||
}
|
||||
|
||||
loaded(data.albums.items.length, PAGE_SIZE)
|
||||
})
|
||||
},
|
||||
|
||||
search_playlists_next: function ($state) {
|
||||
search_playlists_next: function ({ loaded }) {
|
||||
this.spotify_search().then(data => {
|
||||
this.playlists.items = this.playlists.items.concat(data.playlists.items)
|
||||
this.playlists.total = data.playlists.total
|
||||
this.search_param.offset += data.playlists.limit
|
||||
|
||||
$state.loaded()
|
||||
if (this.search_param.offset >= this.playlists.total) {
|
||||
$state.complete()
|
||||
}
|
||||
|
||||
loaded(data.playlists.items.length, PAGE_SIZE)
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
export const LoadDataBeforeEnterMixin = function (dataObject) {
|
||||
return {
|
||||
beforeRouteEnter (to, from, next) {
|
||||
dataObject.load(to).then((response) => {
|
||||
next(vm => dataObject.set(vm, response))
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
const vm = this
|
||||
dataObject.load(to).then((response) => {
|
||||
dataObject.set(vm, response)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user