Merge pull request #634 from chme/webinterface
Update player webinterface (v0.3.0)
This commit is contained in:
commit
4961dd9141
|
@ -1,2 +1,2 @@
|
|||
.fd-notifications{position:fixed;bottom:60px;z-index:20000;width:100%}.fd-notifications .notification{margin-bottom:10px;margin-left:24px;margin-right:24px;-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19)}.slider{min-width:250px;width:100%}.range-slider-fill{background-color:#363636}a.navbar-item{outline:0;line-height:1.5;padding:.5rem 1rem}.fd-expanded{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.fd-margin-left-auto{margin-left:auto}.fd-has-action{cursor:pointer}.fd-is-movable{cursor:move}.fd-has-margin-top{margin-top:24px}.fd-has-margin-bottom{margin-bottom:24px}.fd-has-padding-left-right{padding-left:24px;padding-right:24px}.fd-is-text-clipped{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fd-is-fullheight{height:calc(100vh - 6.5rem)}.fd-is-fullheight-body{-ms-flex-negative:1;flex-shrink:1;overflow:hidden;height:100%}.fd-image-fullheight{height:100%;width:auto}.fd-tabs-section{padding-bottom:0}.fd-progress-bar{top:52px!important}.fd-has-shadow{-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19)}.sortable-chosen .media-right{visibility:hidden}.sortable-ghost h1,.sortable-ghost h2{color:#ff3860!important}.media:first-of-type{padding-top:17px;margin-top:16px}.fade-enter-active,.fade-leave-active{-webkit-transition:opacity .4s;transition:opacity .4s}.fade-enter,.fade-leave-to{opacity:0}.seek-slider{min-width:250px;max-width:500px;width:100%!important}.seek-slider .range-slider-fill{background-color:#00d1b2;-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19)}.seek-slider .range-slider-knob{width:10px;height:10px;background-color:#00d1b2;border-color:#00d1b2}.title:not(.is-spaced)+.subtitle,.title:not(.is-spaced)+.subtitle+.subtitle{margin-top:-1.3rem!important}.fd-modal-card{overflow:visible}.fd-modal-card .card-content{max-height:calc(100vh - 200px);overflow:auto}
|
||||
.fd-notifications{position:fixed;bottom:60px;z-index:20000;width:100%}.fd-notifications .notification{margin-bottom:10px;margin-left:24px;margin-right:24px;-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19)}.slider{min-width:250px;width:100%}.range-slider-fill{background-color:#363636}a.navbar-item{outline:0;line-height:1.5;padding:.5rem 1rem}.fd-expanded{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.fd-margin-left-auto{margin-left:auto}.fd-has-action{cursor:pointer}.fd-is-movable{cursor:move}.fd-has-margin-top{margin-top:24px}.fd-has-margin-bottom{margin-bottom:24px}.fd-remove-padding-bottom{padding-bottom:0}.fd-has-padding-left-right{padding-left:24px;padding-right:24px}.fd-is-square .button{height:27px;width:27px}.fd-is-text-clipped{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fd-is-fullheight{height:calc(100vh - 6.5rem)}.fd-is-fullheight-body{-ms-flex-negative:1;flex-shrink:1;overflow:hidden;height:100%}.fd-image-fullheight{height:100%;width:auto}.fd-tabs-section{padding-bottom:3px;padding-top:3px;background:#fff;top:3.25rem;z-index:20;position:fixed;width:100%}section.fd-tabs-section+section.fd-content{margin-top:24px}.fd-progress-bar{top:52px!important}.fd-has-shadow{-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19)}.sortable-chosen .media-right{visibility:hidden}.sortable-ghost h1,.sortable-ghost h2{color:#ff3860!important}.media:first-of-type{padding-top:17px;margin-top:16px}.fade-enter-active,.fade-leave-active{-webkit-transition:opacity .4s;transition:opacity .4s}.fade-enter,.fade-leave-to{opacity:0}.seek-slider{min-width:250px;max-width:500px;width:100%!important}.seek-slider .range-slider-fill{background-color:#00d1b2;-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19)}.seek-slider .range-slider-knob{width:10px;height:10px;background-color:#00d1b2;border-color:#00d1b2}.title:not(.is-spaced)+.subtitle,.title:not(.is-spaced)+.subtitle+.subtitle{margin-top:-1.3rem!important}.fd-modal-card{overflow:visible}.fd-modal-card .card-content{max-height:calc(100vh - 200px);overflow:auto}.fd-modal-card .card{margin-left:16px;margin-right:16px}
|
||||
/*# sourceMappingURL=app.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "forked-daapd-web",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "forked-daapd web interface",
|
||||
"author": "chme <christian.meffert@googlemail.com>",
|
||||
"license": "GPL-2.0",
|
||||
|
@ -15,24 +15,24 @@
|
|||
"axios": "^0.18.0",
|
||||
"bulma": "^0.7.1",
|
||||
"mdi": "^2.1.99",
|
||||
"moment": "^2.22.1",
|
||||
"moment": "^2.23.0",
|
||||
"moment-duration-format": "^2.2.2",
|
||||
"npm": "^6.4.1",
|
||||
"npm": "^6.5.0",
|
||||
"reconnectingwebsocket": "^1.0.0",
|
||||
"spotify-web-api-js": "^1.1.1",
|
||||
"vue": "^2.5.17",
|
||||
"vue-infinite-loading": "^2.4.0",
|
||||
"vue": "^2.5.21",
|
||||
"vue-infinite-loading": "^2.4.3",
|
||||
"vue-progressbar": "^0.7.4",
|
||||
"vue-range-slider": "^0.6.0",
|
||||
"vue-router": "^3.0.1",
|
||||
"vuedraggable": "^2.16.0",
|
||||
"vue-router": "^3.0.2",
|
||||
"vuedraggable": "^2.17.0",
|
||||
"vuex": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.1.1",
|
||||
"@vue/cli-plugin-eslint": "^3.1.4",
|
||||
"@vue/cli-service": "^3.1.2",
|
||||
"@vue/cli-plugin-babel": "^3.2.0",
|
||||
"@vue/cli-plugin-eslint": "^3.2.1",
|
||||
"@vue/cli-service": "^3.2.0",
|
||||
"@vue/eslint-config-standard": "^3.0.5",
|
||||
"vue-template-compiler": "^2.5.17"
|
||||
"vue-template-compiler": "^2.5.21"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,13 +165,11 @@ export default {
|
|||
this.$store.commit(types.UPDATE_SPOTIFY, data)
|
||||
|
||||
if (this.token_timer_id > 0) {
|
||||
console.log('clear old timer: ' + this.token_timer_id)
|
||||
window.clearTimeout(this.token_timer_id)
|
||||
this.token_timer_id = 0
|
||||
}
|
||||
if (data.webapi_token_expires_in > 0 && data.webapi_token) {
|
||||
this.token_timer_id = window.setTimeout(this.update_spotify, 1000 * data.webapi_token_expires_in)
|
||||
console.log('new timer: ' + this.token_timer_id + ', expires in ' + data.webapi_token_expires_in + ' seconds')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<section>
|
||||
<nav class="buttons is-centered fd-is-square" style="margin-bottom: 48px;" v-if="filtered_index.length > 1">
|
||||
<a v-for="char in filtered_index" :key="char" class="button is-small" @click="nav(char)">{{ char }}</a>
|
||||
</nav>
|
||||
<nav class="buttons is-centered" style="margin-bottom: 6px;" v-if="filtered_index.length > 1">
|
||||
<a class="button is-small is-white" @click="scroll_to_top"><span class="icon is-small"><i class="mdi mdi-chevron-up"></i></span></a>
|
||||
</nav>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'IndexButtonList',
|
||||
|
||||
props: ['index'],
|
||||
|
||||
computed: {
|
||||
filtered_index () {
|
||||
const specialChars = '!"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~'
|
||||
return this.index.filter(c => !specialChars.includes(c))
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
nav: function (id) {
|
||||
this.$router.push({ path: this.$router.currentRoute.path + '#index_' + id })
|
||||
},
|
||||
|
||||
scroll_to_top: function () {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,105 +1,19 @@
|
|||
<template>
|
||||
<div class="media">
|
||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_album">
|
||||
<h1 class="title is-6">{{ album.name }}</h1>
|
||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artist }}</b></h2>
|
||||
<template functional>
|
||||
<div class="media" :id="'index_' + props.album.name_sort.charAt(0).toUpperCase()">
|
||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||
<h1 class="title is-6">{{ props.album.name }}</h1>
|
||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.album.artist }}</b></h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details_modal = true">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<modal-dialog :show="show_details_modal" @close="show_details_modal = false">
|
||||
<template slot="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_album">{{ album.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p v-if="album.artist && media_kind !== 'audiobook'">
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artist }}</a>
|
||||
</p>
|
||||
<p v-if="album.artist && media_kind === 'audiobook'">
|
||||
<span class="heading">Album artist</span>
|
||||
<span class="title is-6">{{ album.artist }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ album.track_count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ListItemAlbum',
|
||||
components: { ModalDialog },
|
||||
|
||||
props: ['album', 'media_kind'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.album.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add(this.album.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add_next(this.album.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.show_details_modal = false
|
||||
if (this.media_kind === 'podcast') {
|
||||
this.$router.push({ path: '/podcasts/' + this.album.id })
|
||||
} else if (this.media_kind === 'audiobook') {
|
||||
this.$router.push({ path: '/audiobooks/' + this.album.id })
|
||||
} else {
|
||||
this.$router.push({ path: '/music/albums/' + this.album.id })
|
||||
}
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/artists/' + this.album.artist_id })
|
||||
}
|
||||
}
|
||||
props: ['album', 'media_kind']
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,89 +1,18 @@
|
|||
<template>
|
||||
<div class="media">
|
||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_artist">
|
||||
<h1 class="title is-6">{{ artist.name }}</h1>
|
||||
<template functional>
|
||||
<div class="media" :id="'index_' + props.artist.name_sort.charAt(0).toUpperCase()">
|
||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||
<h1 class="title is-6">{{ props.artist.name }}</h1>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details_modal = true">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<modal-dialog :show="show_details_modal" @close="show_details_modal = false">
|
||||
<template slot="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_artist">{{ artist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Albums</span>
|
||||
<span class="title is-6">{{ artist.album_count }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ artist.track_count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PartArtist',
|
||||
components: { ModalDialog },
|
||||
|
||||
props: ['artist'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.artist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add(this.artist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Artist tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add_next(this.artist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/artists/' + this.artist.id })
|
||||
}
|
||||
}
|
||||
name: 'ListItemArtist',
|
||||
props: ['artist']
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,97 +1,18 @@
|
|||
<template>
|
||||
<div class="media">
|
||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_genre">
|
||||
<h1 class="title is-6">{{ genre.name }}</h1>
|
||||
<template functional>
|
||||
<div class="media" :id="'index_' + props.genre.name.charAt(0).toUpperCase()">
|
||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||
<h1 class="title is-6">{{ props.genre.name }}</h1>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details_modal = true">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<modal-dialog :show="show_details_modal" @close="show_details_modal = false">
|
||||
<template slot="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_genre">{{ genre.name }}</a>
|
||||
</p>
|
||||
<!--
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Albums</span>
|
||||
<span class="title is-6">{{ genre.album_count }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ genre.track_count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-12px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-12px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-12px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PartGenre',
|
||||
components: { ModalDialog },
|
||||
|
||||
props: [ 'genre' ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.library_genre(this.genre.name).then(({ data }) =>
|
||||
webapi.player_play_uri(data.albums.items.map(a => a.uri).join(','), false)
|
||||
)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.library_genre(this.genre.name).then(({ data }) =>
|
||||
webapi.queue_add(data.albums.items.map(a => a.uri).join(',')).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Genre albums appended to queue', type: 'info', timeout: 1500 })
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.library_genre(this.genre.name).then(({ data }) =>
|
||||
webapi.queue_add_next(data.albums.items.map(a => a.uri).join(',')).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Genre albums playing next', type: 'info', timeout: 1500 })
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
open_genre: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ name: 'Genre', params: { genre: this.genre.name } })
|
||||
}
|
||||
}
|
||||
name: 'ListItemGenre',
|
||||
props: [ 'genre' ]
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,85 +1,18 @@
|
|||
<template>
|
||||
<template functional>
|
||||
<div class="media">
|
||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_playlist">
|
||||
<h1 class="title is-6">{{ playlist.name }}</h1>
|
||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||
<h1 class="title is-6">{{ props.playlist.name }}</h1>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details_modal = true">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<modal-dialog :show="show_details_modal" @close="show_details_modal = false">
|
||||
<template slot="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_playlist">{{ playlist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ playlist.path }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PartPlaylist',
|
||||
components: { ModalDialog },
|
||||
|
||||
props: ['playlist'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.playlist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add(this.playlist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Playlist appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add_next(this.playlist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
open_playlist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/playlists/' + this.playlist.id })
|
||||
}
|
||||
}
|
||||
name: 'ListItemPlaylist',
|
||||
props: ['playlist']
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="media" v-if="is_next || !show_only_next_items">
|
||||
<!---->
|
||||
<div class="media-left" v-if="edit_mode">
|
||||
<span class="icon has-text-grey fd-is-movable handle"><i class="mdi mdi-drag-horizontal mdi-18px"></i></span>
|
||||
</div>
|
||||
|
@ -11,84 +10,18 @@
|
|||
<h2 class="subtitle is-7" :class="{ 'has-text-primary': item.id === state.item_id, 'has-text-grey-light': !is_next, 'has-text-grey': is_next && item.id !== state.item_id }">{{ item.album }}</h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a v-on:click="remove" 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>
|
||||
<a @click="show_details_modal = true" v-if="!edit_mode">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<modal-dialog v-if="!edit_mode" :show="show_details_modal" @close="show_details_modal = false">
|
||||
<template slot="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
{{ item.title }}
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
{{ item.artist }}
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album</span>
|
||||
<span class="title is-6">{{ item.album }}</span>
|
||||
</p>
|
||||
<p v-if="item.album_artist">
|
||||
<span class="heading">Album artist</span>
|
||||
<span class="title is-6">{{ item.album_artist }}</span>
|
||||
</p>
|
||||
<p v-if="item.year > 0">
|
||||
<span class="heading">Year</span>
|
||||
<span class="title is-6">{{ item.year }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Genre</span>
|
||||
<span class="title is-6">{{ item.genre }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Track / Disc</span>
|
||||
<span class="title is-6">{{ item.track_number }} / {{ item.disc_number }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Length</span>
|
||||
<span class="title is-6">{{ item.length_ms | duration }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ item.path }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="remove">
|
||||
<span class="icon"><i class="mdi mdi-delete mdi-18px"></i></span> <span>Remove</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PartQueueItem',
|
||||
components: { ModalDialog },
|
||||
|
||||
name: 'ListItemQueueItem',
|
||||
props: ['item', 'position', 'current_position', 'show_only_next_items', 'edit_mode'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
state () {
|
||||
return this.$store.state.player
|
||||
|
@ -100,13 +33,7 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
remove: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_remove(this.item.id)
|
||||
},
|
||||
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play({ 'item_id': this.item.id })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,142 +1,20 @@
|
|||
<template>
|
||||
<div class="media">
|
||||
<div class="media-content fd-has-action is-clipped" v-on:click="play">
|
||||
<h1 class="title is-6">{{ track.title }}</h1>
|
||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ track.artist }}</b></h2>
|
||||
<h2 class="subtitle is-7 has-text-grey">{{ track.album }}</h2>
|
||||
<template functional>
|
||||
<div class="media" :id="'index_' + props.track.title_sort.charAt(0).toUpperCase()">
|
||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||
<h1 class="title is-6">{{ props.track.title }}</h1>
|
||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.track.artist }}</b></h2>
|
||||
<h2 class="subtitle is-7 has-text-grey">{{ props.track.album }}</h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details_modal = true">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<modal-dialog :show="show_details_modal" @close="show_details_modal = false">
|
||||
<template slot="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
{{ track.title }}
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
{{ track.artist }}
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album</span>
|
||||
<a class="title is-6 has-text-link" @click="open_album">{{ track.album }}</a>
|
||||
</p>
|
||||
<p v-if="track.album_artist && track.media_kind !== 'audiobook'">
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ track.album_artist }}</a>
|
||||
</p>
|
||||
<p v-if="track.date_released">
|
||||
<span class="heading">Release date</span>
|
||||
<span class="title is-6">{{ track.date_released | time('L')}}</span>
|
||||
</p>
|
||||
<p v-else-if="track.year > 0">
|
||||
<span class="heading">Year</span>
|
||||
<span class="title is-6">{{ track.year }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Genre</span>
|
||||
<span class="title is-6">{{ track.genre }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Track / Disc</span>
|
||||
<span class="title is-6">{{ track.track_number }} / {{ track.disc_number }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Length</span>
|
||||
<span class="title is-6">{{ track.length_ms | duration }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ track.path }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Type</span>
|
||||
<span class="title is-6">{{ track.media_kind }} - {{ track.data_kind }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Added at</span>
|
||||
<span class="title is-6">{{ track.time_added | time('L LT')}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play_track">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</modal-dialog>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialog from '@/components/ModalDialog'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'PartTrack',
|
||||
components: { ModalDialog },
|
||||
|
||||
props: ['track', 'position', 'context_uri'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.context_uri, false, this.position)
|
||||
},
|
||||
|
||||
play_track: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.track.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add(this.track.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Track appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.queue_add_next(this.track.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.show_details_modal = false
|
||||
if (this.track.media_kind === 'podcast') {
|
||||
this.$router.push({ path: '/podcasts/' + this.track.album_id })
|
||||
} else if (this.track.media_kind === 'audiobook') {
|
||||
this.$router.push({ path: '/audiobooks/' + this.track.album_id })
|
||||
} else {
|
||||
this.$router.push({ path: '/music/albums/' + this.track.album_id })
|
||||
}
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/artists/' + this.track.album_artist_id })
|
||||
}
|
||||
}
|
||||
name: 'ListItemTrack',
|
||||
props: ['track']
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<figure class="image is-square fd-has-margin-bottom" v-show="artwork_visible">
|
||||
<img :src="artwork_url" @load="artwork_loaded" @error="artwork_error" class="fd-has-shadow">
|
||||
</figure>
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_album">{{ album.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p v-if="album.artist && media_kind !== 'audiobook'">
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artist }}</a>
|
||||
</p>
|
||||
<p v-if="album.artist && media_kind === 'audiobook'">
|
||||
<span class="heading">Album artist</span>
|
||||
<span class="title is-6">{{ album.artist }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ album.track_count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogAlbum',
|
||||
props: [ 'show', 'album', 'media_kind' ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
artwork_visible: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
artwork_url: function () {
|
||||
if (this.album.artwork_url && this.album.artwork_url.startsWith('/')) {
|
||||
return this.album.artwork_url + '?maxwidth=600&maxheight=600'
|
||||
}
|
||||
return this.album.artwork_url
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.album.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.album.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.album.uri)
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
if (this.media_kind === 'podcast') {
|
||||
this.$router.push({ path: '/podcasts/' + this.album.id })
|
||||
} else if (this.media_kind === 'audiobook') {
|
||||
this.$router.push({ path: '/audiobooks/' + this.album.id })
|
||||
} else {
|
||||
this.$router.push({ path: '/music/albums/' + this.album.id })
|
||||
}
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.$router.push({ path: '/music/artists/' + this.album.artist_id })
|
||||
},
|
||||
|
||||
artwork_loaded: function () {
|
||||
this.artwork_visible = true
|
||||
},
|
||||
|
||||
artwork_error: function () {
|
||||
this.artwork_visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_artist">{{ artist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Albums</span>
|
||||
<span class="title is-6">{{ artist.album_count }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ artist.track_count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogArtist',
|
||||
props: [ 'show', 'artist' ],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.artist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.artist.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.artist.uri)
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.$emit('close')
|
||||
this.$router.push({ path: '/music/artists/' + this.artist.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_genre">{{ genre.name }}</a>
|
||||
</p>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogGenre',
|
||||
props: [ 'show', 'genre' ],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.library_genre(this.genre.name).then(({ data }) =>
|
||||
webapi.player_play_uri(data.albums.items.map(a => a.uri).join(','), false)
|
||||
)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.library_genre(this.genre.name).then(({ data }) =>
|
||||
webapi.queue_add(data.albums.items.map(a => a.uri).join(','))
|
||||
)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.library_genre(this.genre.name).then(({ data }) =>
|
||||
webapi.queue_add_next(data.albums.items.map(a => a.uri).join(','))
|
||||
)
|
||||
},
|
||||
|
||||
open_genre: function () {
|
||||
this.$emit('close')
|
||||
this.$router.push({ name: 'Genre', params: { genre: this.genre.name } })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_playlist">{{ playlist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ playlist.path }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogPlaylist',
|
||||
props: [ 'show', 'playlist' ],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.playlist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.playlist.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.playlist.uri)
|
||||
},
|
||||
|
||||
open_playlist: function () {
|
||||
this.$emit('close')
|
||||
this.$router.push({ path: '/playlists/' + this.playlist.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
{{ item.title }}
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
{{ item.artist }}
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album</span>
|
||||
<span class="title is-6">{{ item.album }}</span>
|
||||
</p>
|
||||
<p v-if="item.album_artist">
|
||||
<span class="heading">Album artist</span>
|
||||
<span class="title is-6">{{ item.album_artist }}</span>
|
||||
</p>
|
||||
<p v-if="item.composer">
|
||||
<span class="heading">Composer</span>
|
||||
<span class="title is-6">{{ item.composer }}</span>
|
||||
</p>
|
||||
<p v-if="item.year > 0">
|
||||
<span class="heading">Year</span>
|
||||
<span class="title is-6">{{ item.year }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Genre</span>
|
||||
<span class="title is-6">{{ item.genre }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Track / Disc</span>
|
||||
<span class="title is-6">{{ item.track_number }} / {{ item.disc_number }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Length</span>
|
||||
<span class="title is-6">{{ item.length_ms | duration }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ item.path }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="remove">
|
||||
<span class="icon"><i class="mdi mdi-delete"></i></span> <span class="is-size-7">Remove</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogQueueItem',
|
||||
props: [ 'show', 'item' ],
|
||||
|
||||
methods: {
|
||||
remove: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_remove(this.item.id)
|
||||
},
|
||||
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play({ 'item_id': this.item.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
{{ track.title }}
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
{{ track.artist }}
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album</span>
|
||||
<a class="title is-6 has-text-link" @click="open_album">{{ track.album }}</a>
|
||||
</p>
|
||||
<p v-if="track.album_artist && track.media_kind !== 'audiobook'">
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ track.album_artist }}</a>
|
||||
</p>
|
||||
<p v-if="track.composer">
|
||||
<span class="heading">Composer</span>
|
||||
<span class="title is-6">{{ track.composer }}</span>
|
||||
</p>
|
||||
<p v-if="track.date_released">
|
||||
<span class="heading">Release date</span>
|
||||
<span class="title is-6">{{ track.date_released | time('L')}}</span>
|
||||
</p>
|
||||
<p v-else-if="track.year > 0">
|
||||
<span class="heading">Year</span>
|
||||
<span class="title is-6">{{ track.year }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Genre</span>
|
||||
<span class="title is-6">{{ track.genre }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Track / Disc</span>
|
||||
<span class="title is-6">{{ track.track_number }} / {{ track.disc_number }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Length</span>
|
||||
<span class="title is-6">{{ track.length_ms | duration }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ track.path }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Type</span>
|
||||
<span class="title is-6">{{ track.media_kind }} - {{ track.data_kind }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Added at</span>
|
||||
<span class="title is-6">{{ track.time_added | time('L LT') }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Rating</span>
|
||||
<span class="title is-6">{{ Math.floor(track.rating / 10) }} / 10</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play_track">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogTrack',
|
||||
|
||||
props: ['show', 'track'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play_track: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.track.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.track.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.track.uri)
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.$emit('close')
|
||||
if (this.track.media_kind === 'podcast') {
|
||||
this.$router.push({ path: '/podcasts/' + this.track.album_id })
|
||||
} else if (this.track.media_kind === 'audiobook') {
|
||||
this.$router.push({ path: '/audiobooks/' + this.track.album_id })
|
||||
} else {
|
||||
this.$router.push({ path: '/music/albums/' + this.track.album_id })
|
||||
}
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.$emit('close')
|
||||
this.$router.push({ path: '/music/artists/' + this.track.album_artist_id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -5,103 +5,20 @@
|
|||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artists[0].name }}</b></h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show_details_modal">
|
||||
<div class="modal-background" @click="hide_details"></div>
|
||||
<div class="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_album">{{ album.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artists[0].name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Release date</span>
|
||||
<span class="title is-6">{{ album.release_date }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Type</span>
|
||||
<span class="title is-6">{{ album.album_type }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="hide_details"></button>
|
||||
</div>
|
||||
</transition>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyListItemAlbum',
|
||||
|
||||
props: ['album'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.album.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
webapi.queue_add(this.album.uri).then(
|
||||
// this.$store.commit(types.ADD_NOTIFICATION, { text: 'Album tracks appended to queue', timeout: 0 })
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 3000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
webapi.queue_add_next(this.album.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Album tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
show_details: function () {
|
||||
this.show_details_modal = true
|
||||
},
|
||||
|
||||
hide_details: function () {
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.$router.push({ path: '/music/spotify/albums/' + this.album.id })
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,93 +4,18 @@
|
|||
<h1 class="title is-6">{{ artist.name }}</h1>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show_details_modal">
|
||||
<div class="modal-background" @click="hide_details"></div>
|
||||
<div class="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_artist">{{ artist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Popularity / Followers</span>
|
||||
<span class="title is-6">{{ artist.popularity }} / {{ artist.followers.total }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Genres</span>
|
||||
<span class="title is-6">{{ artist.genres.join(', ') }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="hide_details"></button>
|
||||
</div>
|
||||
</transition>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyListItemArtist',
|
||||
|
||||
props: ['artist'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.artist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
webapi.queue_add(this.artist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Artist tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
webapi.queue_add_next(this.artist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Artist tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
show_details: function () {
|
||||
this.show_details_modal = true
|
||||
},
|
||||
|
||||
hide_details: function () {
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/spotify/artists/' + this.artist.id })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,97 +5,18 @@
|
|||
<h2 class="subtitle is-7">{{ playlist.owner.display_name }}</h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show_details_modal">
|
||||
<div class="modal-background" @click="hide_details"></div>
|
||||
<div class="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_playlist">{{ playlist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Owner</span>
|
||||
<span class="title is-6">{{ playlist.owner.display_name }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ playlist.tracks.total }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ playlist.uri }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="hide_details"></button>
|
||||
</div>
|
||||
</transition>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyListItemPlaylist',
|
||||
|
||||
props: ['playlist'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.playlist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
webapi.queue_add(this.playlist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Playlist appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
webapi.queue_add_next(this.playlist.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Playlist tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
show_details: function () {
|
||||
this.show_details_modal = true
|
||||
},
|
||||
|
||||
hide_details: function () {
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
open_playlist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/spotify/playlists/' + this.playlist.id })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,64 +5,7 @@
|
|||
<h2 class="subtitle is-7 has-text-grey"><b>{{ track.artists[0].name }}</b></h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<a @click="show_details">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show_details_modal">
|
||||
<div class="modal-background" @click="hide_details"></div>
|
||||
<div class="modal-content">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
{{ track.name }}
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
{{ track.artists[0].name }}
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album</span>
|
||||
<a class="title is-6 has-text-link" @click="open_album">{{ album.name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artists[0].name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Release date</span>
|
||||
<span class="title is-6">{{ album.release_date }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Track / Disc</span>
|
||||
<span class="title is-6">{{ track.track_number }} / {{ track.disc_number }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Length</span>
|
||||
<span class="title is-6">{{ track.duration_ms | duration }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ track.uri }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus mdi-18px"></i></span> <span>Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play mdi-18px"></i></span> <span>Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play mdi-18px"></i></span> <span>Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="hide_details"></button>
|
||||
</div>
|
||||
</transition>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -75,48 +18,9 @@ export default {
|
|||
|
||||
props: ['track', 'position', 'album', 'context_uri'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.context_uri, false, this.position)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
webapi.queue_add(this.track.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Track appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
webapi.queue_add_next(this.track.uri).then(() =>
|
||||
this.$store.dispatch('add_notification', { text: 'Track appended to queue', type: 'info', timeout: 2000 })
|
||||
)
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
show_details: function () {
|
||||
this.show_details_modal = true
|
||||
},
|
||||
|
||||
hide_details: function () {
|
||||
this.show_details_modal = false
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/spotify/albums/' + this.album.id })
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<figure class="image is-square fd-has-margin-bottom" v-show="artwork_visible">
|
||||
<img :src="artwork_url" @load="artwork_loaded" @error="artwork_error" class="fd-has-shadow">
|
||||
</figure>
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_album">{{ album.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artists[0].name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Release date</span>
|
||||
<span class="title is-6">{{ album.release_date }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Type</span>
|
||||
<span class="title is-6">{{ album.album_type }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyModalDialogAlbum',
|
||||
props: [ 'show', 'album' ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
artwork_visible: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
artwork_url: function () {
|
||||
if (this.album.images && this.album.images.length > 0) {
|
||||
return this.album.images[0].url
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.album.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.album.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.album.uri)
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.$router.push({ path: '/music/spotify/albums/' + this.album.id })
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
||||
},
|
||||
|
||||
artwork_loaded: function () {
|
||||
this.artwork_visible = true
|
||||
},
|
||||
|
||||
artwork_error: function () {
|
||||
this.artwork_visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_artist">{{ artist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Popularity / Followers</span>
|
||||
<span class="title is-6">{{ artist.popularity }} / {{ artist.followers.total }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Genres</span>
|
||||
<span class="title is-6">{{ artist.genres.join(', ') }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyModalDialogArtist',
|
||||
props: [ 'show', 'artist' ],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.artist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.artist.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.artist.uri)
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.$router.push({ path: '/music/spotify/artists/' + this.artist.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_playlist">{{ playlist.name }}</a>
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Owner</span>
|
||||
<span class="title is-6">{{ playlist.owner.display_name }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<span class="title is-6">{{ playlist.tracks.total }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ playlist.uri }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyModalDialogPlaylist',
|
||||
props: [ 'show', 'playlist' ],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.playlist.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.playlist.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.playlist.uri)
|
||||
},
|
||||
|
||||
open_playlist: function () {
|
||||
this.$router.push({ path: '/music/spotify/playlists/' + this.playlist.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
{{ track.name }}
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
{{ track.artists[0].name }}
|
||||
</p>
|
||||
<div class="content is-small">
|
||||
<p>
|
||||
<span class="heading">Album</span>
|
||||
<a class="title is-6 has-text-link" @click="open_album">{{ album.name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Album artist</span>
|
||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artists[0].name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Release date</span>
|
||||
<span class="title is-6">{{ album.release_date }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Track / Disc</span>
|
||||
<span class="title is-6">{{ track.track_number }} / {{ track.disc_number }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Length</span>
|
||||
<span class="title is-6">{{ track.duration_ms | duration }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Path</span>
|
||||
<span class="title is-6">{{ track.uri }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SpotifyModalDialogTrack',
|
||||
props: [ 'show', 'track', 'album' ],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_uri(this.track.uri, false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add(this.track.uri)
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_add_next(this.track.uri)
|
||||
},
|
||||
|
||||
open_album: function () {
|
||||
this.$router.push({ path: '/music/spotify/albums/' + this.album.id })
|
||||
},
|
||||
|
||||
open_artist: function () {
|
||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<section class="section fd-tabs-section" v-if="spotify_enabled">
|
||||
<section class="section fd-remove-padding-bottom" v-if="spotify_enabled">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
|
|
|
@ -38,11 +38,20 @@ a.navbar-item {
|
|||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.fd-remove-padding-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.fd-has-padding-left-right {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.fd-is-square .button {
|
||||
height: 27px;
|
||||
width: 27px;
|
||||
}
|
||||
|
||||
.fd-is-text-clipped {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
@ -65,7 +74,17 @@ a.navbar-item {
|
|||
}
|
||||
|
||||
.fd-tabs-section {
|
||||
padding-bottom: 0;
|
||||
padding-bottom: 3px;
|
||||
padding-top: 3px;
|
||||
background: white;
|
||||
top: 3.25rem;
|
||||
z-index: 20;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section.fd-tabs-section + section.fd-content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.fd-progress-bar {
|
||||
|
@ -131,3 +150,7 @@ a.navbar-item {
|
|||
max-height: calc(100vh - 200px);
|
||||
overflow: auto;
|
||||
}
|
||||
.fd-modal-card .card {
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,14 @@
|
|||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" :position="index" :context_uri="album.uri"></list-item-track>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -27,6 +34,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumData = {
|
||||
|
@ -46,12 +54,15 @@ const albumData = {
|
|||
export default {
|
||||
name: 'PageAlbum',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||
components: { ContentWithHeading, ListItemTrack },
|
||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
album: {},
|
||||
tracks: []
|
||||
tracks: [],
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -63,6 +74,15 @@ export default {
|
|||
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.album.uri, true)
|
||||
},
|
||||
|
||||
play_track: function (position) {
|
||||
webapi.player_play_uri(this.album.uri, false, position)
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">Albums</p>
|
||||
<p class="heading">{{ albums.total }} albums</p>
|
||||
|
@ -16,7 +19,18 @@
|
|||
</a>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" v-if="!hide_singles || album.track_count > 2"></list-item-album>
|
||||
<list-item-album v-for="album in albums.items"
|
||||
:key="album.id"
|
||||
:album="album"
|
||||
@click="open_album(album)"
|
||||
v-if="!hide_singles || album.track_count > 2">
|
||||
<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>
|
||||
</template>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -26,7 +40,9 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
|
@ -37,17 +53,24 @@ const albumsData = {
|
|||
|
||||
set: function (vm, response) {
|
||||
vm.albums = response.data
|
||||
vm.index_list = [...new Set(vm.albums.items
|
||||
.filter(album => !vm.$store.state.hide_singles || album.track_count > 2)
|
||||
.map(album => album.name_sort.charAt(0).toUpperCase()))]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PageAlbums',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum },
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemAlbum, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
albums: {}
|
||||
albums: { items: [] },
|
||||
index_list: [],
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,6 +83,23 @@ export default {
|
|||
methods: {
|
||||
update_hide_singles: function (e) {
|
||||
this.$store.commit(types.HIDE_SINGLES, !this.hide_singles)
|
||||
},
|
||||
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
'hide_singles' () {
|
||||
this.index_list = [...new Set(this.albums.items
|
||||
.filter(album => !this.$store.state.hide_singles || album.track_count > 2)
|
||||
.map(album => album.name_sort.charAt(0).toUpperCase()))]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,14 @@
|
|||
</template>
|
||||
<template 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-item-album v-for="album in albums.items" :key="album.id" :album="album"></list-item-album>
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<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>
|
||||
</template>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -19,6 +26,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const artistData = {
|
||||
|
@ -38,12 +46,15 @@ const artistData = {
|
|||
export default {
|
||||
name: 'PageArtist',
|
||||
mixins: [ LoadDataBeforeEnterMixin(artistData) ],
|
||||
components: { ContentWithHeading, ListItemAlbum },
|
||||
components: { ContentWithHeading, ListItemAlbum, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
artist: {},
|
||||
albums: {}
|
||||
albums: {},
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -54,6 +65,15 @@ export default {
|
|||
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.albums.items.map(a => a.uri).join(','), true)
|
||||
},
|
||||
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">{{ artist.name }}</p>
|
||||
</template>
|
||||
|
@ -11,7 +14,14 @@
|
|||
</template>
|
||||
<template 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-item-track v-for="(track, index) in tracks.items" :key="track.id" :track="track" :position="index" :context_uri="track.uri"></list-item-track>
|
||||
<list-item-track v-for="(track, index) in tracks.items" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -20,7 +30,9 @@
|
|||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const tracksData = {
|
||||
|
@ -38,14 +50,24 @@ const tracksData = {
|
|||
}
|
||||
|
||||
export default {
|
||||
name: 'PageTracks',
|
||||
name: 'PageArtistTracks',
|
||||
mixins: [ LoadDataBeforeEnterMixin(tracksData) ],
|
||||
components: { ContentWithHeading, ListItemTrack },
|
||||
components: { ContentWithHeading, ListItemTrack, IndexButtonList, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
artist: {},
|
||||
tracks: {}
|
||||
tracks: { items: [] },
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.tracks.items
|
||||
.map(track => track.title_sort.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -57,6 +79,15 @@ export default {
|
|||
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.tracks.items.map(a => a.uri).join(','), true)
|
||||
},
|
||||
|
||||
play_track: function (position) {
|
||||
webapi.player_play_uri(this.tracks.items.map(a => a.uri).join(','), false, position)
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">Artists</p>
|
||||
<p class="heading">{{ artists.total }} artists</p>
|
||||
|
@ -16,7 +19,18 @@
|
|||
</a>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-artist v-for="artist in artists.items" :key="artist.id" :artist="artist" v-if="!hide_singles || artist.track_count > (artist.album_count * 2)"></list-item-artist>
|
||||
<list-item-artist v-for="artist in artists.items"
|
||||
:key="artist.id"
|
||||
:artist="artist"
|
||||
@click="open_artist(artist)"
|
||||
v-if="!hide_singles || artist.track_count > (artist.album_count * 2)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(artist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-artist>
|
||||
<modal-dialog-artist :show="show_details_modal" :artist="selected_artist" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -26,7 +40,9 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListItemArtist from '@/components/ListItemArtist'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
|
@ -43,23 +59,41 @@ const artistsData = {
|
|||
export default {
|
||||
name: 'PageArtists',
|
||||
mixins: [ LoadDataBeforeEnterMixin(artistsData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemArtist },
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemArtist, ModalDialogArtist },
|
||||
|
||||
data () {
|
||||
return {
|
||||
artists: {}
|
||||
artists: { items: [] },
|
||||
|
||||
show_details_modal: false,
|
||||
selected_artist: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
hide_singles () {
|
||||
return this.$store.state.hide_singles
|
||||
},
|
||||
|
||||
index_list () {
|
||||
return [...new Set(this.artists.items
|
||||
.filter(artist => !this.$store.state.hide_singles || artist.track_count > (artist.album_count * 2))
|
||||
.map(artist => artist.name_sort.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
update_hide_singles: function (e) {
|
||||
this.$store.commit(types.HIDE_SINGLES, !this.hide_singles)
|
||||
},
|
||||
|
||||
open_artist: function (artist) {
|
||||
this.$router.push({ path: '/music/artists/' + artist.id })
|
||||
},
|
||||
|
||||
open_dialog: function (artist) {
|
||||
this.selected_artist = artist
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,14 @@
|
|||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" :position="index" :context_uri="album.uri"></list-item-track>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -23,6 +30,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumData = {
|
||||
|
@ -42,18 +50,30 @@ const albumData = {
|
|||
export default {
|
||||
name: 'PageAudiobook',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||
components: { ContentWithHeading, ListItemTrack },
|
||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
album: {},
|
||||
tracks: []
|
||||
tracks: [],
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.album.uri, false)
|
||||
},
|
||||
|
||||
play_track: function (position) {
|
||||
webapi.player_play_uri(this.album.uri, false, position)
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
<p class="heading">{{ albums.total }} audiobooks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" :media_kind="'audiobook'"></list-item-album>
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" :media_kind="'audiobook'" @click="open_album(album)">
|
||||
<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>
|
||||
</template>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" :media_kind="'audiobook'" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -16,6 +23,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumsData = {
|
||||
|
@ -31,11 +39,25 @@ const albumsData = {
|
|||
export default {
|
||||
name: 'PageAudiobooks',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
||||
components: { ContentWithHeading, ListItemAlbum },
|
||||
components: { ContentWithHeading, ListItemAlbum, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
albums: {}
|
||||
albums: {},
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/audiobooks/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,14 @@
|
|||
<p class="heading">albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-album v-for="album in recently_added.items" :key="album.id" :album="album"></list-item-album>
|
||||
<list-item-album v-for="album in recently_added.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<template 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>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav class="level">
|
||||
|
@ -27,7 +34,14 @@
|
|||
<p class="heading">tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-track v-for="track in recently_played.items" :key="track.id" :track="track" :position="0" :context_uri="track.uri"></list-item-track>
|
||||
<list-item-track v-for="track in recently_played.items" :key="track.id" :track="track" @click="play_track(track)">
|
||||
<template 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>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_track_details_modal" :track="selected_track" @close="show_track_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav class="level">
|
||||
|
@ -46,6 +60,8 @@ import ContentWithHeading from '@/templates/ContentWithHeading'
|
|||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const browseData = {
|
||||
|
@ -65,18 +81,42 @@ const browseData = {
|
|||
export default {
|
||||
name: 'PageBrowse',
|
||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum, ListItemTrack },
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum, ListItemTrack, ModalDialogTrack, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
recently_added: {},
|
||||
recently_played: {}
|
||||
recently_played: {},
|
||||
|
||||
show_track_details_modal: false,
|
||||
selected_track: {},
|
||||
|
||||
show_album_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_browse: function (type) {
|
||||
this.$router.push({ path: '/music/browse/' + type })
|
||||
},
|
||||
|
||||
open_track_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
},
|
||||
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_album_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_album_details_modal = true
|
||||
},
|
||||
|
||||
play_track: function (track) {
|
||||
webapi.player_play_uri(track.uri, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,14 @@
|
|||
<p class="heading">albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-album v-for="album in recently_added.items" :key="album.id" :album="album"></list-item-album>
|
||||
<list-item-album v-for="album in recently_added.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<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>
|
||||
</template>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -19,6 +26,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const browseData = {
|
||||
|
@ -38,11 +46,25 @@ const browseData = {
|
|||
export default {
|
||||
name: 'PageBrowseType',
|
||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum },
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
recently_added: {}
|
||||
recently_added: {},
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,14 @@
|
|||
<p class="heading">tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-track v-for="track in recently_played.items" :key="track.id" :track="track" :position="0" :context_uri="track.uri"></list-item-track>
|
||||
<list-item-track v-for="track in recently_played.items" :key="track.id" :track="track" @click="play_track(track)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -19,6 +26,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const browseData = {
|
||||
|
@ -38,11 +46,25 @@ const browseData = {
|
|||
export default {
|
||||
name: 'PageBrowseType',
|
||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemTrack },
|
||||
components: { ContentWithHeading, TabsMusic, ListItemTrack, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
recently_played: {}
|
||||
recently_played: {},
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
},
|
||||
|
||||
play_track: function (track) {
|
||||
webapi.player_play_uri(track.uri, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">{{ name }}</p>
|
||||
<p class="heading">{{ genreAlbums.total }} albums</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<a class="button is-small is-dark is-rounded" @click="play">
|
||||
|
@ -13,7 +13,15 @@
|
|||
</a>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-albums v-for="album in genreAlbums.items" :key="album.id" :album="album" :links="links"></list-item-albums>
|
||||
<p class="heading has-text-centered-mobile">{{ genre_albums.total }} albums | <a class="has-text-link" @click="open_tracks">tracks</a></p>
|
||||
<list-item-albums v-for="album in genre_albums.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<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>
|
||||
</template>
|
||||
</list-item-albums>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -23,7 +31,9 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListItemAlbums from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const genreData = {
|
||||
|
@ -33,40 +43,49 @@ const genreData = {
|
|||
|
||||
set: function (vm, response) {
|
||||
vm.name = vm.$route.params.genre
|
||||
vm.genreAlbums = response.data.albums
|
||||
var li = 0
|
||||
var v = null
|
||||
var i
|
||||
for (i = 0; i < vm.genreAlbums.items.length; i++) {
|
||||
var n = vm.genreAlbums.items[i].name_sort.charAt(0).toUpperCase()
|
||||
if (n !== v) {
|
||||
var obj = {}
|
||||
obj.n = n
|
||||
obj.a = 'idx_nav_' + li
|
||||
vm.links.push(obj)
|
||||
li++
|
||||
v = n
|
||||
}
|
||||
}
|
||||
vm.genre_albums = response.data.albums
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PageGenre',
|
||||
mixins: [ LoadDataBeforeEnterMixin(genreData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbums },
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemAlbums, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
name: '',
|
||||
genreAlbums: {},
|
||||
links: []
|
||||
genre_albums: { items: [] },
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.genre_albums.items
|
||||
.map(album => album.name.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_tracks: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/genres/' + this.name + '/tracks' })
|
||||
},
|
||||
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.genreAlbums.items.map(a => a.uri).join(','), true)
|
||||
webapi.player_play_uri(this.genre_albums.items.map(a => a.uri).join(','), true)
|
||||
},
|
||||
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div>
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">{{ genre }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<a class="button is-small is-dark is-rounded" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
||||
</a>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_genre">albums</a> | {{ tracks.total }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in tracks.items" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import IndexButtonList from '@/components/IndexButtonList'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const tracksData = {
|
||||
load: function (to) {
|
||||
return webapi.library_genre_tracks(to.params.genre)
|
||||
},
|
||||
|
||||
set: function (vm, response) {
|
||||
vm.genre = vm.$route.params.genre
|
||||
vm.tracks = response.data.tracks
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PageGenreTracks',
|
||||
mixins: [ LoadDataBeforeEnterMixin(tracksData) ],
|
||||
components: { ContentWithHeading, ListItemTrack, IndexButtonList, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
tracks: { items: [] },
|
||||
genre: '',
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.tracks.items
|
||||
.map(track => track.title_sort.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_genre: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ path: '/music/genres/' + this.genre })
|
||||
},
|
||||
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.tracks.items.map(a => a.uri).join(','), true)
|
||||
},
|
||||
|
||||
play_track: function (position) {
|
||||
webapi.player_play_uri(this.tracks.items.map(a => a.uri).join(','), false, position)
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -3,12 +3,22 @@
|
|||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="options">
|
||||
<index-button-list :index="index_list"></index-button-list>
|
||||
</template>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">Genres</p>
|
||||
<p class="heading">{{ genres.total }} genres</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-genre v-for="genre in genres.items" :key="genre.name" :genre="genre"></list-item-genre>
|
||||
<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)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-genre>
|
||||
<modal-dialog-genre :show="show_details_modal" :genre="selected_genre" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -18,7 +28,9 @@
|
|||
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 webapi from '@/webapi'
|
||||
|
||||
const genresData = {
|
||||
|
@ -34,15 +46,33 @@ const genresData = {
|
|||
export default {
|
||||
name: 'PageGenres',
|
||||
mixins: [ LoadDataBeforeEnterMixin(genresData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemGenre },
|
||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemGenre, ModalDialogGenre },
|
||||
|
||||
data () {
|
||||
return {
|
||||
genres: {}
|
||||
genres: { items: [] },
|
||||
|
||||
show_details_modal: false,
|
||||
selected_genre: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.genres.items
|
||||
.map(genre => genre.name.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_genre: function (genre) {
|
||||
this.$router.push({ name: 'Genre', params: { genre: genre.name } })
|
||||
},
|
||||
|
||||
open_dialog: function (genre) {
|
||||
this.selected_genre = genre
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -14,7 +14,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="artwork_visible">
|
||||
<img :src="artwork_url" class="fd-has-shadow fd-image-fullheight" @load="artwork_loaded" @error="artwork_error">
|
||||
<img :src="artwork_url" class="fd-has-shadow fd-image-fullheight fd-has-action"
|
||||
@load="artwork_loaded"
|
||||
@error="artwork_error"
|
||||
@click="open_dialog(now_playing)">
|
||||
</div>
|
||||
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="!artwork_visible">
|
||||
<a @click="open_dialog(now_playing)" class="button is-white is-medium">
|
||||
<span class="icon has-text-grey-light"><i class="mdi mdi-information-outline"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hero-foot fd-has-padding-left-right">
|
||||
<div class="container has-text-centered fd-has-margin-bottom">
|
||||
|
@ -41,11 +49,13 @@
|
|||
<player-button-consume class="button is-medium is-light"></player-button-consume>
|
||||
</div>
|
||||
</div>
|
||||
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause'
|
||||
import PlayerButtonNext from '@/components/PlayerButtonNext'
|
||||
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
|
||||
|
@ -58,13 +68,16 @@ import * as types from '@/store/mutation_types'
|
|||
|
||||
export default {
|
||||
name: 'PageNowPlaying',
|
||||
components: { PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat, RangeSlider },
|
||||
components: { ModalDialogQueueItem, PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat, RangeSlider },
|
||||
|
||||
data () {
|
||||
return {
|
||||
item_progress_ms: 0,
|
||||
interval_id: 0,
|
||||
artwork_visible: false
|
||||
artwork_visible: false,
|
||||
|
||||
show_details_modal: false,
|
||||
selected_item: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -118,6 +131,11 @@ export default {
|
|||
|
||||
artwork_error: function () {
|
||||
this.artwork_visible = false
|
||||
},
|
||||
|
||||
open_dialog: function (item) {
|
||||
this.selected_item = item
|
||||
this.show_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -10,7 +10,14 @@
|
|||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile">{{ tracks.length }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" :position="index" :context_uri="playlist.uri"></list-item-track>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -19,6 +26,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const playlistData = {
|
||||
|
@ -38,18 +46,30 @@ const playlistData = {
|
|||
export default {
|
||||
name: 'PagePlaylist',
|
||||
mixins: [ LoadDataBeforeEnterMixin(playlistData) ],
|
||||
components: { ContentWithHeading, ListItemTrack },
|
||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
playlist: {},
|
||||
tracks: []
|
||||
tracks: [],
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.playlist.uri, true)
|
||||
},
|
||||
|
||||
play_track: function (position) {
|
||||
webapi.player_play_uri(this.playlist.uri, false, position)
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,14 @@
|
|||
<p class="heading">{{ playlists.total }} playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist"></list-item-playlist>
|
||||
<list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist" @click="open_playlist(playlist)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(playlist)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-playlist>
|
||||
<modal-dialog-playlist :show="show_details_modal" :playlist="selected_playlist" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -15,6 +22,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemPlaylist from '@/components/ListItemPlaylist'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const playlistsData = {
|
||||
|
@ -30,11 +38,25 @@ const playlistsData = {
|
|||
export default {
|
||||
name: 'PagePlaylists',
|
||||
mixins: [ LoadDataBeforeEnterMixin(playlistsData) ],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemPlaylist },
|
||||
components: { ContentWithHeading, TabsMusic, ListItemPlaylist, ModalDialogPlaylist },
|
||||
|
||||
data () {
|
||||
return {
|
||||
playlists: {}
|
||||
playlists: {},
|
||||
|
||||
show_details_modal: false,
|
||||
selected_playlist: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_playlist: function (playlist) {
|
||||
this.$router.push({ path: '/playlists/' + playlist.id })
|
||||
},
|
||||
|
||||
open_dialog: function (playlist) {
|
||||
this.selected_playlist = playlist
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,14 @@
|
|||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" :position="index" :context_uri="album.uri"></list-item-track>
|
||||
<list-item-track v-for="track in tracks" :key="track.id" :track="track" @click="play_track(track)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -22,6 +29,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumData = {
|
||||
|
@ -41,18 +49,30 @@ const albumData = {
|
|||
export default {
|
||||
name: 'PagePodcast',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||
components: { ContentWithHeading, ListItemTrack },
|
||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
album: {},
|
||||
tracks: []
|
||||
tracks: [],
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
webapi.player_play_uri(this.album.uri, false)
|
||||
},
|
||||
|
||||
play_track: function (track) {
|
||||
webapi.player_play_uri(track.uri, false)
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
<p class="heading">{{ albums.total }} podcasts</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" :media_kind="'podcast'"></list-item-album>
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" :media_kind="'podcast'" @click="open_album(album)">
|
||||
<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>
|
||||
</template>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" :media_kind="'podcast'" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -16,6 +23,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const albumsData = {
|
||||
|
@ -31,11 +39,25 @@ const albumsData = {
|
|||
export default {
|
||||
name: 'PagePodcasts',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
||||
components: { ContentWithHeading, ListItemAlbum },
|
||||
components: { ContentWithHeading, ListItemAlbum, ModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
albums: {}
|
||||
albums: {},
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/podcasts/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,18 @@
|
|||
:key="item.id" :item="item" :position="index"
|
||||
:current_position="current_position"
|
||||
:show_only_next_items="show_only_next_items"
|
||||
:edit_mode="edit_mode"></list-item-queue-item>
|
||||
: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>
|
||||
</draggable>
|
||||
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -49,17 +59,21 @@
|
|||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import ListItemQueueItem from '@/components/ListItemQueueItem'
|
||||
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'PageQueue',
|
||||
components: { ContentWithHeading, ListItemQueueItem, draggable },
|
||||
components: { ContentWithHeading, ListItemQueueItem, draggable, ModalDialogQueueItem },
|
||||
|
||||
data () {
|
||||
return {
|
||||
edit_mode: false
|
||||
edit_mode: false,
|
||||
|
||||
show_details_modal: false,
|
||||
selected_item: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -92,6 +106,10 @@ export default {
|
|||
this.$store.commit(types.SHOW_ONLY_NEXT_ITEMS, !this.show_only_next_items)
|
||||
},
|
||||
|
||||
remove: function (item) {
|
||||
webapi.queue_remove(item.id)
|
||||
},
|
||||
|
||||
move_item: function (e) {
|
||||
var oldPosition = !this.show_only_next_items ? e.oldIndex : e.oldIndex + this.current_position
|
||||
var item = this.queue_items[oldPosition]
|
||||
|
@ -99,6 +117,11 @@ export default {
|
|||
if (newPosition !== oldPosition) {
|
||||
webapi.queue_move(item.id, newPosition)
|
||||
}
|
||||
},
|
||||
|
||||
open_dialog: function (item) {
|
||||
this.selected_item = item
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<!-- Search field + recent searches -->
|
||||
<section class="section fd-tabs-section">
|
||||
<section class="section fd-remove-padding-bottom">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
|
@ -31,7 +31,14 @@
|
|||
<p class="title is-4">Tracks</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-track v-for="track in tracks.items" :key="track.id" :track="track" :position="0" :context_uri="track.uri"></list-item-track>
|
||||
<list-item-track v-for="track in tracks.items" :key="track.id" :track="track" @click="play_track(track)">
|
||||
<template 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>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_track_details_modal" :track="selected_track" @close="show_track_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_tracks_button" class="level">
|
||||
|
@ -49,7 +56,14 @@
|
|||
<p class="title is-4">Artists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-artist v-for="artist in artists.items" :key="artist.id" :artist="artist"></list-item-artist>
|
||||
<list-item-artist v-for="artist in artists.items" :key="artist.id" :artist="artist" @click="open_artist(artist)">
|
||||
<template 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>
|
||||
</list-item-artist>
|
||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="selected_artist" @close="show_artist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_artists_button" class="level">
|
||||
|
@ -67,7 +81,14 @@
|
|||
<p class="title is-4">Albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album"></list-item-album>
|
||||
<list-item-album v-for="album in albums.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<template 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>
|
||||
</list-item-album>
|
||||
<modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_albums_button" class="level">
|
||||
|
@ -85,7 +106,14 @@
|
|||
<p class="title is-4">Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist"></list-item-playlist>
|
||||
<list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist" @click="open_playlist(playlist)">
|
||||
<template 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>
|
||||
</list-item-playlist>
|
||||
<modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_playlists_button" class="level">
|
||||
|
@ -106,12 +134,16 @@ import ListItemTrack from '@/components/ListItemTrack'
|
|||
import ListItemArtist from '@/components/ListItemArtist'
|
||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||
import ListItemPlaylist from '@/components/ListItemPlaylist'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
export default {
|
||||
name: 'PageSearch',
|
||||
components: { ContentWithHeading, TabsSearch, ListItemTrack, ListItemArtist, ListItemAlbum, ListItemPlaylist },
|
||||
components: { ContentWithHeading, TabsSearch, ListItemTrack, ListItemArtist, ListItemAlbum, ListItemPlaylist, ModalDialogTrack, ModalDialogAlbum, ModalDialogArtist, ModalDialogPlaylist },
|
||||
|
||||
data () {
|
||||
return {
|
||||
|
@ -119,7 +151,19 @@ export default {
|
|||
tracks: { items: [], total: 0 },
|
||||
artists: { items: [], total: 0 },
|
||||
albums: { items: [], total: 0 },
|
||||
playlists: { items: [], total: 0 }
|
||||
playlists: { items: [], total: 0 },
|
||||
|
||||
show_track_details_modal: false,
|
||||
selected_track: {},
|
||||
|
||||
show_album_details_modal: false,
|
||||
selected_album: {},
|
||||
|
||||
show_artist_details_modal: false,
|
||||
selected_artist: {},
|
||||
|
||||
show_playlist_details_modal: false,
|
||||
selected_playlist: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -238,9 +282,45 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
play_track: function (track) {
|
||||
webapi.player_play_uri(track.uri, false)
|
||||
},
|
||||
|
||||
open_artist: function (artist) {
|
||||
this.$router.push({ path: '/music/artists/' + artist.id })
|
||||
},
|
||||
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_playlist: function (playlist) {
|
||||
this.$router.push({ path: '/playlists/' + playlist.id })
|
||||
},
|
||||
|
||||
open_recent_search: function (query) {
|
||||
this.search_query = query
|
||||
this.new_search()
|
||||
},
|
||||
|
||||
open_track_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
},
|
||||
|
||||
open_album_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_album_details_modal = true
|
||||
},
|
||||
|
||||
open_artist_dialog: function (artist) {
|
||||
this.selected_artist = artist
|
||||
this.show_artist_details_modal = true
|
||||
},
|
||||
|
||||
open_playlist_dialog: function (playlist) {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -11,7 +11,14 @@
|
|||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile">{{ 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"></spotify-list-item-track>
|
||||
<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">
|
||||
<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>
|
||||
<spotify-modal-dialog-track :show="show_track_details_modal" :track="selected_track" :album="album" @close="show_track_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -20,6 +27,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||
import store from '@/store'
|
||||
import webapi from '@/webapi'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
@ -39,11 +47,14 @@ const albumData = {
|
|||
export default {
|
||||
name: 'PageAlbum',
|
||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||
components: { ContentWithHeading, SpotifyListItemTrack },
|
||||
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack },
|
||||
|
||||
data () {
|
||||
return {
|
||||
album: {}
|
||||
album: { artists: [{}], tracks: {} },
|
||||
|
||||
show_track_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -55,6 +66,11 @@ export default {
|
|||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.album.uri, true)
|
||||
},
|
||||
|
||||
open_track_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,15 @@
|
|||
</template>
|
||||
<template 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"></spotify-list-item-album>
|
||||
<spotify-list-item-album v-for="album in albums" :key="album.id" :album="album">
|
||||
<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>
|
||||
</template>
|
||||
</spotify-list-item-album>
|
||||
<infinite-loading v-if="offset < total" @infinite="load_next"><span slot="no-more">.</span></infinite-loading>
|
||||
<spotify-modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -15,6 +22,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||
import store from '@/store'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
|
@ -42,14 +50,17 @@ const artistData = {
|
|||
export default {
|
||||
name: 'SpotifyPageArtist',
|
||||
mixins: [ LoadDataBeforeEnterMixin(artistData) ],
|
||||
components: { ContentWithHeading, SpotifyListItemAlbum, InfiniteLoading },
|
||||
components: { ContentWithHeading, SpotifyListItemAlbum, SpotifyModalDialogAlbum, InfiniteLoading },
|
||||
|
||||
data () {
|
||||
return {
|
||||
artist: {},
|
||||
albums: [],
|
||||
total: 0,
|
||||
offset: 0
|
||||
offset: 0,
|
||||
|
||||
show_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -73,6 +84,11 @@ export default {
|
|||
$state.complete()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,14 @@
|
|||
<p class="title is-4">New Releases</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-album v-for="album in new_releases" :key="album.id" :album="album"></spotify-list-item-album>
|
||||
<spotify-list-item-album v-for="album in new_releases" :key="album.id" :album="album">
|
||||
<template 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>
|
||||
<spotify-modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav class="level">
|
||||
|
@ -27,7 +34,14 @@
|
|||
<p class="title is-4">Featured Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-playlist v-for="playlist in featured_playlists" :key="playlist.id" :playlist="playlist"></spotify-list-item-playlist>
|
||||
<spotify-list-item-playlist v-for="playlist in featured_playlists" :key="playlist.id" :playlist="playlist">
|
||||
<template 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>
|
||||
<spotify-modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav class="level">
|
||||
|
@ -48,6 +62,8 @@ 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 store from '@/store'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
@ -77,7 +93,17 @@ const browseData = {
|
|||
export default {
|
||||
name: 'SpotifyPageBrowse',
|
||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum, SpotifyListItemPlaylist },
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum, SpotifyListItemPlaylist, SpotifyModalDialogAlbum, SpotifyModalDialogPlaylist },
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_album_details_modal: false,
|
||||
selected_album: {},
|
||||
|
||||
show_playlist_details_modal: false,
|
||||
selected_playlist: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
new_releases () {
|
||||
|
@ -87,6 +113,18 @@ export default {
|
|||
featured_playlists () {
|
||||
return this.$store.state.spotify_featured_playlists.slice(0, 3)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_album_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_album_details_modal = true
|
||||
},
|
||||
|
||||
open_playlist_dialog: function (playlist) {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -7,7 +7,14 @@
|
|||
<p class="title is-4">Featured Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-playlist v-for="playlist in featured_playlists" :key="playlist.id" :playlist="playlist"></spotify-list-item-playlist>
|
||||
<spotify-list-item-playlist v-for="playlist in featured_playlists" :key="playlist.id" :playlist="playlist">
|
||||
<template 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>
|
||||
<spotify-modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -18,6 +25,7 @@ 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 store from '@/store'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
@ -43,12 +51,26 @@ const browseData = {
|
|||
export default {
|
||||
name: 'SpotifyPageBrowseFeaturedPlaylists',
|
||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemPlaylist },
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemPlaylist, SpotifyModalDialogPlaylist },
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_playlist_details_modal: false,
|
||||
selected_playlist: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
featured_playlists () {
|
||||
return this.$store.state.spotify_featured_playlists
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_playlist_dialog: function (playlist) {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -7,7 +7,14 @@
|
|||
<p class="title is-4">New Releases</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-album v-for="album in new_releases" :key="album.id" :album="album"></spotify-list-item-album>
|
||||
<spotify-list-item-album v-for="album in new_releases" :key="album.id" :album="album">
|
||||
<template slot="actions">
|
||||
<a @click="open_album(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</spotify-list-item-album>
|
||||
<spotify-modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
|
@ -18,6 +25,7 @@ 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 store from '@/store'
|
||||
import * as types from '@/store/mutation_types'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
@ -43,12 +51,26 @@ const browseData = {
|
|||
export default {
|
||||
name: 'SpotifyPageBrowseNewReleases',
|
||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum },
|
||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum, SpotifyModalDialogAlbum },
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_album_details_modal: false,
|
||||
selected_album: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
new_releases () {
|
||||
return this.$store.state.spotify_new_releases
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_album: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_album_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -10,8 +10,15 @@
|
|||
</template>
|
||||
<template 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"></spotify-list-item-track>
|
||||
<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">
|
||||
<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>
|
||||
<spotify-modal-dialog-track :show="show_track_details_modal" :track="selected_track" :album="selected_track.album" @close="show_track_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</template>
|
||||
|
@ -20,6 +27,7 @@
|
|||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||
import store from '@/store'
|
||||
import webapi from '@/webapi'
|
||||
import SpotifyWebApi from 'spotify-web-api-js'
|
||||
|
@ -47,14 +55,17 @@ const playlistData = {
|
|||
export default {
|
||||
name: 'SpotifyPagePlaylist',
|
||||
mixins: [ LoadDataBeforeEnterMixin(playlistData) ],
|
||||
components: { ContentWithHeading, SpotifyListItemTrack, InfiniteLoading },
|
||||
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack, InfiniteLoading },
|
||||
|
||||
data () {
|
||||
return {
|
||||
playlist: {},
|
||||
playlist: { tracks: {} },
|
||||
tracks: [],
|
||||
total: 0,
|
||||
offset: 0
|
||||
offset: 0,
|
||||
|
||||
show_track_details_modal: false,
|
||||
selected_track: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -83,6 +94,11 @@ export default {
|
|||
play: function () {
|
||||
this.show_details_modal = false
|
||||
webapi.player_play_uri(this.playlist.uri, true)
|
||||
},
|
||||
|
||||
open_track_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<!-- Search field + recent searches -->
|
||||
<section class="section fd-tabs-section">
|
||||
<section class="section fd-remove-padding-bottom">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
|
@ -31,8 +31,15 @@
|
|||
<p class="title is-4">Tracks</p>
|
||||
</template>
|
||||
<template 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"></spotify-list-item-track>
|
||||
<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">
|
||||
<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>
|
||||
<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">
|
||||
<nav v-if="show_all_tracks_button" class="level">
|
||||
|
@ -50,8 +57,15 @@
|
|||
<p class="title is-4">Artists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-artist v-for="artist in artists.items" :key="artist.id" :artist="artist"></spotify-list-item-artist>
|
||||
<spotify-list-item-artist v-for="artist in artists.items" :key="artist.id" :artist="artist">
|
||||
<template 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>
|
||||
<spotify-modal-dialog-artist :show="show_artist_details_modal" :artist="selected_artist" @close="show_artist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_artists_button" class="level">
|
||||
|
@ -69,8 +83,15 @@
|
|||
<p class="title is-4">Albums</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-album v-for="album in albums.items" :key="album.id" :album="album"></spotify-list-item-album>
|
||||
<spotify-list-item-album v-for="album in albums.items" :key="album.id" :album="album">
|
||||
<template 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>
|
||||
<spotify-modal-dialog-album :show="show_album_details_modal" :album="selected_album" @close="show_album_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_albums_button" class="level">
|
||||
|
@ -88,8 +109,15 @@
|
|||
<p class="title is-4">Playlists</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<spotify-list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist"></spotify-list-item-playlist>
|
||||
<spotify-list-item-playlist v-for="playlist in playlists.items" :key="playlist.id" :playlist="playlist">
|
||||
<template 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>
|
||||
<spotify-modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_playlists_button" class="level">
|
||||
|
@ -110,6 +138,10 @@ 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 SpotifyWebApi from 'spotify-web-api-js'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
@ -117,7 +149,7 @@ import InfiniteLoading from 'vue-infinite-loading'
|
|||
|
||||
export default {
|
||||
name: 'SpotifyPageSearch',
|
||||
components: { ContentWithHeading, TabsSearch, SpotifyListItemTrack, SpotifyListItemArtist, SpotifyListItemAlbum, SpotifyListItemPlaylist, InfiniteLoading },
|
||||
components: { ContentWithHeading, TabsSearch, SpotifyListItemTrack, SpotifyListItemArtist, SpotifyListItemAlbum, SpotifyListItemPlaylist, SpotifyModalDialogTrack, SpotifyModalDialogArtist, SpotifyModalDialogAlbum, SpotifyModalDialogPlaylist, InfiniteLoading },
|
||||
|
||||
data () {
|
||||
return {
|
||||
|
@ -128,7 +160,19 @@ export default {
|
|||
playlists: { items: [], total: 0 },
|
||||
|
||||
query: {},
|
||||
search_param: {}
|
||||
search_param: {},
|
||||
|
||||
show_track_details_modal: false,
|
||||
selected_track: {},
|
||||
|
||||
show_album_details_modal: false,
|
||||
selected_album: {},
|
||||
|
||||
show_artist_details_modal: false,
|
||||
selected_artist: {},
|
||||
|
||||
show_playlist_details_modal: false,
|
||||
selected_playlist: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -321,6 +365,26 @@ export default {
|
|||
open_recent_search: function (query) {
|
||||
this.search_query = query
|
||||
this.new_search()
|
||||
},
|
||||
|
||||
open_track_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
},
|
||||
|
||||
open_album_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_album_details_modal = true
|
||||
},
|
||||
|
||||
open_artist_dialog: function (artist) {
|
||||
this.selected_artist = artist
|
||||
this.show_artist_details_modal = true
|
||||
},
|
||||
|
||||
open_playlist_dialog: function (playlist) {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ import PageAlbums from '@/pages/PageAlbums'
|
|||
import PageAlbum from '@/pages/PageAlbum'
|
||||
import PageGenres from '@/pages/PageGenres'
|
||||
import PageGenre from '@/pages/PageGenre'
|
||||
import PageTracks from '@/pages/PageTracks'
|
||||
import PageGenreTracks from '@/pages/PageGenreTracks'
|
||||
import PageArtistTracks from '@/pages/PageArtistTracks'
|
||||
import PagePodcasts from '@/pages/PagePodcasts'
|
||||
import PagePodcast from '@/pages/PagePodcast'
|
||||
import PageAudiobooks from '@/pages/PageAudiobooks'
|
||||
|
@ -57,25 +58,25 @@ export const router = new VueRouter({
|
|||
path: '/music/browse',
|
||||
name: 'Browse',
|
||||
component: PageBrowse,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true }
|
||||
},
|
||||
{
|
||||
path: '/music/browse/recently_added',
|
||||
name: 'Browse Recently Added',
|
||||
component: PageBrowseRecentlyAdded,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true }
|
||||
},
|
||||
{
|
||||
path: '/music/browse/recently_played',
|
||||
name: 'Browse Recently Played',
|
||||
component: PageBrowseRecentlyPlayed,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true }
|
||||
},
|
||||
{
|
||||
path: '/music/artists',
|
||||
name: 'Artists',
|
||||
component: PageArtists,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/artists/:artist_id',
|
||||
|
@ -86,14 +87,14 @@ export const router = new VueRouter({
|
|||
{
|
||||
path: '/music/artists/:artist_id/tracks',
|
||||
name: 'Tracks',
|
||||
component: PageTracks,
|
||||
meta: { show_progress: true }
|
||||
component: PageArtistTracks,
|
||||
meta: { show_progress: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/albums',
|
||||
name: 'Albums',
|
||||
component: PageAlbums,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/albums/:album_id',
|
||||
|
@ -105,13 +106,19 @@ export const router = new VueRouter({
|
|||
path: '/music/genres',
|
||||
name: 'Genres',
|
||||
component: PageGenres,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/genres/:genre',
|
||||
name: 'Genre',
|
||||
component: PageGenre,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/genres/:genre/tracks',
|
||||
name: 'GenreTracks',
|
||||
component: PageGenreTracks,
|
||||
meta: { show_progress: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/podcasts',
|
||||
|
@ -162,19 +169,19 @@ export const router = new VueRouter({
|
|||
path: '/music/spotify',
|
||||
name: 'Spotify',
|
||||
component: SpotifyPageBrowse,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true }
|
||||
},
|
||||
{
|
||||
path: '/music/spotify/new-releases',
|
||||
name: 'Spotify Browse New Releases',
|
||||
component: SpotifyPageBrowseNewReleases,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true }
|
||||
},
|
||||
{
|
||||
path: '/music/spotify/featured-playlists',
|
||||
name: 'Spotify Browse Featured Playlists',
|
||||
component: SpotifyPageBrowseFeaturedPlaylists,
|
||||
meta: { show_progress: true }
|
||||
meta: { show_progress: true, has_tabs: true }
|
||||
},
|
||||
{
|
||||
path: '/music/spotify/artists/:artist_id',
|
||||
|
@ -201,11 +208,30 @@ export const router = new VueRouter({
|
|||
}
|
||||
],
|
||||
scrollBehavior (to, from, savedPosition) {
|
||||
// console.log(to.path + '_' + from.path + '__' + to.hash + ' savedPosition:' + savedPosition)
|
||||
if (savedPosition) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve(savedPosition)
|
||||
}, 500)
|
||||
}, 10)
|
||||
})
|
||||
} else if (to.path === from.path && to.hash) {
|
||||
return { selector: to.hash, offset: { x: 0, y: 90 } }
|
||||
} else if (to.hash) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve({ selector: to.hash, offset: { x: 0, y: 90 } })
|
||||
}, 10)
|
||||
})
|
||||
} else if (to.meta.has_index) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
if (to.meta.has_tabs) {
|
||||
resolve({ selector: '#top', offset: { x: 0, y: 140 } })
|
||||
} else {
|
||||
resolve({ selector: '#top', offset: { x: 0, y: 100 } })
|
||||
}
|
||||
}, 10)
|
||||
})
|
||||
} else {
|
||||
return { x: 0, y: 0 }
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<section class="section">
|
||||
<section class="section fd-content">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
<nav class="level">
|
||||
<slot name="options"></slot>
|
||||
<nav class="level" id="top">
|
||||
<!-- Left side -->
|
||||
<div class="level-left">
|
||||
<div class="level-item has-text-centered-mobile">
|
||||
|
|
|
@ -41,8 +41,13 @@ export default {
|
|||
return axios.put('/api/queue/items/' + itemId + '?new_position=' + newPosition)
|
||||
},
|
||||
|
||||
queue_add (uri) {
|
||||
return axios.post('/api/queue/items/add?uris=' + uri)
|
||||
queue_add (uri, showNotification = true) {
|
||||
return axios.post('/api/queue/items/add?uris=' + uri).then((response) => {
|
||||
if (showNotification) {
|
||||
store.dispatch('add_notification', { text: response.data.count + ' tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
}
|
||||
return Promise.resolve(response)
|
||||
})
|
||||
},
|
||||
|
||||
queue_add_next (uri) {
|
||||
|
@ -50,17 +55,20 @@ export default {
|
|||
if (store.getters.now_playing && store.getters.now_playing.id) {
|
||||
position = store.getters.now_playing.position + 1
|
||||
}
|
||||
return axios.post('/api/queue/items/add?uris=' + uri + '&position=' + position)
|
||||
return axios.post('/api/queue/items/add?uris=' + uri + '&position=' + position).then((response) => {
|
||||
store.dispatch('add_notification', { text: response.data.count + ' tracks appended to queue', type: 'info', timeout: 2000 })
|
||||
return Promise.resolve(response)
|
||||
})
|
||||
},
|
||||
|
||||
player_status () {
|
||||
return axios.get('/api/player')
|
||||
},
|
||||
|
||||
player_play_uri (uris, shuffle, position = 0) {
|
||||
player_play_uri (uris, shuffle, position = undefined) {
|
||||
return this.queue_clear().then(() =>
|
||||
this.player_shuffle(shuffle).then(() =>
|
||||
this.queue_add(uris).then(() =>
|
||||
this.queue_add(uris, false).then(() =>
|
||||
this.player_play({ 'position': position })
|
||||
)
|
||||
)
|
||||
|
@ -163,6 +171,17 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
library_genre_tracks (genre) {
|
||||
var genreParams = {
|
||||
'type': 'tracks',
|
||||
'media_kind': 'music',
|
||||
'expression': 'genre is "' + genre + '"'
|
||||
}
|
||||
return axios.get('/api/search', {
|
||||
params: genreParams
|
||||
})
|
||||
},
|
||||
|
||||
library_artist_tracks (artist) {
|
||||
if (artist) {
|
||||
var artistParams = {
|
||||
|
|
Loading…
Reference in New Issue