mirror of
https://github.com/owntone/owntone-server.git
synced 2025-03-31 09:43:45 -04:00
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 */
|
/*# 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
1490
web-src/package-lock.json
generated
1490
web-src/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "forked-daapd-web",
|
"name": "forked-daapd-web",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "forked-daapd web interface",
|
"description": "forked-daapd web interface",
|
||||||
"author": "chme <christian.meffert@googlemail.com>",
|
"author": "chme <christian.meffert@googlemail.com>",
|
||||||
"license": "GPL-2.0",
|
"license": "GPL-2.0",
|
||||||
@ -15,24 +15,24 @@
|
|||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"bulma": "^0.7.1",
|
"bulma": "^0.7.1",
|
||||||
"mdi": "^2.1.99",
|
"mdi": "^2.1.99",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.23.0",
|
||||||
"moment-duration-format": "^2.2.2",
|
"moment-duration-format": "^2.2.2",
|
||||||
"npm": "^6.4.1",
|
"npm": "^6.5.0",
|
||||||
"reconnectingwebsocket": "^1.0.0",
|
"reconnectingwebsocket": "^1.0.0",
|
||||||
"spotify-web-api-js": "^1.1.1",
|
"spotify-web-api-js": "^1.1.1",
|
||||||
"vue": "^2.5.17",
|
"vue": "^2.5.21",
|
||||||
"vue-infinite-loading": "^2.4.0",
|
"vue-infinite-loading": "^2.4.3",
|
||||||
"vue-progressbar": "^0.7.4",
|
"vue-progressbar": "^0.7.4",
|
||||||
"vue-range-slider": "^0.6.0",
|
"vue-range-slider": "^0.6.0",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.2",
|
||||||
"vuedraggable": "^2.16.0",
|
"vuedraggable": "^2.17.0",
|
||||||
"vuex": "^3.0.1"
|
"vuex": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^3.1.1",
|
"@vue/cli-plugin-babel": "^3.2.0",
|
||||||
"@vue/cli-plugin-eslint": "^3.1.4",
|
"@vue/cli-plugin-eslint": "^3.2.1",
|
||||||
"@vue/cli-service": "^3.1.2",
|
"@vue/cli-service": "^3.2.0",
|
||||||
"@vue/eslint-config-standard": "^3.0.5",
|
"@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)
|
this.$store.commit(types.UPDATE_SPOTIFY, data)
|
||||||
|
|
||||||
if (this.token_timer_id > 0) {
|
if (this.token_timer_id > 0) {
|
||||||
console.log('clear old timer: ' + this.token_timer_id)
|
|
||||||
window.clearTimeout(this.token_timer_id)
|
window.clearTimeout(this.token_timer_id)
|
||||||
this.token_timer_id = 0
|
this.token_timer_id = 0
|
||||||
}
|
}
|
||||||
if (data.webapi_token_expires_in > 0 && data.webapi_token) {
|
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)
|
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')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
38
web-src/src/components/IndexButtonList.vue
Normal file
38
web-src/src/components/IndexButtonList.vue
Normal file
@ -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>
|
<template functional>
|
||||||
<div class="media">
|
<div class="media" :id="'index_' + props.album.name_sort.charAt(0).toUpperCase()">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_album">
|
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||||
<h1 class="title is-6">{{ album.name }}</h1>
|
<h1 class="title is-6">{{ props.album.name }}</h1>
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artist }}</b></h2>
|
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.album.artist }}</b></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details_modal = true">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from '@/components/ModalDialog'
|
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListItemAlbum',
|
name: 'ListItemAlbum',
|
||||||
components: { ModalDialog },
|
props: ['album', 'media_kind']
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,89 +1,18 @@
|
|||||||
<template>
|
<template functional>
|
||||||
<div class="media">
|
<div class="media" :id="'index_' + props.artist.name_sort.charAt(0).toUpperCase()">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_artist">
|
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||||
<h1 class="title is-6">{{ artist.name }}</h1>
|
<h1 class="title is-6">{{ props.artist.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details_modal = true">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from '@/components/ModalDialog'
|
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PartArtist',
|
name: 'ListItemArtist',
|
||||||
components: { ModalDialog },
|
props: ['artist']
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,97 +1,18 @@
|
|||||||
<template>
|
<template functional>
|
||||||
<div class="media">
|
<div class="media" :id="'index_' + props.genre.name.charAt(0).toUpperCase()">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_genre">
|
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||||
<h1 class="title is-6">{{ genre.name }}</h1>
|
<h1 class="title is-6">{{ props.genre.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details_modal = true">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from '@/components/ModalDialog'
|
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PartGenre',
|
name: 'ListItemGenre',
|
||||||
components: { ModalDialog },
|
props: [ 'genre' ]
|
||||||
|
|
||||||
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 } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,85 +1,18 @@
|
|||||||
<template>
|
<template functional>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_playlist">
|
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||||
<h1 class="title is-6">{{ playlist.name }}</h1>
|
<h1 class="title is-6">{{ props.playlist.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details_modal = true">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from '@/components/ModalDialog'
|
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PartPlaylist',
|
name: 'ListItemPlaylist',
|
||||||
components: { ModalDialog },
|
props: ['playlist']
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media" v-if="is_next || !show_only_next_items">
|
<div class="media" v-if="is_next || !show_only_next_items">
|
||||||
<!---->
|
|
||||||
<div class="media-left" v-if="edit_mode">
|
<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>
|
<span class="icon has-text-grey fd-is-movable handle"><i class="mdi mdi-drag-horizontal mdi-18px"></i></span>
|
||||||
</div>
|
</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>
|
<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>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a v-on:click="remove" v-if="item.id !== state.item_id && edit_mode">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from '@/components/ModalDialog'
|
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PartQueueItem',
|
name: 'ListItemQueueItem',
|
||||||
components: { ModalDialog },
|
|
||||||
|
|
||||||
props: ['item', 'position', 'current_position', 'show_only_next_items', 'edit_mode'],
|
props: ['item', 'position', 'current_position', 'show_only_next_items', 'edit_mode'],
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show_details_modal: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
state () {
|
state () {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
@ -100,13 +33,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
remove: function () {
|
|
||||||
this.show_details_modal = false
|
|
||||||
webapi.queue_remove(this.item.id)
|
|
||||||
},
|
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
this.show_details_modal = false
|
|
||||||
webapi.player_play({ 'item_id': this.item.id })
|
webapi.player_play({ 'item_id': this.item.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,142 +1,20 @@
|
|||||||
<template>
|
<template functional>
|
||||||
<div class="media">
|
<div class="media" :id="'index_' + props.track.title_sort.charAt(0).toUpperCase()">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="play">
|
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||||
<h1 class="title is-6">{{ track.title }}</h1>
|
<h1 class="title is-6">{{ props.track.title }}</h1>
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ track.artist }}</b></h2>
|
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.track.artist }}</b></h2>
|
||||||
<h2 class="subtitle is-7 has-text-grey">{{ track.album }}</h2>
|
<h2 class="subtitle is-7 has-text-grey">{{ props.track.album }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details_modal = true">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from '@/components/ModalDialog'
|
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PartTrack',
|
name: 'ListItemTrack',
|
||||||
components: { ModalDialog },
|
props: ['track']
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
113
web-src/src/components/ModalDialogAlbum.vue
Normal file
113
web-src/src/components/ModalDialogAlbum.vue
Normal file
@ -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>
|
74
web-src/src/components/ModalDialogArtist.vue
Normal file
74
web-src/src/components/ModalDialogArtist.vue
Normal file
@ -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>
|
70
web-src/src/components/ModalDialogGenre.vue
Normal file
70
web-src/src/components/ModalDialogGenre.vue
Normal file
@ -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>
|
70
web-src/src/components/ModalDialogPlaylist.vue
Normal file
70
web-src/src/components/ModalDialogPlaylist.vue
Normal file
@ -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>
|
88
web-src/src/components/ModalDialogQueueItem.vue
Normal file
88
web-src/src/components/ModalDialogQueueItem.vue
Normal file
@ -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>
|
134
web-src/src/components/ModalDialogTrack.vue
Normal file
134
web-src/src/components/ModalDialogTrack.vue
Normal file
@ -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>
|
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artists[0].name }}</b></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SpotifyListItemAlbum',
|
name: 'SpotifyListItemAlbum',
|
||||||
|
|
||||||
props: ['album'],
|
props: ['album'],
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show_details_modal: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
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 () {
|
open_album: function () {
|
||||||
this.$router.push({ path: '/music/spotify/albums/' + this.album.id })
|
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>
|
<h1 class="title is-6">{{ artist.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SpotifyListItemArtist',
|
name: 'SpotifyListItemArtist',
|
||||||
|
|
||||||
props: ['artist'],
|
props: ['artist'],
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show_details_modal: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
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 () {
|
open_artist: function () {
|
||||||
this.show_details_modal = false
|
|
||||||
this.$router.push({ path: '/music/spotify/artists/' + this.artist.id })
|
this.$router.push({ path: '/music/spotify/artists/' + this.artist.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,97 +5,18 @@
|
|||||||
<h2 class="subtitle is-7">{{ playlist.owner.display_name }}</h2>
|
<h2 class="subtitle is-7">{{ playlist.owner.display_name }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import webapi from '@/webapi'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SpotifyListItemPlaylist',
|
name: 'SpotifyListItemPlaylist',
|
||||||
|
|
||||||
props: ['playlist'],
|
props: ['playlist'],
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show_details_modal: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
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 () {
|
open_playlist: function () {
|
||||||
this.show_details_modal = false
|
|
||||||
this.$router.push({ path: '/music/spotify/playlists/' + this.playlist.id })
|
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>
|
<h2 class="subtitle is-7 has-text-grey"><b>{{ track.artists[0].name }}</b></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click="show_details">
|
<slot name="actions"></slot>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -75,48 +18,9 @@ export default {
|
|||||||
|
|
||||||
props: ['track', 'position', 'album', 'context_uri'],
|
props: ['track', 'position', 'album', 'context_uri'],
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show_details_modal: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
this.show_details_modal = false
|
|
||||||
webapi.player_play_uri(this.context_uri, false, this.position)
|
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 })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
107
web-src/src/components/SpotifyModalDialogAlbum.vue
Normal file
107
web-src/src/components/SpotifyModalDialogAlbum.vue
Normal file
@ -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>
|
73
web-src/src/components/SpotifyModalDialogArtist.vue
Normal file
73
web-src/src/components/SpotifyModalDialogArtist.vue
Normal file
@ -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>
|
77
web-src/src/components/SpotifyModalDialogPlaylist.vue
Normal file
77
web-src/src/components/SpotifyModalDialogPlaylist.vue
Normal file
@ -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>
|
96
web-src/src/components/SpotifyModalDialogTrack.vue
Normal file
96
web-src/src/components/SpotifyModalDialogTrack.vue
Normal file
@ -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>
|
<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="container">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
|
@ -38,11 +38,20 @@ a.navbar-item {
|
|||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fd-remove-padding-bottom {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.fd-has-padding-left-right {
|
.fd-has-padding-left-right {
|
||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
padding-right: 24px;
|
padding-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fd-is-square .button {
|
||||||
|
height: 27px;
|
||||||
|
width: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
.fd-is-text-clipped {
|
.fd-is-text-clipped {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -65,7 +74,17 @@ a.navbar-item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fd-tabs-section {
|
.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 {
|
.fd-progress-bar {
|
||||||
@ -131,3 +150,7 @@ a.navbar-item {
|
|||||||
max-height: calc(100vh - 200px);
|
max-height: calc(100vh - 200px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.fd-modal-card .card {
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
@ -18,7 +18,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -27,6 +34,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const albumData = {
|
const albumData = {
|
||||||
@ -46,12 +54,15 @@ const albumData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAlbum',
|
name: 'PageAlbum',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||||
components: { ContentWithHeading, ListItemTrack },
|
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
tracks: []
|
tracks: [],
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -63,6 +74,15 @@ export default {
|
|||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.album.uri, true)
|
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>
|
<tabs-music></tabs-music>
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
|
<template slot="options">
|
||||||
|
<index-button-list :index="index_list"></index-button-list>
|
||||||
|
</template>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<p class="title is-4">Albums</p>
|
<p class="title is-4">Albums</p>
|
||||||
<p class="heading">{{ albums.total }} albums</p>
|
<p class="heading">{{ albums.total }} albums</p>
|
||||||
@ -16,7 +19,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -26,7 +40,9 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
|
import IndexButtonList from '@/components/IndexButtonList'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
@ -37,17 +53,24 @@ const albumsData = {
|
|||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.albums = response.data
|
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 {
|
export default {
|
||||||
name: 'PageAlbums',
|
name: 'PageAlbums',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum },
|
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemAlbum, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
albums: {}
|
albums: { items: [] },
|
||||||
|
index_list: [],
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_album: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -60,6 +83,23 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
update_hide_singles: function (e) {
|
update_hide_singles: function (e) {
|
||||||
this.$store.commit(types.HIDE_SINGLES, !this.hide_singles)
|
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>
|
||||||
<template slot="content">
|
<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>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -19,6 +26,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const artistData = {
|
const artistData = {
|
||||||
@ -38,12 +46,15 @@ const artistData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageArtist',
|
name: 'PageArtist',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(artistData) ],
|
mixins: [ LoadDataBeforeEnterMixin(artistData) ],
|
||||||
components: { ContentWithHeading, ListItemAlbum },
|
components: { ContentWithHeading, ListItemAlbum, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
artist: {},
|
artist: {},
|
||||||
albums: {}
|
albums: {},
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_album: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -54,6 +65,15 @@ export default {
|
|||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.albums.items.map(a => a.uri).join(','), true)
|
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>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
|
<template slot="options">
|
||||||
|
<index-button-list :index="index_list"></index-button-list>
|
||||||
|
</template>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<p class="title is-4">{{ artist.name }}</p>
|
<p class="title is-4">{{ artist.name }}</p>
|
||||||
</template>
|
</template>
|
||||||
@ -11,7 +14,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -20,7 +30,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
|
import IndexButtonList from '@/components/IndexButtonList'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const tracksData = {
|
const tracksData = {
|
||||||
@ -38,14 +50,24 @@ const tracksData = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageTracks',
|
name: 'PageArtistTracks',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(tracksData) ],
|
mixins: [ LoadDataBeforeEnterMixin(tracksData) ],
|
||||||
components: { ContentWithHeading, ListItemTrack },
|
components: { ContentWithHeading, ListItemTrack, IndexButtonList, ModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
artist: {},
|
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 () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.tracks.items.map(a => a.uri).join(','), true)
|
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>
|
<tabs-music></tabs-music>
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
|
<template slot="options">
|
||||||
|
<index-button-list :index="index_list"></index-button-list>
|
||||||
|
</template>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<p class="title is-4">Artists</p>
|
<p class="title is-4">Artists</p>
|
||||||
<p class="heading">{{ artists.total }} artists</p>
|
<p class="heading">{{ artists.total }} artists</p>
|
||||||
@ -16,7 +19,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -26,7 +40,9 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
|
import IndexButtonList from '@/components/IndexButtonList'
|
||||||
import ListItemArtist from '@/components/ListItemArtist'
|
import ListItemArtist from '@/components/ListItemArtist'
|
||||||
|
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
@ -43,23 +59,41 @@ const artistsData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageArtists',
|
name: 'PageArtists',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(artistsData) ],
|
mixins: [ LoadDataBeforeEnterMixin(artistsData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemArtist },
|
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemArtist, ModalDialogArtist },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
artists: {}
|
artists: { items: [] },
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_artist: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
hide_singles () {
|
hide_singles () {
|
||||||
return this.$store.state.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: {
|
methods: {
|
||||||
update_hide_singles: function (e) {
|
update_hide_singles: function (e) {
|
||||||
this.$store.commit(types.HIDE_SINGLES, !this.hide_singles)
|
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>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -23,6 +30,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const albumData = {
|
const albumData = {
|
||||||
@ -42,18 +50,30 @@ const albumData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAudiobook',
|
name: 'PageAudiobook',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||||
components: { ContentWithHeading, ListItemTrack },
|
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
tracks: []
|
tracks: [],
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.album.uri, false)
|
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>
|
<p class="heading">{{ albums.total }} audiobooks</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -16,6 +23,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const albumsData = {
|
const albumsData = {
|
||||||
@ -31,11 +39,25 @@ const albumsData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAudiobooks',
|
name: 'PageAudiobooks',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
||||||
components: { ContentWithHeading, ListItemAlbum },
|
components: { ContentWithHeading, ListItemAlbum, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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>
|
<p class="heading">albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
@ -27,7 +34,14 @@
|
|||||||
<p class="heading">tracks</p>
|
<p class="heading">tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
@ -46,6 +60,8 @@ import ContentWithHeading from '@/templates/ContentWithHeading'
|
|||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const browseData = {
|
const browseData = {
|
||||||
@ -65,18 +81,42 @@ const browseData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageBrowse',
|
name: 'PageBrowse',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum, ListItemTrack },
|
components: { ContentWithHeading, TabsMusic, ListItemAlbum, ListItemTrack, ModalDialogTrack, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
recently_added: {},
|
recently_added: {},
|
||||||
recently_played: {}
|
recently_played: {},
|
||||||
|
|
||||||
|
show_track_details_modal: false,
|
||||||
|
selected_track: {},
|
||||||
|
|
||||||
|
show_album_details_modal: false,
|
||||||
|
selected_album: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_browse: function (type) {
|
open_browse: function (type) {
|
||||||
this.$router.push({ path: '/music/browse/' + 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>
|
<p class="heading">albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -19,6 +26,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const browseData = {
|
const browseData = {
|
||||||
@ -38,11 +46,25 @@ const browseData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageBrowseType',
|
name: 'PageBrowseType',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemAlbum },
|
components: { ContentWithHeading, TabsMusic, ListItemAlbum, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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>
|
<p class="heading">tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -19,6 +26,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const browseData = {
|
const browseData = {
|
||||||
@ -38,11 +46,25 @@ const browseData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageBrowseType',
|
name: 'PageBrowseType',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemTrack },
|
components: { ContentWithHeading, TabsMusic, ListItemTrack, ModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<tabs-music></tabs-music>
|
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
|
<template slot="options">
|
||||||
|
<index-button-list :index="index_list"></index-button-list>
|
||||||
|
</template>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<p class="title is-4">{{ name }}</p>
|
<p class="title is-4">{{ name }}</p>
|
||||||
<p class="heading">{{ genreAlbums.total }} albums</p>
|
|
||||||
</template>
|
</template>
|
||||||
<template slot="heading-right">
|
<template slot="heading-right">
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
@ -13,7 +13,15 @@
|
|||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -23,7 +31,9 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
|
import IndexButtonList from '@/components/IndexButtonList'
|
||||||
import ListItemAlbums from '@/components/ListItemAlbum'
|
import ListItemAlbums from '@/components/ListItemAlbum'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const genreData = {
|
const genreData = {
|
||||||
@ -33,40 +43,49 @@ const genreData = {
|
|||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.name = vm.$route.params.genre
|
vm.name = vm.$route.params.genre
|
||||||
vm.genreAlbums = response.data.albums
|
vm.genre_albums = 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageGenre',
|
name: 'PageGenre',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(genreData) ],
|
mixins: [ LoadDataBeforeEnterMixin(genreData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemAlbums },
|
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemAlbums, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
genreAlbums: {},
|
genre_albums: { items: [] },
|
||||||
links: []
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_album: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
index_list () {
|
||||||
|
return [...new Set(this.genre_albums.items
|
||||||
|
.map(album => album.name.charAt(0).toUpperCase()))]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
open_tracks: function () {
|
||||||
|
this.show_details_modal = false
|
||||||
|
this.$router.push({ path: '/music/genres/' + this.name + '/tracks' })
|
||||||
|
},
|
||||||
|
|
||||||
play: function () {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
94
web-src/src/pages/PageGenreTracks.vue
Normal file
94
web-src/src/pages/PageGenreTracks.vue
Normal file
@ -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>
|
<tabs-music></tabs-music>
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
|
<template slot="options">
|
||||||
|
<index-button-list :index="index_list"></index-button-list>
|
||||||
|
</template>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<p class="title is-4">Genres</p>
|
<p class="title is-4">Genres</p>
|
||||||
<p class="heading">{{ genres.total }} genres</p>
|
<p class="heading">{{ genres.total }} genres</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -18,7 +28,9 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
|
import IndexButtonList from '@/components/IndexButtonList'
|
||||||
import ListItemGenre from '@/components/ListItemGenre'
|
import ListItemGenre from '@/components/ListItemGenre'
|
||||||
|
import ModalDialogGenre from '@/components/ModalDialogGenre'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const genresData = {
|
const genresData = {
|
||||||
@ -34,15 +46,33 @@ const genresData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageGenres',
|
name: 'PageGenres',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(genresData) ],
|
mixins: [ LoadDataBeforeEnterMixin(genresData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemGenre },
|
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemGenre, ModalDialogGenre },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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: {
|
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>
|
</script>
|
||||||
|
@ -14,7 +14,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-body fd-is-fullheight-body has-text-centered" v-show="artwork_visible">
|
<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>
|
||||||
<div class="hero-foot fd-has-padding-left-right">
|
<div class="hero-foot fd-has-padding-left-right">
|
||||||
<div class="container has-text-centered fd-has-margin-bottom">
|
<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>
|
<player-button-consume class="button is-medium is-light"></player-button-consume>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||||
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause'
|
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause'
|
||||||
import PlayerButtonNext from '@/components/PlayerButtonNext'
|
import PlayerButtonNext from '@/components/PlayerButtonNext'
|
||||||
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
|
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
|
||||||
@ -58,13 +68,16 @@ import * as types from '@/store/mutation_types'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageNowPlaying',
|
name: 'PageNowPlaying',
|
||||||
components: { PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat, RangeSlider },
|
components: { ModalDialogQueueItem, PlayerButtonPlayPause, PlayerButtonNext, PlayerButtonPrevious, PlayerButtonShuffle, PlayerButtonConsume, PlayerButtonRepeat, RangeSlider },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
item_progress_ms: 0,
|
item_progress_ms: 0,
|
||||||
interval_id: 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 () {
|
artwork_error: function () {
|
||||||
this.artwork_visible = false
|
this.artwork_visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
open_dialog: function (item) {
|
||||||
|
this.selected_item = item
|
||||||
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -10,7 +10,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ tracks.length }} tracks</p>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -19,6 +26,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const playlistData = {
|
const playlistData = {
|
||||||
@ -38,18 +46,30 @@ const playlistData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PagePlaylist',
|
name: 'PagePlaylist',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(playlistData) ],
|
mixins: [ LoadDataBeforeEnterMixin(playlistData) ],
|
||||||
components: { ContentWithHeading, ListItemTrack },
|
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
playlist: {},
|
playlist: {},
|
||||||
tracks: []
|
tracks: [],
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.playlist.uri, true)
|
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>
|
<p class="heading">{{ playlists.total }} playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -15,6 +22,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import ListItemPlaylist from '@/components/ListItemPlaylist'
|
import ListItemPlaylist from '@/components/ListItemPlaylist'
|
||||||
|
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const playlistsData = {
|
const playlistsData = {
|
||||||
@ -30,11 +38,25 @@ const playlistsData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PagePlaylists',
|
name: 'PagePlaylists',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(playlistsData) ],
|
mixins: [ LoadDataBeforeEnterMixin(playlistsData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, ListItemPlaylist },
|
components: { ContentWithHeading, TabsMusic, ListItemPlaylist, ModalDialogPlaylist },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -22,6 +29,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const albumData = {
|
const albumData = {
|
||||||
@ -41,18 +49,30 @@ const albumData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PagePodcast',
|
name: 'PagePodcast',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||||
components: { ContentWithHeading, ListItemTrack },
|
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
tracks: []
|
tracks: [],
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.album.uri, false)
|
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>
|
<p class="heading">{{ albums.total }} podcasts</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -16,6 +23,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const albumsData = {
|
const albumsData = {
|
||||||
@ -31,11 +39,25 @@ const albumsData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PagePodcasts',
|
name: 'PagePodcasts',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumsData) ],
|
||||||
components: { ContentWithHeading, ListItemAlbum },
|
components: { ContentWithHeading, ListItemAlbum, ModalDialogAlbum },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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"
|
:key="item.id" :item="item" :position="index"
|
||||||
:current_position="current_position"
|
:current_position="current_position"
|
||||||
:show_only_next_items="show_only_next_items"
|
: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>
|
</draggable>
|
||||||
|
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -49,17 +59,21 @@
|
|||||||
<script>
|
<script>
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import ListItemQueueItem from '@/components/ListItemQueueItem'
|
import ListItemQueueItem from '@/components/ListItemQueueItem'
|
||||||
|
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageQueue',
|
name: 'PageQueue',
|
||||||
components: { ContentWithHeading, ListItemQueueItem, draggable },
|
components: { ContentWithHeading, ListItemQueueItem, draggable, ModalDialogQueueItem },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
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)
|
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) {
|
move_item: function (e) {
|
||||||
var oldPosition = !this.show_only_next_items ? e.oldIndex : e.oldIndex + this.current_position
|
var oldPosition = !this.show_only_next_items ? e.oldIndex : e.oldIndex + this.current_position
|
||||||
var item = this.queue_items[oldPosition]
|
var item = this.queue_items[oldPosition]
|
||||||
@ -99,6 +117,11 @@ export default {
|
|||||||
if (newPosition !== oldPosition) {
|
if (newPosition !== oldPosition) {
|
||||||
webapi.queue_move(item.id, newPosition)
|
webapi.queue_move(item.id, newPosition)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
open_dialog: function (item) {
|
||||||
|
this.selected_item = item
|
||||||
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- Search field + recent searches -->
|
<!-- Search field + recent searches -->
|
||||||
<section class="section fd-tabs-section">
|
<section class="section fd-remove-padding-bottom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
@ -31,7 +31,14 @@
|
|||||||
<p class="title is-4">Tracks</p>
|
<p class="title is-4">Tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_tracks_button" class="level">
|
<nav v-if="show_all_tracks_button" class="level">
|
||||||
@ -49,7 +56,14 @@
|
|||||||
<p class="title is-4">Artists</p>
|
<p class="title is-4">Artists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_artists_button" class="level">
|
<nav v-if="show_all_artists_button" class="level">
|
||||||
@ -67,7 +81,14 @@
|
|||||||
<p class="title is-4">Albums</p>
|
<p class="title is-4">Albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_albums_button" class="level">
|
<nav v-if="show_all_albums_button" class="level">
|
||||||
@ -85,7 +106,14 @@
|
|||||||
<p class="title is-4">Playlists</p>
|
<p class="title is-4">Playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_playlists_button" class="level">
|
<nav v-if="show_all_playlists_button" class="level">
|
||||||
@ -106,12 +134,16 @@ import ListItemTrack from '@/components/ListItemTrack'
|
|||||||
import ListItemArtist from '@/components/ListItemArtist'
|
import ListItemArtist from '@/components/ListItemArtist'
|
||||||
import ListItemAlbum from '@/components/ListItemAlbum'
|
import ListItemAlbum from '@/components/ListItemAlbum'
|
||||||
import ListItemPlaylist from '@/components/ListItemPlaylist'
|
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 webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageSearch',
|
name: 'PageSearch',
|
||||||
components: { ContentWithHeading, TabsSearch, ListItemTrack, ListItemArtist, ListItemAlbum, ListItemPlaylist },
|
components: { ContentWithHeading, TabsSearch, ListItemTrack, ListItemArtist, ListItemAlbum, ListItemPlaylist, ModalDialogTrack, ModalDialogAlbum, ModalDialogArtist, ModalDialogPlaylist },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@ -119,7 +151,19 @@ export default {
|
|||||||
tracks: { items: [], total: 0 },
|
tracks: { items: [], total: 0 },
|
||||||
artists: { items: [], total: 0 },
|
artists: { items: [], total: 0 },
|
||||||
albums: { 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) {
|
open_recent_search: function (query) {
|
||||||
this.search_query = query
|
this.search_query = query
|
||||||
this.new_search()
|
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>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ album.tracks.total }} tracks</p>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -20,6 +27,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||||
|
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
@ -39,11 +47,14 @@ const albumData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAlbum',
|
name: 'PageAlbum',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
mixins: [ LoadDataBeforeEnterMixin(albumData) ],
|
||||||
components: { ContentWithHeading, SpotifyListItemTrack },
|
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
album: {}
|
album: { artists: [{}], tracks: {} },
|
||||||
|
|
||||||
|
show_track_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -55,6 +66,11 @@ export default {
|
|||||||
play: function () {
|
play: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
webapi.player_play_uri(this.album.uri, true)
|
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>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ total }} albums</p>
|
<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>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -15,6 +22,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||||
|
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
import InfiniteLoading from 'vue-infinite-loading'
|
import InfiniteLoading from 'vue-infinite-loading'
|
||||||
@ -42,14 +50,17 @@ const artistData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'SpotifyPageArtist',
|
name: 'SpotifyPageArtist',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(artistData) ],
|
mixins: [ LoadDataBeforeEnterMixin(artistData) ],
|
||||||
components: { ContentWithHeading, SpotifyListItemAlbum, InfiniteLoading },
|
components: { ContentWithHeading, SpotifyListItemAlbum, SpotifyModalDialogAlbum, InfiniteLoading },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
artist: {},
|
artist: {},
|
||||||
albums: [],
|
albums: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
offset: 0
|
offset: 0,
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_album: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -73,6 +84,11 @@ export default {
|
|||||||
$state.complete()
|
$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>
|
<p class="title is-4">New Releases</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
@ -27,7 +34,14 @@
|
|||||||
<p class="title is-4">Featured Playlists</p>
|
<p class="title is-4">Featured Playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
@ -48,6 +62,8 @@ import ContentWithHeading from '@/templates/ContentWithHeading'
|
|||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
||||||
|
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||||
|
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
@ -77,7 +93,17 @@ const browseData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'SpotifyPageBrowse',
|
name: 'SpotifyPageBrowse',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
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: {
|
computed: {
|
||||||
new_releases () {
|
new_releases () {
|
||||||
@ -87,6 +113,18 @@ export default {
|
|||||||
featured_playlists () {
|
featured_playlists () {
|
||||||
return this.$store.state.spotify_featured_playlists.slice(0, 3)
|
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>
|
</script>
|
||||||
|
@ -7,7 +7,14 @@
|
|||||||
<p class="title is-4">Featured Playlists</p>
|
<p class="title is-4">Featured Playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -18,6 +25,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
||||||
|
import SpotifyModalDialogPlaylist from '@/components/SpotifyModalDialogPlaylist'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
@ -43,12 +51,26 @@ const browseData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'SpotifyPageBrowseFeaturedPlaylists',
|
name: 'SpotifyPageBrowseFeaturedPlaylists',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemPlaylist },
|
components: { ContentWithHeading, TabsMusic, SpotifyListItemPlaylist, SpotifyModalDialogPlaylist },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
show_playlist_details_modal: false,
|
||||||
|
selected_playlist: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
featured_playlists () {
|
featured_playlists () {
|
||||||
return this.$store.state.spotify_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>
|
</script>
|
||||||
|
@ -7,7 +7,14 @@
|
|||||||
<p class="title is-4">New Releases</p>
|
<p class="title is-4">New Releases</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -18,6 +25,7 @@ import { LoadDataBeforeEnterMixin } from './mixin'
|
|||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import TabsMusic from '@/components/TabsMusic'
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||||
|
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
@ -43,12 +51,26 @@ const browseData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'SpotifyPageBrowseNewReleases',
|
name: 'SpotifyPageBrowseNewReleases',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
mixins: [ LoadDataBeforeEnterMixin(browseData) ],
|
||||||
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum },
|
components: { ContentWithHeading, TabsMusic, SpotifyListItemAlbum, SpotifyModalDialogAlbum },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
show_album_details_modal: false,
|
||||||
|
selected_album: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
new_releases () {
|
new_releases () {
|
||||||
return this.$store.state.spotify_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>
|
</script>
|
||||||
|
@ -10,8 +10,15 @@
|
|||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ playlist.tracks.total }} tracks</p>
|
<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>
|
<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>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -20,6 +27,7 @@
|
|||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||||
|
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
@ -47,14 +55,17 @@ const playlistData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'SpotifyPagePlaylist',
|
name: 'SpotifyPagePlaylist',
|
||||||
mixins: [ LoadDataBeforeEnterMixin(playlistData) ],
|
mixins: [ LoadDataBeforeEnterMixin(playlistData) ],
|
||||||
components: { ContentWithHeading, SpotifyListItemTrack, InfiniteLoading },
|
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack, InfiniteLoading },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
playlist: {},
|
playlist: { tracks: {} },
|
||||||
tracks: [],
|
tracks: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
offset: 0
|
offset: 0,
|
||||||
|
|
||||||
|
show_track_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -83,6 +94,11 @@ export default {
|
|||||||
play: function () {
|
play: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
webapi.player_play_uri(this.playlist.uri, true)
|
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>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- Search field + recent searches -->
|
<!-- Search field + recent searches -->
|
||||||
<section class="section fd-tabs-section">
|
<section class="section fd-remove-padding-bottom">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
@ -31,8 +31,15 @@
|
|||||||
<p class="title is-4">Tracks</p>
|
<p class="title is-4">Tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_tracks_button" class="level">
|
<nav v-if="show_all_tracks_button" class="level">
|
||||||
@ -50,8 +57,15 @@
|
|||||||
<p class="title is-4">Artists</p>
|
<p class="title is-4">Artists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_artists_button" class="level">
|
<nav v-if="show_all_artists_button" class="level">
|
||||||
@ -69,8 +83,15 @@
|
|||||||
<p class="title is-4">Albums</p>
|
<p class="title is-4">Albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_albums_button" class="level">
|
<nav v-if="show_all_albums_button" class="level">
|
||||||
@ -88,8 +109,15 @@
|
|||||||
<p class="title is-4">Playlists</p>
|
<p class="title is-4">Playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<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>
|
<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>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<nav v-if="show_all_playlists_button" class="level">
|
<nav v-if="show_all_playlists_button" class="level">
|
||||||
@ -110,6 +138,10 @@ import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
|||||||
import SpotifyListItemArtist from '@/components/SpotifyListItemArtist'
|
import SpotifyListItemArtist from '@/components/SpotifyListItemArtist'
|
||||||
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
import SpotifyListItemAlbum from '@/components/SpotifyListItemAlbum'
|
||||||
import SpotifyListItemPlaylist from '@/components/SpotifyListItemPlaylist'
|
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 SpotifyWebApi from 'spotify-web-api-js'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import * as types from '@/store/mutation_types'
|
import * as types from '@/store/mutation_types'
|
||||||
@ -117,7 +149,7 @@ import InfiniteLoading from 'vue-infinite-loading'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SpotifyPageSearch',
|
name: 'SpotifyPageSearch',
|
||||||
components: { ContentWithHeading, TabsSearch, SpotifyListItemTrack, SpotifyListItemArtist, SpotifyListItemAlbum, SpotifyListItemPlaylist, InfiniteLoading },
|
components: { ContentWithHeading, TabsSearch, SpotifyListItemTrack, SpotifyListItemArtist, SpotifyListItemAlbum, SpotifyListItemPlaylist, SpotifyModalDialogTrack, SpotifyModalDialogArtist, SpotifyModalDialogAlbum, SpotifyModalDialogPlaylist, InfiniteLoading },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@ -128,7 +160,19 @@ export default {
|
|||||||
playlists: { items: [], total: 0 },
|
playlists: { items: [], total: 0 },
|
||||||
|
|
||||||
query: {},
|
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) {
|
open_recent_search: function (query) {
|
||||||
this.search_query = query
|
this.search_query = query
|
||||||
this.new_search()
|
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 PageAlbum from '@/pages/PageAlbum'
|
||||||
import PageGenres from '@/pages/PageGenres'
|
import PageGenres from '@/pages/PageGenres'
|
||||||
import PageGenre from '@/pages/PageGenre'
|
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 PagePodcasts from '@/pages/PagePodcasts'
|
||||||
import PagePodcast from '@/pages/PagePodcast'
|
import PagePodcast from '@/pages/PagePodcast'
|
||||||
import PageAudiobooks from '@/pages/PageAudiobooks'
|
import PageAudiobooks from '@/pages/PageAudiobooks'
|
||||||
@ -57,25 +58,25 @@ export const router = new VueRouter({
|
|||||||
path: '/music/browse',
|
path: '/music/browse',
|
||||||
name: 'Browse',
|
name: 'Browse',
|
||||||
component: PageBrowse,
|
component: PageBrowse,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/browse/recently_added',
|
path: '/music/browse/recently_added',
|
||||||
name: 'Browse Recently Added',
|
name: 'Browse Recently Added',
|
||||||
component: PageBrowseRecentlyAdded,
|
component: PageBrowseRecentlyAdded,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/browse/recently_played',
|
path: '/music/browse/recently_played',
|
||||||
name: 'Browse Recently Played',
|
name: 'Browse Recently Played',
|
||||||
component: PageBrowseRecentlyPlayed,
|
component: PageBrowseRecentlyPlayed,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/artists',
|
path: '/music/artists',
|
||||||
name: 'Artists',
|
name: 'Artists',
|
||||||
component: PageArtists,
|
component: PageArtists,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/artists/:artist_id',
|
path: '/music/artists/:artist_id',
|
||||||
@ -86,14 +87,14 @@ export const router = new VueRouter({
|
|||||||
{
|
{
|
||||||
path: '/music/artists/:artist_id/tracks',
|
path: '/music/artists/:artist_id/tracks',
|
||||||
name: 'Tracks',
|
name: 'Tracks',
|
||||||
component: PageTracks,
|
component: PageArtistTracks,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_index: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/albums',
|
path: '/music/albums',
|
||||||
name: 'Albums',
|
name: 'Albums',
|
||||||
component: PageAlbums,
|
component: PageAlbums,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/albums/:album_id',
|
path: '/music/albums/:album_id',
|
||||||
@ -105,13 +106,19 @@ export const router = new VueRouter({
|
|||||||
path: '/music/genres',
|
path: '/music/genres',
|
||||||
name: 'Genres',
|
name: 'Genres',
|
||||||
component: PageGenres,
|
component: PageGenres,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/genres/:genre',
|
path: '/music/genres/:genre',
|
||||||
name: 'Genre',
|
name: 'Genre',
|
||||||
component: PageGenre,
|
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',
|
path: '/podcasts',
|
||||||
@ -162,19 +169,19 @@ export const router = new VueRouter({
|
|||||||
path: '/music/spotify',
|
path: '/music/spotify',
|
||||||
name: 'Spotify',
|
name: 'Spotify',
|
||||||
component: SpotifyPageBrowse,
|
component: SpotifyPageBrowse,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/spotify/new-releases',
|
path: '/music/spotify/new-releases',
|
||||||
name: 'Spotify Browse New Releases',
|
name: 'Spotify Browse New Releases',
|
||||||
component: SpotifyPageBrowseNewReleases,
|
component: SpotifyPageBrowseNewReleases,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/spotify/featured-playlists',
|
path: '/music/spotify/featured-playlists',
|
||||||
name: 'Spotify Browse Featured Playlists',
|
name: 'Spotify Browse Featured Playlists',
|
||||||
component: SpotifyPageBrowseFeaturedPlaylists,
|
component: SpotifyPageBrowseFeaturedPlaylists,
|
||||||
meta: { show_progress: true }
|
meta: { show_progress: true, has_tabs: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/music/spotify/artists/:artist_id',
|
path: '/music/spotify/artists/:artist_id',
|
||||||
@ -201,11 +208,30 @@ export const router = new VueRouter({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
scrollBehavior (to, from, savedPosition) {
|
scrollBehavior (to, from, savedPosition) {
|
||||||
|
// console.log(to.path + '_' + from.path + '__' + to.hash + ' savedPosition:' + savedPosition)
|
||||||
if (savedPosition) {
|
if (savedPosition) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(savedPosition)
|
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 {
|
} else {
|
||||||
return { x: 0, y: 0 }
|
return { x: 0, y: 0 }
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="section">
|
<section class="section fd-content">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
<nav class="level">
|
<slot name="options"></slot>
|
||||||
|
<nav class="level" id="top">
|
||||||
<!-- Left side -->
|
<!-- Left side -->
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
<div class="level-item has-text-centered-mobile">
|
<div class="level-item has-text-centered-mobile">
|
||||||
|
@ -41,8 +41,13 @@ export default {
|
|||||||
return axios.put('/api/queue/items/' + itemId + '?new_position=' + newPosition)
|
return axios.put('/api/queue/items/' + itemId + '?new_position=' + newPosition)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add (uri) {
|
queue_add (uri, showNotification = true) {
|
||||||
return axios.post('/api/queue/items/add?uris=' + uri)
|
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) {
|
queue_add_next (uri) {
|
||||||
@ -50,17 +55,20 @@ export default {
|
|||||||
if (store.getters.now_playing && store.getters.now_playing.id) {
|
if (store.getters.now_playing && store.getters.now_playing.id) {
|
||||||
position = store.getters.now_playing.position + 1
|
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 () {
|
player_status () {
|
||||||
return axios.get('/api/player')
|
return axios.get('/api/player')
|
||||||
},
|
},
|
||||||
|
|
||||||
player_play_uri (uris, shuffle, position = 0) {
|
player_play_uri (uris, shuffle, position = undefined) {
|
||||||
return this.queue_clear().then(() =>
|
return this.queue_clear().then(() =>
|
||||||
this.player_shuffle(shuffle).then(() =>
|
this.player_shuffle(shuffle).then(() =>
|
||||||
this.queue_add(uris).then(() =>
|
this.queue_add(uris, false).then(() =>
|
||||||
this.player_play({ 'position': position })
|
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) {
|
library_artist_tracks (artist) {
|
||||||
if (artist) {
|
if (artist) {
|
||||||
var artistParams = {
|
var artistParams = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user