mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-23 12:05:47 -04:00
[web] Format web sources with prettier and run fix linting errors
This commit is contained in:
parent
d7f1c13585
commit
c78f861f45
@ -1,13 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true
|
||||||
},
|
},
|
||||||
extends: [
|
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', 'prettier'],
|
||||||
'eslint:recommended',
|
rules: {
|
||||||
'plugin:vue/vue3-recommended',
|
// override/add rules settings here, such as:
|
||||||
],
|
// 'vue/no-unused-vars': 'error'
|
||||||
rules: {
|
}
|
||||||
// override/add rules settings here, such as:
|
}
|
||||||
// 'vue/no-unused-vars': 'error'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
- Vue Dev Tools required in version 6 (currently only released as beta versions): <https://devtools.vuejs.org/guide/installation.html#beta>
|
- Vue Dev Tools required in version 6 (currently only released as beta versions): <https://devtools.vuejs.org/guide/installation.html#beta>
|
||||||
|
|
||||||
- [ ] vite does not support env vars in `vite.config.js` from `.env` files
|
- [ ] vite does not support env vars in `vite.config.js` from `.env` files
|
||||||
|
|
||||||
- <https://stackoverflow.com/questions/66389043/how-can-i-use-vite-env-variables-in-vite-config-js>
|
- <https://stackoverflow.com/questions/66389043/how-can-i-use-vite-env-variables-in-vite-config-js>
|
||||||
- <https://github.com/vitejs/vite/issues/1930>
|
- <https://github.com/vitejs/vite/issues/1930>
|
||||||
|
|
||||||
@ -13,6 +14,7 @@
|
|||||||
- [x] Update dialog is missing scan options
|
- [x] Update dialog is missing scan options
|
||||||
|
|
||||||
- [ ] Performance with huge artists/albums/tracks list (no functional template supported any more)
|
- [ ] Performance with huge artists/albums/tracks list (no functional template supported any more)
|
||||||
|
|
||||||
- [ ] Do not reload data, if using the index-nav
|
- [ ] Do not reload data, if using the index-nav
|
||||||
- [x] PageAlbums
|
- [x] PageAlbums
|
||||||
- [ ] PageArtists
|
- [ ] PageArtists
|
||||||
@ -21,54 +23,64 @@
|
|||||||
- [ ] Evaluate virtual scroller <https://github.com/Akryum/vue-virtual-scroller/tree/next/packages/vue-virtual-scroller>
|
- [ ] Evaluate virtual scroller <https://github.com/Akryum/vue-virtual-scroller/tree/next/packages/vue-virtual-scroller>
|
||||||
|
|
||||||
- [x] JS error on Podacst page
|
- [x] JS error on Podacst page
|
||||||
|
|
||||||
- Problem caused by the Slider component
|
- Problem caused by the Slider component
|
||||||
- Replace with plain html
|
- Replace with plain html
|
||||||
|
|
||||||
- [ ] vue-router scroll-behavior
|
- [ ] vue-router scroll-behavior
|
||||||
|
|
||||||
- [x] Index list not always hidden
|
- [x] Index list not always hidden
|
||||||
- [x] Check transitions
|
- [x] Check transitions
|
||||||
- [ ] Page display is "jumpy"
|
- [ ] Page display is "jumpy"
|
||||||
- Workaround is removing the page transition effect
|
- Workaround is removing the page transition effect
|
||||||
|
|
||||||
- [x] Index navigation "scroll up/down" button does not scroll down, if index is visible
|
- [x] Index navigation "scroll up/down" button does not scroll down, if index is visible
|
||||||
|
|
||||||
- [x] Use native intersection observer solves it in desktop mode
|
- [x] Use native intersection observer solves it in desktop mode
|
||||||
- [x] Mobile view still broken
|
- [x] Mobile view still broken
|
||||||
|
|
||||||
- [x] Update to latest dependency versions (vite, vue, etc.)
|
- [x] Update to latest dependency versions (vite, vue, etc.)
|
||||||
|
|
||||||
- [x] Index navigation is broken (jump to "A")
|
- [x] Index navigation is broken (jump to "A")
|
||||||
|
|
||||||
- Change in `$router.push` syntax, hash has to be passed as a separate parameter instead of as part of the path
|
- Change in `$router.push` syntax, hash has to be passed as a separate parameter instead of as part of the path
|
||||||
|
|
||||||
- [x] `vue-range-slider` is not compatible with vue3
|
- [x] `vue-range-slider` is not compatible with vue3
|
||||||
|
|
||||||
- replacement option: <https://github.com/vueform/slider>
|
- replacement option: <https://github.com/vueform/slider>
|
||||||
- [x] `@vueform/slider` for volume control
|
- [x] `@vueform/slider` for volume control
|
||||||
- [x] track progress (now playing)
|
- [x] track progress (now playing)
|
||||||
- [x] track progress (podcasts)
|
- [x] track progress (podcasts)
|
||||||
|
|
||||||
- [x] vue-router does not support navigation guards in mixins: <https://github.com/vuejs/vue-router-next/issues/454>
|
- [x] vue-router does not support navigation guards in mixins: <https://github.com/vuejs/vue-router-next/issues/454>
|
||||||
|
|
||||||
- replace mixin with composition api? <https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards>
|
- replace mixin with composition api? <https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards>
|
||||||
- Copied nav guards into each component
|
- Copied nav guards into each component
|
||||||
|
|
||||||
- [x] vue-router link does not support `tag` and `active-class` properties: <https://next.router.vuejs.org/guide/migration/index.html#removal-of-event-and-tag-props-in-router-link>
|
- [x] vue-router link does not support `tag` and `active-class` properties: <https://next.router.vuejs.org/guide/migration/index.html#removal-of-event-and-tag-props-in-router-link>
|
||||||
|
|
||||||
- [x] `vue-tiny-lazyload-img` does not support Vue 3
|
- [x] `vue-tiny-lazyload-img` does not support Vue 3
|
||||||
|
|
||||||
- No sign of interesst to add support <https://github.com/mazipan/vue-tiny-lazyload-img>
|
- No sign of interesst to add support <https://github.com/mazipan/vue-tiny-lazyload-img>
|
||||||
- `v-lazy-image` (<https://github.com/alexjoverm/v-lazy-image>) seems to be supported and popular
|
- `v-lazy-image` (<https://github.com/alexjoverm/v-lazy-image>) seems to be supported and popular
|
||||||
- Works as a component instead of a directive
|
- Works as a component instead of a directive
|
||||||
- __DOES NOT__ have a good error handling, if the (remote) image does not exist
|
- **DOES NOT** have a good error handling, if the (remote) image does not exist
|
||||||
- `vue3-lazyload` (<https://github.com/murongg/vue3-lazyload>)
|
- `vue3-lazyload` (<https://github.com/murongg/vue3-lazyload>)
|
||||||
- Works as a directive
|
- Works as a directive
|
||||||
- Easy replacement for `vue-tiny-lazyload-img`
|
- Easy replacement for `vue-tiny-lazyload-img`
|
||||||
|
|
||||||
- [x] Top margin in pages is wrong (related to vue-router scroll behavior changes)
|
- [x] Top margin in pages is wrong (related to vue-router scroll behavior changes)
|
||||||
|
|
||||||
- Solved by adding the correct margin to take the top navbar (and where shown the tabs) into account
|
- Solved by adding the correct margin to take the top navbar (and where shown the tabs) into account
|
||||||
|
|
||||||
- [x] Mobile view seems to be broken
|
- [x] Mobile view seems to be broken
|
||||||
|
|
||||||
- Looks like the cause of this was the broken router-link in bulma tabs component
|
- Looks like the cause of this was the broken router-link in bulma tabs component
|
||||||
|
|
||||||
- [x] Changing sort option (artist albums view) does not work
|
- [x] Changing sort option (artist albums view) does not work
|
||||||
|
|
||||||
- [x] Replace unmaintained `vue-infinite-loading` dependency
|
- [x] Replace unmaintained `vue-infinite-loading` dependency
|
||||||
|
|
||||||
- Replace with `@ts-pro/vue-eternal-loading`: <https://github.com/ts-pro/vue-eternal-loading>
|
- Replace with `@ts-pro/vue-eternal-loading`: <https://github.com/ts-pro/vue-eternal-loading>
|
||||||
|
|
||||||
- [x] Replace `bulma-switch` with `@vueform/toggle`?
|
- [x] Replace `bulma-switch` with `@vueform/toggle`?
|
||||||
|
@ -3,13 +3,17 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?ver2.0">
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
rel="apple-touch-icon"
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
sizes="180x180"
|
||||||
<link rel="manifest" href="/site.webmanifest">
|
href="/apple-touch-icon.png?ver2.0"
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
/>
|
||||||
<meta name="msapplication-TileColor" content="#da532c">
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||||
<meta name="theme-color" content="#ffffff">
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>OwnTone</title>
|
<title>OwnTone</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "OwnTone",
|
"name": "OwnTone",
|
||||||
"short_name": "OwnTone",
|
"short_name": "OwnTone",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "/android-chrome-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-512x512.png",
|
"src": "/android-chrome-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#ffffff",
|
"theme_color": "#ffffff",
|
||||||
"background_color": "#ffffff",
|
"background_color": "#ffffff",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,21 @@
|
|||||||
<component :is="Component" class="fd-page" />
|
<component :is="Component" class="fd-page" />
|
||||||
</router-view>
|
</router-view>
|
||||||
|
|
||||||
<modal-dialog-remote-pairing :show="pairing_active" @close="pairing_active = false" />
|
<modal-dialog-remote-pairing
|
||||||
|
:show="pairing_active"
|
||||||
|
@close="pairing_active = false"
|
||||||
|
/>
|
||||||
<modal-dialog-update
|
<modal-dialog-update
|
||||||
:show="show_update_dialog"
|
:show="show_update_dialog"
|
||||||
@close="show_update_dialog = false" />
|
@close="show_update_dialog = false"
|
||||||
|
/>
|
||||||
<notifications v-show="!show_burger_menu" />
|
<notifications v-show="!show_burger_menu" />
|
||||||
<navbar-bottom />
|
<navbar-bottom />
|
||||||
<div class="fd-overlay-fullscreen" v-show="show_burger_menu || show_player_menu"
|
<div
|
||||||
@click="show_burger_menu = show_player_menu = false"></div>
|
v-show="show_burger_menu || show_player_menu"
|
||||||
|
class="fd-overlay-fullscreen"
|
||||||
|
@click="show_burger_menu = show_player_menu = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -30,10 +37,15 @@ import moment from 'moment'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: { NavbarTop, NavbarBottom, Notifications, ModalDialogRemotePairing, ModalDialogUpdate },
|
components: {
|
||||||
template: '<App/>',
|
NavbarTop,
|
||||||
|
NavbarBottom,
|
||||||
|
Notifications,
|
||||||
|
ModalDialogRemotePairing,
|
||||||
|
ModalDialogUpdate
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
token_timer_id: 0,
|
token_timer_id: 0,
|
||||||
reconnect_attempts: 0,
|
reconnect_attempts: 0,
|
||||||
@ -43,31 +55,40 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
show_burger_menu: {
|
show_burger_menu: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_burger_menu
|
return this.$store.state.show_burger_menu
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
show_player_menu: {
|
show_player_menu: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_player_menu
|
return this.$store.state.show_player_menu
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
show_update_dialog: {
|
show_update_dialog: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_update_dialog
|
return this.$store.state.show_update_dialog
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_UPDATE_DIALOG, value)
|
this.$store.commit(types.SHOW_UPDATE_DIALOG, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
show_burger_menu() {
|
||||||
|
this.update_is_clipped()
|
||||||
|
},
|
||||||
|
show_player_menu() {
|
||||||
|
this.update_is_clipped()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
created: function () {
|
created: function () {
|
||||||
moment.locale(navigator.language)
|
moment.locale(navigator.language)
|
||||||
this.connect()
|
this.connect()
|
||||||
@ -97,23 +118,38 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
connect: function () {
|
connect: function () {
|
||||||
this.$store.dispatch('add_notification', { text: 'Connecting to OwnTone server', type: 'info', topic: 'connection', timeout: 2000 })
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'Connecting to OwnTone server',
|
||||||
webapi.config().then(({ data }) => {
|
type: 'info',
|
||||||
this.$store.commit(types.UPDATE_CONFIG, data)
|
topic: 'connection',
|
||||||
this.$store.commit(types.HIDE_SINGLES, data.hide_singles)
|
timeout: 2000
|
||||||
document.title = data.library_name
|
|
||||||
|
|
||||||
this.open_ws()
|
|
||||||
this.$Progress.finish()
|
|
||||||
}).catch(() => {
|
|
||||||
this.$store.dispatch('add_notification', { text: 'Failed to connect to OwnTone server', type: 'danger', topic: 'connection' })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
webapi
|
||||||
|
.config()
|
||||||
|
.then(({ data }) => {
|
||||||
|
this.$store.commit(types.UPDATE_CONFIG, data)
|
||||||
|
this.$store.commit(types.HIDE_SINGLES, data.hide_singles)
|
||||||
|
document.title = data.library_name
|
||||||
|
|
||||||
|
this.open_ws()
|
||||||
|
this.$Progress.finish()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'Failed to connect to OwnTone server',
|
||||||
|
type: 'danger',
|
||||||
|
topic: 'connection'
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_ws: function () {
|
open_ws: function () {
|
||||||
if (this.$store.state.config.websocket_port <= 0) {
|
if (this.$store.state.config.websocket_port <= 0) {
|
||||||
this.$store.dispatch('add_notification', { text: 'Missing websocket port', type: 'danger' })
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'Missing websocket port',
|
||||||
|
type: 'danger'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,22 +160,47 @@ export default {
|
|||||||
protocol = 'wss://'
|
protocol = 'wss://'
|
||||||
}
|
}
|
||||||
|
|
||||||
let wsUrl = protocol + window.location.hostname + ':' + vm.$store.state.config.websocket_port
|
let wsUrl =
|
||||||
if (import.meta.env.NODE_ENV === 'development' && import.meta.env.VUE_APP_WEBSOCKET_SERVER) {
|
protocol +
|
||||||
|
window.location.hostname +
|
||||||
|
':' +
|
||||||
|
vm.$store.state.config.websocket_port
|
||||||
|
if (
|
||||||
|
import.meta.env.NODE_ENV === 'development' &&
|
||||||
|
import.meta.env.VUE_APP_WEBSOCKET_SERVER
|
||||||
|
) {
|
||||||
// If we are running in the development server, use the websocket url configured in .env.development
|
// If we are running in the development server, use the websocket url configured in .env.development
|
||||||
wsUrl = import.meta.env.VUE_APP_WEBSOCKET_SERVER
|
wsUrl = import.meta.env.VUE_APP_WEBSOCKET_SERVER
|
||||||
}
|
}
|
||||||
|
|
||||||
const socket = new ReconnectingWebSocket(
|
const socket = new ReconnectingWebSocket(wsUrl, 'notify', {
|
||||||
wsUrl,
|
reconnectInterval: 3000
|
||||||
'notify',
|
})
|
||||||
{ reconnectInterval: 3000 }
|
|
||||||
)
|
|
||||||
|
|
||||||
socket.onopen = function () {
|
socket.onopen = function () {
|
||||||
vm.$store.dispatch('add_notification', { text: 'Connection to server established', type: 'primary', topic: 'connection', timeout: 2000 })
|
vm.$store.dispatch('add_notification', {
|
||||||
|
text: 'Connection to server established',
|
||||||
|
type: 'primary',
|
||||||
|
topic: 'connection',
|
||||||
|
timeout: 2000
|
||||||
|
})
|
||||||
vm.reconnect_attempts = 0
|
vm.reconnect_attempts = 0
|
||||||
socket.send(JSON.stringify({ notify: ['update', 'database', 'player', 'options', 'outputs', 'volume', 'queue', 'spotify', 'lastfm', 'pairing'] }))
|
socket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
notify: [
|
||||||
|
'update',
|
||||||
|
'database',
|
||||||
|
'player',
|
||||||
|
'options',
|
||||||
|
'outputs',
|
||||||
|
'volume',
|
||||||
|
'queue',
|
||||||
|
'spotify',
|
||||||
|
'lastfm',
|
||||||
|
'pairing'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
vm.update_outputs()
|
vm.update_outputs()
|
||||||
vm.update_player_status()
|
vm.update_player_status()
|
||||||
@ -155,14 +216,26 @@ export default {
|
|||||||
}
|
}
|
||||||
socket.onerror = function () {
|
socket.onerror = function () {
|
||||||
vm.reconnect_attempts++
|
vm.reconnect_attempts++
|
||||||
vm.$store.dispatch('add_notification', { text: 'Connection lost. Reconnecting ... (' + vm.reconnect_attempts + ')', type: 'danger', topic: 'connection' })
|
vm.$store.dispatch('add_notification', {
|
||||||
|
text:
|
||||||
|
'Connection lost. Reconnecting ... (' + vm.reconnect_attempts + ')',
|
||||||
|
type: 'danger',
|
||||||
|
topic: 'connection'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
socket.onmessage = function (response) {
|
socket.onmessage = function (response) {
|
||||||
const data = JSON.parse(response.data)
|
const data = JSON.parse(response.data)
|
||||||
if (data.notify.includes('update') || data.notify.includes('database')) {
|
if (
|
||||||
|
data.notify.includes('update') ||
|
||||||
|
data.notify.includes('database')
|
||||||
|
) {
|
||||||
vm.update_library_stats()
|
vm.update_library_stats()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('player') || data.notify.includes('options') || data.notify.includes('volume')) {
|
if (
|
||||||
|
data.notify.includes('player') ||
|
||||||
|
data.notify.includes('options') ||
|
||||||
|
data.notify.includes('volume')
|
||||||
|
) {
|
||||||
vm.update_player_status()
|
vm.update_player_status()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('outputs') || data.notify.includes('volume')) {
|
if (data.notify.includes('outputs') || data.notify.includes('volume')) {
|
||||||
@ -237,7 +310,10 @@ export default {
|
|||||||
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -257,17 +333,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
template: '<App/>'
|
||||||
watch: {
|
|
||||||
'show_burger_menu' () {
|
|
||||||
this.update_is_clipped()
|
|
||||||
},
|
|
||||||
'show_player_menu' () {
|
|
||||||
this.update_is_clipped()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -9,7 +9,7 @@ export default {
|
|||||||
_gain: null,
|
_gain: null,
|
||||||
|
|
||||||
// setup audio routing
|
// setup audio routing
|
||||||
setupAudio () {
|
setupAudio() {
|
||||||
const AudioContext = window.AudioContext || window.webkitAudioContext
|
const AudioContext = window.AudioContext || window.webkitAudioContext
|
||||||
this._context = new AudioContext()
|
this._context = new AudioContext()
|
||||||
this._source = this._context.createMediaElementSource(this._audio)
|
this._source = this._context.createMediaElementSource(this._audio)
|
||||||
@ -18,26 +18,26 @@ export default {
|
|||||||
this._source.connect(this._gain)
|
this._source.connect(this._gain)
|
||||||
this._gain.connect(this._context.destination)
|
this._gain.connect(this._context.destination)
|
||||||
|
|
||||||
this._audio.addEventListener('canplaythrough', e => {
|
this._audio.addEventListener('canplaythrough', (e) => {
|
||||||
this._audio.play()
|
this._audio.play()
|
||||||
})
|
})
|
||||||
this._audio.addEventListener('canplay', e => {
|
this._audio.addEventListener('canplay', (e) => {
|
||||||
this._audio.play()
|
this._audio.play()
|
||||||
})
|
})
|
||||||
return this._audio
|
return this._audio
|
||||||
},
|
},
|
||||||
|
|
||||||
// set audio volume
|
// set audio volume
|
||||||
setVolume (volume) {
|
setVolume(volume) {
|
||||||
if (!this._gain) return
|
if (!this._gain) return
|
||||||
volume = parseFloat(volume) || 0.0
|
volume = parseFloat(volume) || 0.0
|
||||||
volume = (volume < 0) ? 0 : volume
|
volume = volume < 0 ? 0 : volume
|
||||||
volume = (volume > 1) ? 1 : volume
|
volume = volume > 1 ? 1 : volume
|
||||||
this._gain.gain.value = volume
|
this._gain.gain.value = volume
|
||||||
},
|
},
|
||||||
|
|
||||||
// play audio source url
|
// play audio source url
|
||||||
playSource (source) {
|
playSource(source) {
|
||||||
this.stopAudio()
|
this.stopAudio()
|
||||||
this._context.resume().then(() => {
|
this._context.resume().then(() => {
|
||||||
this._audio.src = String(source || '') + '?x=' + Date.now()
|
this._audio.src = String(source || '') + '?x=' + Date.now()
|
||||||
@ -47,9 +47,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// stop playing audio
|
// stop playing audio
|
||||||
stopAudio () {
|
stopAudio() {
|
||||||
try { this._audio.pause() } catch (e) {}
|
try {
|
||||||
try { this._audio.stop() } catch (e) {}
|
this._audio.pause()
|
||||||
try { this._audio.close() } catch (e) {}
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
this._audio.stop()
|
||||||
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
this._audio.close()
|
||||||
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<figure>
|
<figure>
|
||||||
<img v-lazy="{ src: artwork_url_with_size, lifecycle: lazy_lifecycle }"
|
<img
|
||||||
@click="$emit('click')">
|
v-lazy="{ src: artwork_url_with_size, lifecycle: lazy_lifecycle }"
|
||||||
|
@click="$emit('click')"
|
||||||
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -13,7 +15,7 @@ export default {
|
|||||||
name: 'CoverArtwork',
|
name: 'CoverArtwork',
|
||||||
props: ['artist', 'album', 'artwork_url', 'maxwidth', 'maxheight'],
|
props: ['artist', 'album', 'artwork_url', 'maxwidth', 'maxheight'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
width: 600,
|
width: 600,
|
||||||
height: 600,
|
height: 600,
|
||||||
@ -31,16 +33,20 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
artwork_url_with_size: function () {
|
artwork_url_with_size: function () {
|
||||||
if (this.maxwidth > 0 && this.maxheight > 0) {
|
if (this.maxwidth > 0 && this.maxheight > 0) {
|
||||||
return webapi.artwork_url_append_size_params(this.artwork_url, this.maxwidth, this.maxheight)
|
return webapi.artwork_url_append_size_params(
|
||||||
|
this.artwork_url,
|
||||||
|
this.maxwidth,
|
||||||
|
this.maxheight
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return webapi.artwork_url_append_size_params(this.artwork_url)
|
return webapi.artwork_url_append_size_params(this.artwork_url)
|
||||||
},
|
},
|
||||||
|
|
||||||
alt_text () {
|
alt_text() {
|
||||||
return this.artist + ' - ' + this.album
|
return this.artist + ' - ' + this.album
|
||||||
},
|
},
|
||||||
|
|
||||||
caption () {
|
caption() {
|
||||||
if (this.album) {
|
if (this.album) {
|
||||||
return this.album.substring(0, 2)
|
return this.album.substring(0, 2)
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dropdown" :class="{ 'is-active': is_active }" v-click-away="onClickOutside">
|
<div
|
||||||
|
v-click-away="onClickOutside"
|
||||||
|
class="dropdown"
|
||||||
|
:class="{ 'is-active': is_active }"
|
||||||
|
>
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu" @click="is_active = !is_active">
|
<button
|
||||||
|
class="button"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-controls="dropdown-menu"
|
||||||
|
@click="is_active = !is_active"
|
||||||
|
>
|
||||||
<span>{{ modelValue }}</span>
|
<span>{{ modelValue }}</span>
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<i class="mdi mdi-chevron-down" aria-hidden="true"></i>
|
<i class="mdi mdi-chevron-down" aria-hidden="true" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
<div id="dropdown-menu" class="dropdown-menu" role="menu">
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a class="dropdown-item"
|
<a
|
||||||
v-for="option in options" :key="option"
|
v-for="option in options"
|
||||||
:class="{'is-active': modelValue === option}"
|
:key="option"
|
||||||
@click="select(option)">
|
class="dropdown-item"
|
||||||
|
:class="{ 'is-active': modelValue === option }"
|
||||||
|
@click="select(option)"
|
||||||
|
>
|
||||||
{{ option }}
|
{{ option }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -28,18 +40,18 @@ export default {
|
|||||||
props: ['modelValue', 'options'],
|
props: ['modelValue', 'options'],
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
is_active: false
|
is_active: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onClickOutside (event) {
|
onClickOutside(event) {
|
||||||
this.is_active = false
|
this.is_active = false
|
||||||
},
|
},
|
||||||
|
|
||||||
select (option) {
|
select(option) {
|
||||||
this.is_active = false
|
this.is_active = false
|
||||||
this.$emit('update:modelValue', option)
|
this.$emit('update:modelValue', option)
|
||||||
}
|
}
|
||||||
@ -47,5 +59,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<nav class="buttons is-centered fd-is-square" style="margin-bottom: 16px;">
|
<nav class="buttons is-centered fd-is-square" style="margin-bottom: 16px">
|
||||||
<a v-for="char in filtered_index" :key="char" class="button is-small" @click="nav(char)">{{ char }}</a>
|
<a
|
||||||
|
v-for="char in filtered_index"
|
||||||
|
:key="char"
|
||||||
|
class="button is-small"
|
||||||
|
@click="nav(char)"
|
||||||
|
>{{ char }}</a
|
||||||
|
>
|
||||||
</nav>
|
</nav>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@ -13,9 +19,9 @@ export default {
|
|||||||
props: ['index'],
|
props: ['index'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
filtered_index () {
|
filtered_index() {
|
||||||
const specialChars = '!"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~'
|
const specialChars = '!"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~'
|
||||||
return this.index.filter(c => !specialChars.includes(c))
|
return this.index.filter((c) => !specialChars.includes(c))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -31,5 +37,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,84 +2,105 @@
|
|||||||
<div>
|
<div>
|
||||||
<div v-if="is_grouped">
|
<div v-if="is_grouped">
|
||||||
<div v-for="idx in albums.indexList" :key="idx" class="mb-6">
|
<div v-for="idx in albums.indexList" :key="idx" class="mb-6">
|
||||||
<span class="tag is-info is-light is-small has-text-weight-bold" :id="'index_' + idx">{{ idx }}</span>
|
<span
|
||||||
|
:id="'index_' + idx"
|
||||||
|
class="tag is-info is-light is-small has-text-weight-bold"
|
||||||
|
>{{ idx }}</span
|
||||||
|
>
|
||||||
|
|
||||||
<div class="media" v-for="album in albums.grouped[idx]"
|
<div
|
||||||
:key="album.id"
|
v-for="album in albums.grouped[idx]"
|
||||||
:album="album"
|
:key="album.id"
|
||||||
@click="open_album(album)">
|
class="media"
|
||||||
<div class="media-left fd-has-action"
|
:album="album"
|
||||||
v-if="is_visible_artwork">
|
@click="open_album(album)"
|
||||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
>
|
||||||
<cover-artwork
|
<div v-if="is_visible_artwork" class="media-left fd-has-action">
|
||||||
|
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
||||||
|
<cover-artwork
|
||||||
:artwork_url="album.artwork_url"
|
:artwork_url="album.artwork_url"
|
||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
:maxwidth="64"
|
:maxwidth="64"
|
||||||
:maxheight="64" />
|
:maxheight="64"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<div style="margin-top:0.7rem;">
|
<div style="margin-top: 0.7rem">
|
||||||
<h1 class="title is-6">{{ album.name }}</h1>
|
<h1 class="title is-6">
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artist }}</b></h2>
|
{{ album.name }}
|
||||||
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal"
|
</h1>
|
||||||
v-if="album.date_released && album.media_kind === 'music'">
|
<h2 class="subtitle is-7 has-text-grey">
|
||||||
{{ $filters.time(album.date_released, 'L') }}
|
<b>{{ album.artist }}</b>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
<h2
|
||||||
</div>
|
v-if="album.date_released && album.media_kind === 'music'"
|
||||||
<div class="media-right" style="padding-top:0.7rem;">
|
class="subtitle is-7 has-text-grey has-text-weight-normal"
|
||||||
<a @click.prevent.stop="open_dialog(album)">
|
>
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
{{ $filters.time(album.date_released, 'L') }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="media-right" style="padding-top: 0.7rem">
|
||||||
|
<a @click.prevent.stop="open_dialog(album)">
|
||||||
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<list-item-album v-for="album in albums_list"
|
<list-item-album
|
||||||
:key="album.id"
|
v-for="album in albums_list"
|
||||||
:album="album"
|
:key="album.id"
|
||||||
@click="open_album(album)">
|
:album="album"
|
||||||
<template v-slot:artwork v-if="is_visible_artwork">
|
@click="open_album(album)"
|
||||||
|
>
|
||||||
|
<template v-if="is_visible_artwork" #artwork>
|
||||||
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
<p class="image is-64x64 fd-has-shadow fd-has-action">
|
||||||
<cover-artwork
|
<cover-artwork
|
||||||
:artwork_url="album.artwork_url"
|
:artwork_url="album.artwork_url"
|
||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
:maxwidth="64"
|
:maxwidth="64"
|
||||||
:maxheight="64" />
|
:maxheight="64"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:actions>
|
<template #actions>
|
||||||
<a @click.prevent.stop="open_dialog(album)">
|
<a @click.prevent.stop="open_dialog(album)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-album>
|
</list-item-album>
|
||||||
</div>
|
</div>
|
||||||
<modal-dialog-album
|
<modal-dialog-album
|
||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:album="selected_album"
|
:album="selected_album"
|
||||||
:media_kind="media_kind"
|
:media_kind="media_kind"
|
||||||
@remove-podcast="open_remove_podcast_dialog()"
|
@remove-podcast="open_remove_podcast_dialog()"
|
||||||
@play-count-changed="play_count_changed()"
|
@play-count-changed="play_count_changed()"
|
||||||
@close="show_details_modal = false" />
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
<modal-dialog
|
<modal-dialog
|
||||||
:show="show_remove_podcast_modal"
|
:show="show_remove_podcast_modal"
|
||||||
title="Remove podcast"
|
title="Remove podcast"
|
||||||
delete_action="Remove"
|
delete_action="Remove"
|
||||||
@close="show_remove_podcast_modal = false"
|
@close="show_remove_podcast_modal = false"
|
||||||
@delete="remove_podcast">
|
@delete="remove_podcast"
|
||||||
<template v-slot:modal-content>
|
>
|
||||||
|
<template #modal-content>
|
||||||
<p>Permanently remove this podcast from your library?</p>
|
<p>Permanently remove this podcast from your library?</p>
|
||||||
<p class="is-size-7">(This will also remove the RSS playlist <b>{{ rss_playlist_to_remove.name }}</b>.)</p>
|
<p class="is-size-7">
|
||||||
|
(This will also remove the RSS playlist
|
||||||
|
<b>{{ rss_playlist_to_remove.name }}</b
|
||||||
|
>.)
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</modal-dialog>
|
</modal-dialog>
|
||||||
</div>
|
</div>
|
||||||
@ -99,7 +120,7 @@ export default {
|
|||||||
|
|
||||||
props: ['albums', 'media_kind'],
|
props: ['albums', 'media_kind'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_details_modal: false,
|
show_details_modal: false,
|
||||||
selected_album: {},
|
selected_album: {},
|
||||||
@ -110,8 +131,11 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_visible_artwork () {
|
is_visible_artwork() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_cover_artwork_in_album_lists').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_cover_artwork_in_album_lists'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
|
|
||||||
media_kind_resolved: function () {
|
media_kind_resolved: function () {
|
||||||
@ -129,7 +153,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_grouped: function () {
|
is_grouped: function () {
|
||||||
return (this.albums instanceof Albums && this.albums.options.group)
|
return this.albums instanceof Albums && this.albums.options.group
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -151,19 +175,24 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
open_remove_podcast_dialog: function () {
|
open_remove_podcast_dialog: function () {
|
||||||
webapi.library_album_tracks(this.selected_album.id, { limit: 1 }).then(({ data }) => {
|
webapi
|
||||||
webapi.library_track_playlists(data.items[0].id).then(({ data }) => {
|
.library_album_tracks(this.selected_album.id, { limit: 1 })
|
||||||
const rssPlaylists = data.items.filter(pl => pl.type === 'rss')
|
.then(({ data }) => {
|
||||||
if (rssPlaylists.length !== 1) {
|
webapi.library_track_playlists(data.items[0].id).then(({ data }) => {
|
||||||
this.$store.dispatch('add_notification', { text: 'Podcast cannot be removed. Probably it was not added as an RSS playlist.', type: 'danger' })
|
const rssPlaylists = data.items.filter((pl) => pl.type === 'rss')
|
||||||
return
|
if (rssPlaylists.length !== 1) {
|
||||||
}
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'Podcast cannot be removed. Probably it was not added as an RSS playlist.',
|
||||||
|
type: 'danger'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.rss_playlist_to_remove = rssPlaylists[0]
|
this.rss_playlist_to_remove = rssPlaylists[0]
|
||||||
this.show_remove_podcast_modal = true
|
this.show_remove_podcast_modal = true
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
play_count_changed: function () {
|
play_count_changed: function () {
|
||||||
@ -172,13 +201,14 @@ export default {
|
|||||||
|
|
||||||
remove_podcast: function () {
|
remove_podcast: function () {
|
||||||
this.show_remove_podcast_modal = false
|
this.show_remove_podcast_modal = false
|
||||||
webapi.library_playlist_delete(this.rss_playlist_to_remove.id).then(() => {
|
webapi
|
||||||
this.$emit('podcast-deleted')
|
.library_playlist_delete(this.rss_playlist_to_remove.id)
|
||||||
})
|
.then(() => {
|
||||||
|
this.$emit('podcast-deleted')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,32 +2,49 @@
|
|||||||
<div>
|
<div>
|
||||||
<div v-if="is_grouped">
|
<div v-if="is_grouped">
|
||||||
<div v-for="idx in artists.indexList" :key="idx" class="mb-6">
|
<div v-for="idx in artists.indexList" :key="idx" class="mb-6">
|
||||||
<span class="tag is-info is-light is-small has-text-weight-bold" :id="'index_' + idx">{{ idx }}</span>
|
<span
|
||||||
<list-item-artist v-for="artist in artists.grouped[idx]"
|
:id="'index_' + idx"
|
||||||
:key="artist.id"
|
class="tag is-info is-light is-small has-text-weight-bold"
|
||||||
:artist="artist"
|
>{{ idx }}</span
|
||||||
@click="open_artist(artist)">
|
>
|
||||||
<template v-slot:actions>
|
<list-item-artist
|
||||||
<a @click.prevent.stop="open_dialog(artist)">
|
v-for="artist in artists.grouped[idx]"
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
:key="artist.id"
|
||||||
</a>
|
:artist="artist"
|
||||||
</template>
|
@click="open_artist(artist)"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
|
<a @click.prevent.stop="open_dialog(artist)">
|
||||||
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
</list-item-artist>
|
</list-item-artist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<list-item-artist v-for="artist in artists_list"
|
<list-item-artist
|
||||||
:key="artist.id"
|
v-for="artist in artists_list"
|
||||||
:artist="artist"
|
:key="artist.id"
|
||||||
@click="open_artist(artist)">
|
:artist="artist"
|
||||||
<template v-slot:actions>
|
@click="open_artist(artist)"
|
||||||
<a @click.prevent.stop="open_dialog(artist)">
|
>
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<template #actions>
|
||||||
</a>
|
<a @click.prevent.stop="open_dialog(artist)">
|
||||||
</template>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
</list-item-artist>
|
</list-item-artist>
|
||||||
</div>
|
</div>
|
||||||
<modal-dialog-artist :show="show_details_modal" :artist="selected_artist" :media_kind="media_kind" @close="show_details_modal = false" />
|
<modal-dialog-artist
|
||||||
|
:show="show_details_modal"
|
||||||
|
:artist="selected_artist"
|
||||||
|
:media_kind="media_kind"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -42,7 +59,7 @@ export default {
|
|||||||
|
|
||||||
props: ['artists', 'media_kind'],
|
props: ['artists', 'media_kind'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_details_modal: false,
|
show_details_modal: false,
|
||||||
selected_artist: {}
|
selected_artist: {}
|
||||||
@ -62,7 +79,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_grouped: function () {
|
is_grouped: function () {
|
||||||
return (this.artists instanceof Artists && this.artists.options.group)
|
return this.artists instanceof Artists && this.artists.options.group
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -86,5 +103,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,32 +2,49 @@
|
|||||||
<div>
|
<div>
|
||||||
<div v-if="is_grouped">
|
<div v-if="is_grouped">
|
||||||
<div v-for="idx in composers.indexList" :key="idx" class="mb-6">
|
<div v-for="idx in composers.indexList" :key="idx" class="mb-6">
|
||||||
<span class="tag is-info is-light is-small has-text-weight-bold" :id="'index_' + idx">{{ idx }}</span>
|
<span
|
||||||
<list-item-composer v-for="composer in composers.grouped[idx]"
|
:id="'index_' + idx"
|
||||||
:key="composer.id"
|
class="tag is-info is-light is-small has-text-weight-bold"
|
||||||
:composer="composer"
|
>{{ idx }}</span
|
||||||
@click="open_composer(composer)">
|
>
|
||||||
<template v-slot:actions>
|
<list-item-composer
|
||||||
<a @click.prevent.stop="open_dialog(composer)">
|
v-for="composer in composers.grouped[idx]"
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
:key="composer.id"
|
||||||
</a>
|
:composer="composer"
|
||||||
</template>
|
@click="open_composer(composer)"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
|
<a @click.prevent.stop="open_dialog(composer)">
|
||||||
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
</list-item-composer>
|
</list-item-composer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<list-item-composer v-for="composer in composers_list"
|
<list-item-composer
|
||||||
:key="composer.id"
|
v-for="composer in composers_list"
|
||||||
:composer="composer"
|
:key="composer.id"
|
||||||
@click="open_composer(composer)">
|
:composer="composer"
|
||||||
<template v-slot:actions>
|
@click="open_composer(composer)"
|
||||||
<a @click.prevent.stop="open_dialog(composer)">
|
>
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<template #actions>
|
||||||
</a>
|
<a @click.prevent.stop="open_dialog(composer)">
|
||||||
</template>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
</list-item-composer>
|
</list-item-composer>
|
||||||
</div>
|
</div>
|
||||||
<modal-dialog-composer :show="show_details_modal" :composer="selected_composer" :media_kind="media_kind" @close="show_details_modal = false" />
|
<modal-dialog-composer
|
||||||
|
:show="show_details_modal"
|
||||||
|
:composer="selected_composer"
|
||||||
|
:media_kind="media_kind"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -42,7 +59,7 @@ export default {
|
|||||||
|
|
||||||
props: ['composers', 'media_kind'],
|
props: ['composers', 'media_kind'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_details_modal: false,
|
show_details_modal: false,
|
||||||
selected_composer: {}
|
selected_composer: {}
|
||||||
@ -51,7 +68,9 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
media_kind_resolved: function () {
|
media_kind_resolved: function () {
|
||||||
return this.media_kind ? this.media_kind : this.selected_composer.media_kind
|
return this.media_kind
|
||||||
|
? this.media_kind
|
||||||
|
: this.selected_composer.media_kind
|
||||||
},
|
},
|
||||||
|
|
||||||
composers_list: function () {
|
composers_list: function () {
|
||||||
@ -62,14 +81,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_grouped: function () {
|
is_grouped: function () {
|
||||||
return (this.composers instanceof Composers && this.composers.options.group)
|
return this.composers instanceof Composers && this.composers.options.group
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_composer: function (composer) {
|
open_composer: function (composer) {
|
||||||
this.selected_composer = composer
|
this.selected_composer = composer
|
||||||
this.$router.push({ name: 'ComposerTracks', params: { composer: composer.name } })
|
this.$router.push({
|
||||||
|
name: 'ComposerTracks',
|
||||||
|
params: { composer: composer.name }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_dialog: function (composer) {
|
open_dialog: function (composer) {
|
||||||
@ -80,5 +102,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media" :id="'index_' + album.name_sort.charAt(0).toUpperCase()">
|
<div :id="'index_' + album.name_sort.charAt(0).toUpperCase()" class="media">
|
||||||
<div class="media-left fd-has-action"
|
<div v-if="$slots['artwork']" class="media-left fd-has-action">
|
||||||
v-if="$slots['artwork']">
|
<slot name="artwork" />
|
||||||
<slot name="artwork"></slot>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<div style="margin-top:0.7rem;">
|
<div style="margin-top: 0.7rem">
|
||||||
<h1 class="title is-6">{{ album.name }}</h1>
|
<h1 class="title is-6">
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artist }}</b></h2>
|
{{ album.name }}
|
||||||
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal"
|
</h1>
|
||||||
v-if="album.date_released && album.media_kind === 'music'">
|
<h2 class="subtitle is-7 has-text-grey">
|
||||||
|
<b>{{ album.artist }}</b>
|
||||||
|
</h2>
|
||||||
|
<h2
|
||||||
|
v-if="album.date_released && album.media_kind === 'music'"
|
||||||
|
class="subtitle is-7 has-text-grey has-text-weight-normal"
|
||||||
|
>
|
||||||
{{ $filters.time(album.date_released, 'L') }}
|
{{ $filters.time(album.date_released, 'L') }}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right" style="padding-top:0.7rem;">
|
<div class="media-right" style="padding-top: 0.7rem">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -27,5 +32,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<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">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -16,5 +18,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media" :id="'index_' + composer.name.charAt(0).toUpperCase()">
|
<div :id="'index_' + composer.name.charAt(0).toUpperCase()" class="media">
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6">{{ composer.name }}</h1>
|
<h1 class="title is-6">
|
||||||
|
{{ composer.name }}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -16,5 +18,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,15 +2,19 @@
|
|||||||
<div class="media">
|
<div class="media">
|
||||||
<figure class="media-left fd-has-action">
|
<figure class="media-left fd-has-action">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-folder"></i>
|
<i class="mdi mdi-folder" />
|
||||||
</span>
|
</span>
|
||||||
</figure>
|
</figure>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6">{{ directory.path.substring(directory.path.lastIndexOf('/') + 1) }}</h1>
|
<h1 class="title is-6">
|
||||||
<h2 class="subtitle is-7 has-text-grey-light">{{ directory.path }}</h2>
|
{{ directory.path.substring(directory.path.lastIndexOf('/') + 1) }}
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey-light">
|
||||||
|
{{ directory.path }}
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -22,5 +26,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media" :id="'index_' + genre.name.charAt(0).toUpperCase()">
|
<div :id="'index_' + genre.name.charAt(0).toUpperCase()" class="media">
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6">{{ genre.name }}</h1>
|
<h1 class="title is-6">
|
||||||
|
{{ genre.name }}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -16,5 +18,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<figure class="media-left fd-has-action" v-if="$slots.icon">
|
<figure v-if="$slots.icon" class="media-left fd-has-action">
|
||||||
<slot name="icon"></slot>
|
<slot name="icon" />
|
||||||
</figure>
|
</figure>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6">{{ playlist.name }}</h1>
|
<h1 class="title is-6">
|
||||||
|
{{ playlist.name }}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -19,5 +21,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,16 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media" v-if="is_next || !show_only_next_items">
|
<div v-if="is_next || !show_only_next_items" class="media">
|
||||||
<div class="media-left" v-if="edit_mode">
|
<div v-if="edit_mode" class="media-left">
|
||||||
<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"
|
||||||
|
/></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="play">
|
<div class="media-content fd-has-action is-clipped" @click="play">
|
||||||
<h1 class="title is-6" :class="{ 'has-text-primary': item.id === state.item_id, 'has-text-grey-light': !is_next }">{{ item.title }}</h1>
|
<h1
|
||||||
<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 }"><b>{{ item.artist }}</b></h2>
|
class="title is-6"
|
||||||
<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>
|
:class="{
|
||||||
|
'has-text-primary': item.id === state.item_id,
|
||||||
|
'has-text-grey-light': !is_next
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</h1>
|
||||||
|
<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
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<b>{{ item.artist }}</b>
|
||||||
|
</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">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -20,14 +48,20 @@ import webapi from '@/webapi'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListItemQueueItem',
|
name: 'ListItemQueueItem',
|
||||||
props: ['item', 'position', 'current_position', 'show_only_next_items', 'edit_mode'],
|
props: [
|
||||||
|
'item',
|
||||||
|
'position',
|
||||||
|
'current_position',
|
||||||
|
'show_only_next_items',
|
||||||
|
'edit_mode'
|
||||||
|
],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
state () {
|
state() {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
|
|
||||||
is_next () {
|
is_next() {
|
||||||
return this.current_position < 0 || this.position >= this.current_position
|
return this.current_position < 0 || this.position >= this.current_position
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -40,5 +74,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,16 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media" :id="'index_' + track.title_sort.charAt(0).toUpperCase()" :class="{ 'with-progress': $slots.progress }">
|
<div
|
||||||
<figure class="media-left fd-has-action" v-if="$slots.icon">
|
:id="'index_' + track.title_sort.charAt(0).toUpperCase()"
|
||||||
<slot name="icon"></slot>
|
class="media"
|
||||||
|
:class="{ 'with-progress': $slots.progress }"
|
||||||
|
>
|
||||||
|
<figure v-if="$slots.icon" class="media-left fd-has-action">
|
||||||
|
<slot name="icon" />
|
||||||
</figure>
|
</figure>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6" :class="{ 'has-text-grey': track.media_kind === 'podcast' && track.play_count > 0 }">{{ track.title }}</h1>
|
<h1
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ track.artist }}</b></h2>
|
class="title is-6"
|
||||||
<h2 class="subtitle is-7 has-text-grey">{{ track.album }}</h2>
|
:class="{
|
||||||
<slot name="progress"></slot>
|
'has-text-grey':
|
||||||
|
track.media_kind === 'podcast' && track.play_count > 0
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ track.title }}
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey">
|
||||||
|
<b>{{ track.artist }}</b>
|
||||||
|
</h2>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey">
|
||||||
|
{{ track.album }}
|
||||||
|
</h2>
|
||||||
|
<slot name="progress" />
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -22,5 +38,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,18 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<list-item-playlist v-for="playlist in playlists" :key="playlist.id" :playlist="playlist" @click="open_playlist(playlist)">
|
<list-item-playlist
|
||||||
<template v-slot:icon>
|
v-for="playlist in playlists"
|
||||||
|
:key="playlist.id"
|
||||||
|
:playlist="playlist"
|
||||||
|
@click="open_playlist(playlist)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi" :class="{ 'mdi-library-music': playlist.type !== 'folder', 'mdi-rss': playlist.type === 'rss', 'mdi-folder': playlist.type === 'folder' }"></i>
|
<i
|
||||||
|
class="mdi"
|
||||||
|
:class="{
|
||||||
|
'mdi-library-music': playlist.type !== 'folder',
|
||||||
|
'mdi-rss': playlist.type === 'rss',
|
||||||
|
'mdi-folder': playlist.type === 'folder'
|
||||||
|
}"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:actions>
|
<template #actions>
|
||||||
<a @click.prevent.stop="open_dialog(playlist)">
|
<a @click.prevent.stop="open_dialog(playlist)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-playlist>
|
</list-item-playlist>
|
||||||
<modal-dialog-playlist :show="show_details_modal" :playlist="selected_playlist" @close="show_details_modal = false" />
|
<modal-dialog-playlist
|
||||||
|
:show="show_details_modal"
|
||||||
|
:playlist="selected_playlist"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -26,7 +44,7 @@ export default {
|
|||||||
|
|
||||||
props: ['playlists'],
|
props: ['playlists'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_details_modal: false,
|
show_details_modal: false,
|
||||||
selected_playlist: {}
|
selected_playlist: {}
|
||||||
@ -50,5 +68,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,13 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index, track)">
|
<list-item-track
|
||||||
<template v-slot:actions>
|
v-for="(track, index) in tracks"
|
||||||
|
:key="track.id"
|
||||||
|
:track="track"
|
||||||
|
@click="play_track(index, track)"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
<a @click.prevent.stop="open_dialog(track)">
|
<a @click.prevent.stop="open_dialog(track)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-track>
|
</list-item-track>
|
||||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
<modal-dialog-track
|
||||||
|
:show="show_details_modal"
|
||||||
|
:track="selected_track"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -22,7 +33,7 @@ export default {
|
|||||||
|
|
||||||
props: ['tracks', 'uris', 'expression'],
|
props: ['tracks', 'uris', 'expression'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_details_modal: false,
|
show_details_modal: false,
|
||||||
selected_track: {}
|
selected_track: {}
|
||||||
@ -48,5 +59,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,30 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4" v-if="title">
|
<p v-if="title" class="title is-4">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</p>
|
</p>
|
||||||
<slot name="modal-content"></slot>
|
<slot name="modal-content" />
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="$emit('close')">
|
<a class="card-footer-item has-text-dark" @click="$emit('close')">
|
||||||
<span class="icon"><i class="mdi mdi-cancel"></i></span> <span class="is-size-7">{{ close_action ? close_action : 'Cancel' }}</span>
|
<span class="icon"><i class="mdi mdi-cancel" /></span>
|
||||||
|
<span class="is-size-7">{{
|
||||||
|
close_action ? close_action : 'Cancel'
|
||||||
|
}}</span>
|
||||||
</a>
|
</a>
|
||||||
<a v-if="delete_action" class="card-footer-item has-background-danger has-text-white has-text-weight-bold" @click="$emit('delete')">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-delete"></i></span> <span class="is-size-7">{{ delete_action }}</span>
|
v-if="delete_action"
|
||||||
|
class="card-footer-item has-background-danger has-text-white has-text-weight-bold"
|
||||||
|
@click="$emit('delete')"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-delete" /></span>
|
||||||
|
<span class="is-size-7">{{ delete_action }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a v-if="ok_action" class="card-footer-item has-background-info has-text-white has-text-weight-bold" @click="$emit('ok')">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-check"></i></span> <span class="is-size-7">{{ ok_action }}</span>
|
v-if="ok_action"
|
||||||
|
class="card-footer-item has-background-info has-text-white has-text-weight-bold"
|
||||||
|
@click="$emit('ok')"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-check" /></span>
|
||||||
|
<span class="is-size-7">{{ ok_action }}</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -37,5 +54,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -10,32 +10,54 @@
|
|||||||
<form @submit.prevent="add_stream">
|
<form @submit.prevent="add_stream">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p class="control is-expanded has-icons-left">
|
<p class="control is-expanded has-icons-left">
|
||||||
<input class="input is-shadowless" type="text" placeholder="http://url-to-rss" v-model="url" :disabled="loading" ref="url_field">
|
<input
|
||||||
|
ref="url_field"
|
||||||
|
v-model="url"
|
||||||
|
class="input is-shadowless"
|
||||||
|
type="text"
|
||||||
|
placeholder="http://url-to-rss"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="mdi mdi-rss"></i>
|
<i class="mdi mdi-rss" />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="help">Adding a podcast includes creating an RSS playlist, that will allow OwnTone to manage the podcast subscription.
|
<p class="help">
|
||||||
|
Adding a podcast includes creating an RSS playlist, that
|
||||||
|
will allow OwnTone to manage the podcast subscription.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer" v-if="loading">
|
<footer v-if="loading" class="card-footer">
|
||||||
<a class="card-footer-item button is-loading">
|
<a class="card-footer-item button is-loading">
|
||||||
<span class="icon"><i class="mdi mdi-web"></i></span> <span class="is-size-7">Processing ...</span>
|
<span class="icon"><i class="mdi mdi-web" /></span>
|
||||||
|
<span class="is-size-7">Processing ...</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
<footer class="card-footer" v-else>
|
<footer v-else class="card-footer">
|
||||||
<a class="card-footer-item has-text-danger" @click="$emit('close')">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-cancel"></i></span> <span class="is-size-7">Cancel</span>
|
class="card-footer-item has-text-danger"
|
||||||
|
@click="$emit('close')"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-cancel" /></span>
|
||||||
|
<span class="is-size-7">Cancel</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-background-info has-text-white has-text-weight-bold" @click="add_stream">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
class="card-footer-item has-background-info has-text-white has-text-weight-bold"
|
||||||
|
@click="add_stream"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -48,28 +70,15 @@ export default {
|
|||||||
name: 'ModalDialogAddRss',
|
name: 'ModalDialogAddRss',
|
||||||
props: ['show'],
|
props: ['show'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
url: '',
|
url: '',
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
add_stream: function () {
|
|
||||||
this.loading = true
|
|
||||||
webapi.library_add(this.url).then(() => {
|
|
||||||
this.$emit('close')
|
|
||||||
this.$emit('podcast-added')
|
|
||||||
this.url = ''
|
|
||||||
}).catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'show' () {
|
show() {
|
||||||
if (this.show) {
|
if (this.show) {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
||||||
@ -79,9 +88,24 @@ export default {
|
|||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
add_stream: function () {
|
||||||
|
this.loading = true
|
||||||
|
webapi
|
||||||
|
.library_add(this.url)
|
||||||
|
.then(() => {
|
||||||
|
this.$emit('close')
|
||||||
|
this.$emit('podcast-added')
|
||||||
|
this.url = ''
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,44 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">Add stream URL</p>
|
||||||
Add stream URL
|
<form class="fd-has-margin-bottom" @submit.prevent="play">
|
||||||
</p>
|
|
||||||
<form v-on:submit.prevent="play" class="fd-has-margin-bottom">
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p class="control is-expanded has-icons-left">
|
<p class="control is-expanded has-icons-left">
|
||||||
<input class="input is-shadowless" type="text" placeholder="http://url-to-stream" v-model="url" :disabled="loading" ref="url_field">
|
<input
|
||||||
|
ref="url_field"
|
||||||
|
v-model="url"
|
||||||
|
class="input is-shadowless"
|
||||||
|
type="text"
|
||||||
|
placeholder="http://url-to-stream"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="mdi mdi-web"></i>
|
<i class="mdi mdi-web" />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer" v-if="loading">
|
<footer v-if="loading" class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark">
|
<a class="card-footer-item has-text-dark">
|
||||||
<span class="icon"><i class="mdi mdi-web"></i></span> <span class="is-size-7">Loading ...</span>
|
<span class="icon"><i class="mdi mdi-web" /></span>
|
||||||
|
<span class="is-size-7">Loading ...</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
<footer class="card-footer" v-else>
|
<footer v-else class="card-footer">
|
||||||
<a class="card-footer-item has-text-danger" @click="$emit('close')">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-cancel"></i></span> <span class="is-size-7">Cancel</span>
|
class="card-footer-item has-text-danger"
|
||||||
|
@click="$emit('close')"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-cancel" /></span>
|
||||||
|
<span class="is-size-7">Cancel</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="add_stream">
|
<a class="card-footer-item has-text-dark" @click="add_stream">
|
||||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-background-info has-text-white has-text-weight-bold" @click="play">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
class="card-footer-item has-background-info has-text-white has-text-weight-bold"
|
||||||
|
@click="play"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -51,37 +70,15 @@ export default {
|
|||||||
name: 'ModalDialogAddUrlStream',
|
name: 'ModalDialogAddUrlStream',
|
||||||
props: ['show'],
|
props: ['show'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
url: '',
|
url: '',
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
add_stream: function () {
|
|
||||||
this.loading = true
|
|
||||||
webapi.queue_add(this.url).then(() => {
|
|
||||||
this.$emit('close')
|
|
||||||
this.url = ''
|
|
||||||
}).catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
play: function () {
|
|
||||||
this.loading = true
|
|
||||||
webapi.player_play_uri(this.url, false).then(() => {
|
|
||||||
this.$emit('close')
|
|
||||||
this.url = ''
|
|
||||||
}).catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'show' () {
|
show() {
|
||||||
if (this.show) {
|
if (this.show) {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
||||||
@ -91,9 +88,36 @@ export default {
|
|||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
add_stream: function () {
|
||||||
|
this.loading = true
|
||||||
|
webapi
|
||||||
|
.queue_add(this.url)
|
||||||
|
.then(() => {
|
||||||
|
this.$emit('close')
|
||||||
|
this.url = ''
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
play: function () {
|
||||||
|
this.loading = true
|
||||||
|
webapi
|
||||||
|
.player_play_uri(this.url, false)
|
||||||
|
.then(() => {
|
||||||
|
this.$emit('close')
|
||||||
|
this.url = ''
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -10,22 +10,33 @@
|
|||||||
:artwork_url="album.artwork_url"
|
:artwork_url="album.artwork_url"
|
||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
class="image is-square fd-has-margin-bottom fd-has-shadow" />
|
class="image is-square fd-has-margin-bottom fd-has-shadow"
|
||||||
|
/>
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_album">{{ album.name }}</a>
|
<a class="has-text-link" @click="open_album">{{
|
||||||
|
album.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="buttons" v-if="media_kind_resolved === 'podcast'">
|
<div v-if="media_kind_resolved === 'podcast'" class="buttons">
|
||||||
<a class="button is-small" @click="mark_played">Mark as played</a>
|
<a class="button is-small" @click="mark_played"
|
||||||
<a class="button is-small" @click="$emit('remove-podcast')">Remove podcast</a>
|
>Mark as played</a
|
||||||
|
>
|
||||||
|
<a class="button is-small" @click="$emit('remove-podcast')"
|
||||||
|
>Remove podcast</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p v-if="album.artist">
|
<p v-if="album.artist">
|
||||||
<span class="heading">Album artist</span>
|
<span class="heading">Album artist</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artist }}</a>
|
<a class="title is-6 has-text-link" @click="open_artist">{{
|
||||||
|
album.artist
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="album.date_released">
|
<p v-if="album.date_released">
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ $filters.time(album.date_released, 'L') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(album.date_released, 'L')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="album.year > 0">
|
<p v-else-if="album.year > 0">
|
||||||
<span class="heading">Year</span>
|
<span class="heading">Year</span>
|
||||||
@ -37,32 +48,45 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{ $filters.duration(album.length_ms) }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.duration(album.length_ms)
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Type</span>
|
<span class="heading">Type</span>
|
||||||
<span class="title is-6">{{ album.media_kind }} - {{ album.data_kind }}</span>
|
<span class="title is-6"
|
||||||
|
>{{ album.media_kind }} - {{ album.data_kind }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
<span class="title is-6">{{ $filters.time(album.time_added, 'L LT') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(album.time_added, 'L LT')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -77,7 +101,7 @@ export default {
|
|||||||
components: { CoverArtwork },
|
components: { CoverArtwork },
|
||||||
props: ['show', 'album', 'media_kind', 'new_tracks'],
|
props: ['show', 'album', 'media_kind', 'new_tracks'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
artwork_visible: false
|
artwork_visible: false
|
||||||
}
|
}
|
||||||
@ -123,17 +147,21 @@ export default {
|
|||||||
if (this.media_kind_resolved === 'podcast') {
|
if (this.media_kind_resolved === 'podcast') {
|
||||||
// No artist page for podcasts
|
// No artist page for podcasts
|
||||||
} else if (this.media_kind_resolved === 'audiobook') {
|
} else if (this.media_kind_resolved === 'audiobook') {
|
||||||
this.$router.push({ path: '/audiobooks/artists/' + this.album.artist_id })
|
this.$router.push({
|
||||||
|
path: '/audiobooks/artists/' + this.album.artist_id
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$router.push({ path: '/music/artists/' + this.album.artist_id })
|
this.$router.push({ path: '/music/artists/' + this.album.artist_id })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mark_played: function () {
|
mark_played: function () {
|
||||||
webapi.library_album_track_update(this.album.id, { play_count: 'played' }).then(({ data }) => {
|
webapi
|
||||||
this.$emit('play-count-changed')
|
.library_album_track_update(this.album.id, { play_count: 'played' })
|
||||||
this.$emit('close')
|
.then(({ data }) => {
|
||||||
})
|
this.$emit('play-count-changed')
|
||||||
|
this.$emit('close')
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
artwork_loaded: function () {
|
artwork_loaded: function () {
|
||||||
@ -147,5 +175,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_artist">{{ artist.name }}</a>
|
<a class="has-text-link" @click="open_artist">{{
|
||||||
|
artist.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
@ -24,24 +26,33 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
<span class="title is-6">{{ $filters.time(artist.time_added, 'L LT') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(artist.time_added, 'L LT')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -78,5 +89,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,37 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_albums">{{ composer.name }}</a>
|
<a class="has-text-link" @click="open_albums">{{
|
||||||
|
composer.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Albums</span>
|
<span class="heading">Albums</span>
|
||||||
<a class="has-text-link is-6" @click="open_albums">{{ composer.album_count }}</a>
|
<a class="has-text-link is-6" @click="open_albums">{{
|
||||||
|
composer.album_count
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Tracks</span>
|
<span class="heading">Tracks</span>
|
||||||
<a class="has-text-link is-6" @click="open_tracks">{{ composer.track_count }}</a>
|
<a class="has-text-link is-6" @click="open_tracks">{{
|
||||||
|
composer.track_count
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -47,31 +60,43 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.player_play_expression('composer is "' + this.composer.name + '" and media_kind is music', false)
|
webapi.player_play_expression(
|
||||||
|
'composer is "' + this.composer.name + '" and media_kind is music',
|
||||||
|
false
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add: function () {
|
queue_add: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.queue_expression_add('composer is "' + this.composer.name + '" and media_kind is music')
|
webapi.queue_expression_add(
|
||||||
|
'composer is "' + this.composer.name + '" and media_kind is music'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add_next: function () {
|
queue_add_next: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.queue_expression_add_next('composer is "' + this.composer.name + '" and media_kind is music')
|
webapi.queue_expression_add_next(
|
||||||
|
'composer is "' + this.composer.name + '" and media_kind is music'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
open_albums: function () {
|
open_albums: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: this.composer.name } })
|
this.$router.push({
|
||||||
|
name: 'ComposerAlbums',
|
||||||
|
params: { composer: this.composer.name }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_tracks: function () {
|
open_tracks: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
this.$router.push({ name: 'ComposerTracks', params: { composer: this.composer.name } })
|
this.$router.push({
|
||||||
|
name: 'ComposerTracks',
|
||||||
|
params: { composer: this.composer.name }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -12,18 +12,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -39,21 +46,27 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.player_play_expression('path starts with "' + this.directory.path + '" order by path asc', false)
|
webapi.player_play_expression(
|
||||||
|
'path starts with "' + this.directory.path + '" order by path asc',
|
||||||
|
false
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add: function () {
|
queue_add: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.queue_expression_add('path starts with "' + this.directory.path + '" order by path asc')
|
webapi.queue_expression_add(
|
||||||
|
'path starts with "' + this.directory.path + '" order by path asc'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add_next: function () {
|
queue_add_next: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.queue_expression_add_next('path starts with "' + this.directory.path + '" order by path asc')
|
webapi.queue_expression_add_next(
|
||||||
|
'path starts with "' + this.directory.path + '" order by path asc'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,29 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_genre">{{ genre.name }}</a>
|
<a class="has-text-link" @click="open_genre">{{
|
||||||
|
genre.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -39,17 +48,24 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.player_play_expression('genre is "' + this.genre.name + '" and media_kind is music', false)
|
webapi.player_play_expression(
|
||||||
|
'genre is "' + this.genre.name + '" and media_kind is music',
|
||||||
|
false
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add: function () {
|
queue_add: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.queue_expression_add('genre is "' + this.genre.name + '" and media_kind is music')
|
webapi.queue_expression_add(
|
||||||
|
'genre is "' + this.genre.name + '" and media_kind is music'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
queue_add_next: function () {
|
queue_add_next: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
webapi.queue_expression_add_next('genre is "' + this.genre.name + '" and media_kind is music')
|
webapi.queue_expression_add_next(
|
||||||
|
'genre is "' + this.genre.name + '" and media_kind is music'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
open_genre: function () {
|
open_genre: function () {
|
||||||
@ -60,5 +76,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_playlist">{{ playlist.name }}</a>
|
<a class="has-text-link" @click="open_playlist">{{
|
||||||
|
playlist.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
@ -20,20 +22,27 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer" v-if="!playlist.folder">
|
<footer v-if="!playlist.folder" class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -70,5 +79,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,41 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">Save queue to playlist</p>
|
||||||
Save queue to playlist
|
<form class="fd-has-margin-bottom" @submit.prevent="save">
|
||||||
</p>
|
|
||||||
<form v-on:submit.prevent="save" class="fd-has-margin-bottom">
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p class="control is-expanded has-icons-left">
|
<p class="control is-expanded has-icons-left">
|
||||||
<input class="input is-shadowless" type="text" placeholder="Playlist name" v-model="playlist_name" :disabled="loading" ref="playlist_name_field">
|
<input
|
||||||
|
ref="playlist_name_field"
|
||||||
|
v-model="playlist_name"
|
||||||
|
class="input is-shadowless"
|
||||||
|
type="text"
|
||||||
|
placeholder="Playlist name"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="mdi mdi-file-music"></i>
|
<i class="mdi mdi-file-music" />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer" v-if="loading">
|
<footer v-if="loading" class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark">
|
<a class="card-footer-item has-text-dark">
|
||||||
<span class="icon"><i class="mdi mdi-web"></i></span> <span class="is-size-7">Saving ...</span>
|
<span class="icon"><i class="mdi mdi-web" /></span>
|
||||||
|
<span class="is-size-7">Saving ...</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
<footer class="card-footer" v-else>
|
<footer v-else class="card-footer">
|
||||||
<a class="card-footer-item has-text-danger" @click="$emit('close')">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-cancel"></i></span> <span class="is-size-7">Cancel</span>
|
class="card-footer-item has-text-danger"
|
||||||
|
@click="$emit('close')"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-cancel" /></span>
|
||||||
|
<span class="is-size-7">Cancel</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-background-info has-text-white has-text-weight-bold" @click="save">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-content-save"></i></span> <span class="is-size-7">Save</span>
|
class="card-footer-item has-background-info has-text-white has-text-weight-bold"
|
||||||
|
@click="save"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-content-save" /></span>
|
||||||
|
<span class="is-size-7">Save</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -48,13 +66,26 @@ export default {
|
|||||||
name: 'ModalDialogPlaylistSave',
|
name: 'ModalDialogPlaylistSave',
|
||||||
props: ['show'],
|
props: ['show'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
playlist_name: '',
|
playlist_name: '',
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
show() {
|
||||||
|
if (this.show) {
|
||||||
|
this.loading = false
|
||||||
|
|
||||||
|
// We need to delay setting the focus to the input field until the field is part of the dom and visible
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.playlist_name_field.focus()
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
save: function () {
|
save: function () {
|
||||||
if (this.playlist_name.length < 1) {
|
if (this.playlist_name.length < 1) {
|
||||||
@ -62,29 +93,18 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
webapi.queue_save_playlist(this.playlist_name).then(() => {
|
webapi
|
||||||
this.$emit('close')
|
.queue_save_playlist(this.playlist_name)
|
||||||
this.playlist_name = ''
|
.then(() => {
|
||||||
}).catch(() => {
|
this.$emit('close')
|
||||||
this.loading = false
|
this.playlist_name = ''
|
||||||
})
|
})
|
||||||
}
|
.catch(() => {
|
||||||
},
|
this.loading = false
|
||||||
|
})
|
||||||
watch: {
|
|
||||||
'show' () {
|
|
||||||
if (this.show) {
|
|
||||||
this.loading = false
|
|
||||||
|
|
||||||
// We need to delay setting the focus to the input field until the field is part of the dom and visible
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.playlist_name_field.focus()
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -15,12 +15,22 @@
|
|||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Album</span>
|
<span class="heading">Album</span>
|
||||||
<a v-if="item.album_id" class="title is-6 has-text-link" @click="open_album">{{ item.album }}</a>
|
<a
|
||||||
|
v-if="item.album_id"
|
||||||
|
class="title is-6 has-text-link"
|
||||||
|
@click="open_album"
|
||||||
|
>{{ item.album }}</a
|
||||||
|
>
|
||||||
<span v-else class="title is-6">{{ item.album }}</span>
|
<span v-else class="title is-6">{{ item.album }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="item.album_artist">
|
<p v-if="item.album_artist">
|
||||||
<span class="heading">Album artist</span>
|
<span class="heading">Album artist</span>
|
||||||
<a v-if="item.album_artist_id" class="title is-6 has-text-link" @click="open_album_artist">{{ item.album_artist }}</a>
|
<a
|
||||||
|
v-if="item.album_artist_id"
|
||||||
|
class="title is-6 has-text-link"
|
||||||
|
@click="open_album_artist"
|
||||||
|
>{{ item.album_artist }}</a
|
||||||
|
>
|
||||||
<span v-else class="title is-6">{{ item.album_artist }}</span>
|
<span v-else class="title is-6">{{ item.album_artist }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="item.composer">
|
<p v-if="item.composer">
|
||||||
@ -33,15 +43,21 @@
|
|||||||
</p>
|
</p>
|
||||||
<p v-if="item.genre">
|
<p v-if="item.genre">
|
||||||
<span class="heading">Genre</span>
|
<span class="heading">Genre</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_genre">{{ item.genre }}</a>
|
<a class="title is-6 has-text-link" @click="open_genre">{{
|
||||||
|
item.genre
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Track / Disc</span>
|
<span class="heading">Track / Disc</span>
|
||||||
<span class="title is-6">{{ item.track_number }} / {{ item.disc_number }}</span>
|
<span class="title is-6"
|
||||||
|
>{{ item.track_number }} / {{ item.disc_number }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{ $filters.duration(item.length_ms) }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.duration(item.length_ms)
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Path</span>
|
<span class="heading">Path</span>
|
||||||
@ -49,14 +65,26 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Type</span>
|
<span class="heading">Type</span>
|
||||||
<span class="title is-6">{{ item.media_kind }} - {{ item.data_kind }} <span class="has-text-weight-normal" v-if="item.data_kind === 'spotify'">(<a @click="open_spotify_artist">artist</a>, <a @click="open_spotify_album">album</a>)</span></span>
|
<span class="title is-6"
|
||||||
|
>{{ item.media_kind }} - {{ item.data_kind }}
|
||||||
|
<span
|
||||||
|
v-if="item.data_kind === 'spotify'"
|
||||||
|
class="has-text-weight-normal"
|
||||||
|
>(<a @click="open_spotify_artist">artist</a>,
|
||||||
|
<a @click="open_spotify_album">album</a>)</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Quality</span>
|
<span class="heading">Quality</span>
|
||||||
<span class="title is-6">
|
<span class="title is-6">
|
||||||
{{ item.type }}
|
{{ item.type }}
|
||||||
<span v-if="item.samplerate"> | {{ item.samplerate }} Hz</span>
|
<span v-if="item.samplerate">
|
||||||
<span v-if="item.channels"> | {{ $filters.channels(item.channels) }}</span>
|
| {{ item.samplerate }} Hz</span
|
||||||
|
>
|
||||||
|
<span v-if="item.channels">
|
||||||
|
| {{ $filters.channels(item.channels) }}</span
|
||||||
|
>
|
||||||
<span v-if="item.bitrate"> | {{ item.bitrate }} Kb/s</span>
|
<span v-if="item.bitrate"> | {{ item.bitrate }} Kb/s</span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
@ -64,15 +92,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="remove">
|
<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>
|
<span class="icon"><i class="mdi mdi-delete" /></span>
|
||||||
|
<span class="is-size-7">Remove</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -86,12 +120,28 @@ export default {
|
|||||||
name: 'ModalDialogQueueItem',
|
name: 'ModalDialogQueueItem',
|
||||||
props: ['show', 'item'],
|
props: ['show', 'item'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
spotify_track: {}
|
spotify_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
item() {
|
||||||
|
if (this.item && this.item.data_kind === 'spotify') {
|
||||||
|
const spotifyApi = new SpotifyWebApi()
|
||||||
|
spotifyApi.setAccessToken(this.$store.state.spotify.webapi_token)
|
||||||
|
spotifyApi
|
||||||
|
.getTrack(this.item.path.slice(this.item.path.lastIndexOf(':') + 1))
|
||||||
|
.then((response) => {
|
||||||
|
this.spotify_track = response
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.spotify_track = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
remove: function () {
|
remove: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
@ -123,30 +173,19 @@ export default {
|
|||||||
|
|
||||||
open_spotify_artist: function () {
|
open_spotify_artist: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.$router.push({ path: '/music/spotify/artists/' + this.spotify_track.artists[0].id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/artists/' + this.spotify_track.artists[0].id
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_spotify_album: function () {
|
open_spotify_album: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.$router.push({ path: '/music/spotify/albums/' + this.spotify_track.album.id })
|
this.$router.push({
|
||||||
}
|
path: '/music/spotify/albums/' + this.spotify_track.album.id
|
||||||
},
|
})
|
||||||
|
|
||||||
watch: {
|
|
||||||
'item' () {
|
|
||||||
if (this.item && this.item.data_kind === 'spotify') {
|
|
||||||
const spotifyApi = new SpotifyWebApi()
|
|
||||||
spotifyApi.setAccessToken(this.$store.state.spotify.webapi_token)
|
|
||||||
spotifyApi.getTrack(this.item.path.slice(this.item.path.lastIndexOf(':') + 1)).then((response) => {
|
|
||||||
this.spotify_track = response
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.spotify_track = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,36 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">Remote pairing request</p>
|
||||||
Remote pairing request
|
<form @submit.prevent="kickoff_pairing">
|
||||||
</p>
|
|
||||||
<form v-on:submit.prevent="kickoff_pairing">
|
|
||||||
<label class="label">
|
<label class="label">
|
||||||
{{ pairing.remote }}
|
{{ pairing.remote }}
|
||||||
</label>
|
</label>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" placeholder="Enter pairing code" v-model="pairing_req.pin" ref="pin_field">
|
<input
|
||||||
|
ref="pin_field"
|
||||||
|
v-model="pairing_req.pin"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter pairing code"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-danger" @click="$emit('close')">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-cancel"></i></span> <span class="is-size-7">Cancel</span>
|
class="card-footer-item has-text-danger"
|
||||||
|
@click="$emit('close')"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-cancel" /></span>
|
||||||
|
<span class="is-size-7">Cancel</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-background-info has-text-white has-text-weight-bold" @click="kickoff_pairing">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-cellphone-iphone"></i></span> <span class="is-size-7">Pair Remote</span>
|
class="card-footer-item has-background-info has-text-white has-text-weight-bold"
|
||||||
|
@click="kickoff_pairing"
|
||||||
|
>
|
||||||
|
<span class="icon"><i class="mdi mdi-cellphone-iphone" /></span>
|
||||||
|
<span class="is-size-7">Pair Remote</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -43,28 +59,20 @@ export default {
|
|||||||
name: 'ModalDialogRemotePairing',
|
name: 'ModalDialogRemotePairing',
|
||||||
props: ['show'],
|
props: ['show'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
pairing_req: { pin: '' }
|
pairing_req: { pin: '' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
pairing () {
|
pairing() {
|
||||||
return this.$store.state.pairing
|
return this.$store.state.pairing
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
kickoff_pairing () {
|
|
||||||
webapi.pairing_kickoff(this.pairing_req).then(() => {
|
|
||||||
this.pairing_req.pin = ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'show' () {
|
show() {
|
||||||
if (this.show) {
|
if (this.show) {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
||||||
@ -74,9 +82,16 @@ export default {
|
|||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
kickoff_pairing() {
|
||||||
|
webapi.pairing_kickoff(this.pairing_req).then(() => {
|
||||||
|
this.pairing_req.pin = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -12,18 +12,34 @@
|
|||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
{{ track.artist }}
|
{{ track.artist }}
|
||||||
</p>
|
</p>
|
||||||
<div class="buttons" v-if="track.media_kind === 'podcast'">
|
<div v-if="track.media_kind === 'podcast'" class="buttons">
|
||||||
<a class="button is-small" v-if="track.play_count > 0" @click="mark_new">Mark as new</a>
|
<a
|
||||||
<a class="button is-small" v-if="track.play_count === 0" @click="mark_played">Mark as played</a>
|
v-if="track.play_count > 0"
|
||||||
|
class="button is-small"
|
||||||
|
@click="mark_new"
|
||||||
|
>Mark as new</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
v-if="track.play_count === 0"
|
||||||
|
class="button is-small"
|
||||||
|
@click="mark_played"
|
||||||
|
>Mark as played</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Album</span>
|
<span class="heading">Album</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_album">{{ track.album }}</a>
|
<a class="title is-6 has-text-link" @click="open_album">{{
|
||||||
|
track.album
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="track.album_artist && track.media_kind !== 'audiobook'">
|
<p
|
||||||
|
v-if="track.album_artist && track.media_kind !== 'audiobook'"
|
||||||
|
>
|
||||||
<span class="heading">Album artist</span>
|
<span class="heading">Album artist</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_artist">{{ track.album_artist }}</a>
|
<a class="title is-6 has-text-link" @click="open_artist">{{
|
||||||
|
track.album_artist
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="track.composer">
|
<p v-if="track.composer">
|
||||||
<span class="heading">Composer</span>
|
<span class="heading">Composer</span>
|
||||||
@ -31,7 +47,9 @@
|
|||||||
</p>
|
</p>
|
||||||
<p v-if="track.date_released">
|
<p v-if="track.date_released">
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ $filters.time(track.date_released, 'L') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(track.date_released, 'L')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="track.year > 0">
|
<p v-else-if="track.year > 0">
|
||||||
<span class="heading">Year</span>
|
<span class="heading">Year</span>
|
||||||
@ -39,15 +57,21 @@
|
|||||||
</p>
|
</p>
|
||||||
<p v-if="track.genre">
|
<p v-if="track.genre">
|
||||||
<span class="heading">Genre</span>
|
<span class="heading">Genre</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_genre">{{ track.genre }}</a>
|
<a class="title is-6 has-text-link" @click="open_genre">{{
|
||||||
|
track.genre
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Track / Disc</span>
|
<span class="heading">Track / Disc</span>
|
||||||
<span class="title is-6">{{ track.track_number }} / {{ track.disc_number }}</span>
|
<span class="title is-6"
|
||||||
|
>{{ track.track_number }} / {{ track.disc_number }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{ $filters.duration(track.length_ms) }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.duration(track.length_ms)
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Path</span>
|
<span class="heading">Path</span>
|
||||||
@ -55,24 +79,42 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Type</span>
|
<span class="heading">Type</span>
|
||||||
<span class="title is-6">{{ track.media_kind }} - {{ track.data_kind }} <span class="has-text-weight-normal" v-if="track.data_kind === 'spotify'">(<a @click="open_spotify_artist">artist</a>, <a @click="open_spotify_album">album</a>)</span></span>
|
<span class="title is-6"
|
||||||
|
>{{ track.media_kind }} - {{ track.data_kind }}
|
||||||
|
<span
|
||||||
|
v-if="track.data_kind === 'spotify'"
|
||||||
|
class="has-text-weight-normal"
|
||||||
|
>(<a @click="open_spotify_artist">artist</a>,
|
||||||
|
<a @click="open_spotify_album">album</a>)</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Quality</span>
|
<span class="heading">Quality</span>
|
||||||
<span class="title is-6">
|
<span class="title is-6">
|
||||||
{{ track.type }}
|
{{ track.type }}
|
||||||
<span v-if="track.samplerate"> | {{ track.samplerate }} Hz</span>
|
<span v-if="track.samplerate">
|
||||||
<span v-if="track.channels"> | {{ $filters.channels(track.channels) }}</span>
|
| {{ track.samplerate }} Hz</span
|
||||||
<span v-if="track.bitrate"> | {{ track.bitrate }} Kb/s</span>
|
>
|
||||||
|
<span v-if="track.channels">
|
||||||
|
| {{ $filters.channels(track.channels) }}</span
|
||||||
|
>
|
||||||
|
<span v-if="track.bitrate">
|
||||||
|
| {{ track.bitrate }} Kb/s</span
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
<span class="title is-6">{{ $filters.time(track.time_added, 'L LT') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(track.time_added, 'L LT')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Rating</span>
|
<span class="heading">Rating</span>
|
||||||
<span class="title is-6">{{ Math.floor(track.rating / 10) }} / 10</span>
|
<span class="title is-6"
|
||||||
|
>{{ Math.floor(track.rating / 10) }} / 10</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="track.comment">
|
<p v-if="track.comment">
|
||||||
<span class="heading">Comment</span>
|
<span class="heading">Comment</span>
|
||||||
@ -82,18 +124,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play_track">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -108,12 +157,28 @@ export default {
|
|||||||
|
|
||||||
props: ['show', 'track'],
|
props: ['show', 'track'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
spotify_track: {}
|
spotify_track: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
track() {
|
||||||
|
if (this.track && this.track.data_kind === 'spotify') {
|
||||||
|
const spotifyApi = new SpotifyWebApi()
|
||||||
|
spotifyApi.setAccessToken(this.$store.state.spotify.webapi_token)
|
||||||
|
spotifyApi
|
||||||
|
.getTrack(this.track.path.slice(this.track.path.lastIndexOf(':') + 1))
|
||||||
|
.then((response) => {
|
||||||
|
this.spotify_track = response
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.spotify_track = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play_track: function () {
|
play_track: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
@ -143,7 +208,9 @@ export default {
|
|||||||
|
|
||||||
open_artist: function () {
|
open_artist: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.$router.push({ path: '/music/artists/' + this.track.album_artist_id })
|
this.$router.push({
|
||||||
|
path: '/music/artists/' + this.track.album_artist_id
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_genre: function () {
|
open_genre: function () {
|
||||||
@ -152,44 +219,37 @@ export default {
|
|||||||
|
|
||||||
open_spotify_artist: function () {
|
open_spotify_artist: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.$router.push({ path: '/music/spotify/artists/' + this.spotify_track.artists[0].id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/artists/' + this.spotify_track.artists[0].id
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_spotify_album: function () {
|
open_spotify_album: function () {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.$router.push({ path: '/music/spotify/albums/' + this.spotify_track.album.id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/albums/' + this.spotify_track.album.id
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
mark_new: function () {
|
mark_new: function () {
|
||||||
webapi.library_track_update(this.track.id, { play_count: 'reset' }).then(() => {
|
webapi
|
||||||
this.$emit('play-count-changed')
|
.library_track_update(this.track.id, { play_count: 'reset' })
|
||||||
this.$emit('close')
|
.then(() => {
|
||||||
})
|
this.$emit('play-count-changed')
|
||||||
|
this.$emit('close')
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
mark_played: function () {
|
mark_played: function () {
|
||||||
webapi.library_track_update(this.track.id, { play_count: 'increment' }).then(() => {
|
webapi
|
||||||
this.$emit('play-count-changed')
|
.library_track_update(this.track.id, { play_count: 'increment' })
|
||||||
this.$emit('close')
|
.then(() => {
|
||||||
})
|
this.$emit('play-count-changed')
|
||||||
}
|
this.$emit('close')
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'track' () {
|
|
||||||
if (this.track && this.track.data_kind === 'spotify') {
|
|
||||||
const spotifyApi = new SpotifyWebApi()
|
|
||||||
spotifyApi.setAccessToken(this.$store.state.spotify.webapi_token)
|
|
||||||
spotifyApi.getTrack(this.track.path.slice(this.track.path.lastIndexOf(':') + 1)).then((response) => {
|
|
||||||
this.spotify_track = response
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
this.spotify_track = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<modal-dialog
|
<modal-dialog
|
||||||
:show="show"
|
:show="show"
|
||||||
title="Update library"
|
title="Update library"
|
||||||
:ok_action="library.updating ? '' : 'Rescan'"
|
:ok_action="library.updating ? '' : 'Rescan'"
|
||||||
close_action="Close"
|
close_action="Close"
|
||||||
@ok="update_library"
|
@ok="update_library"
|
||||||
@close="close()">
|
@close="close()"
|
||||||
<template v-slot:modal-content>
|
>
|
||||||
|
<template #modal-content>
|
||||||
<div v-if="!library.updating">
|
<div v-if="!library.updating">
|
||||||
<p class="mb-3">Scan for new, deleted and modified files</p>
|
<p class="mb-3">Scan for new, deleted and modified files</p>
|
||||||
<div class="field" v-if="spotify_enabled || rss.tracks > 0">
|
<div v-if="spotify_enabled || rss.tracks > 0" class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select is-small">
|
<div class="select is-small">
|
||||||
<select v-model="update_dialog_scan_kind">
|
<select v-model="update_dialog_scan_kind">
|
||||||
<option value="">Update everything</option>
|
<option value="">Update everything</option>
|
||||||
<option value="files">Only update local library</option>
|
<option value="files">Only update local library</option>
|
||||||
<option value="spotify" v-if="spotify_enabled">Only update Spotify</option>
|
<option v-if="spotify_enabled" value="spotify">
|
||||||
<option value="rss" v-if="rss.tracks > 0">Only update RSS feeds</option>
|
Only update Spotify
|
||||||
|
</option>
|
||||||
|
<option v-if="rss.tracks > 0" value="rss">
|
||||||
|
Only update RSS feeds
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="checkbox is-size-7 is-small">
|
<label class="checkbox is-size-7 is-small">
|
||||||
<input type="checkbox" v-model="rescan_metadata">
|
<input v-model="rescan_metadata" type="checkbox" />
|
||||||
Rescan metadata for unmodified files
|
Rescan metadata for unmodified files
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -45,37 +50,37 @@ export default {
|
|||||||
components: { ModalDialog },
|
components: { ModalDialog },
|
||||||
props: ['show'],
|
props: ['show'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
rescan_metadata: false
|
rescan_metadata: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
library () {
|
library() {
|
||||||
return this.$store.state.library
|
return this.$store.state.library
|
||||||
},
|
},
|
||||||
|
|
||||||
rss () {
|
rss() {
|
||||||
return this.$store.state.rss_count
|
return this.$store.state.rss_count
|
||||||
},
|
},
|
||||||
|
|
||||||
spotify_enabled () {
|
spotify_enabled() {
|
||||||
return this.$store.state.spotify.webapi_token_valid
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
},
|
},
|
||||||
|
|
||||||
update_dialog_scan_kind: {
|
update_dialog_scan_kind: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.update_dialog_scan_kind
|
return this.$store.state.update_dialog_scan_kind
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.UPDATE_DIALOG_SCAN_KIND, value)
|
this.$store.commit(types.UPDATE_DIALOG_SCAN_KIND, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
update_library () {
|
update_library() {
|
||||||
if (this.rescan_metadata) {
|
if (this.rescan_metadata) {
|
||||||
webapi.library_rescan(this.update_dialog_scan_kind)
|
webapi.library_rescan(this.update_dialog_scan_kind)
|
||||||
} else {
|
} else {
|
||||||
@ -83,7 +88,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
close () {
|
close() {
|
||||||
this.update_dialog_scan_kind = ''
|
this.update_dialog_scan_kind = ''
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
}
|
}
|
||||||
@ -91,5 +96,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,65 +1,140 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="fd-bottom-navbar navbar is-white is-fixed-bottom" :style="zindex" :class="{ 'is-transparent': is_now_playing_page, 'is-dark': !is_now_playing_page }" role="navigation" aria-label="player controls">
|
<nav
|
||||||
|
class="fd-bottom-navbar navbar is-white is-fixed-bottom"
|
||||||
|
:style="zindex"
|
||||||
|
:class="{
|
||||||
|
'is-transparent': is_now_playing_page,
|
||||||
|
'is-dark': !is_now_playing_page
|
||||||
|
}"
|
||||||
|
role="navigation"
|
||||||
|
aria-label="player controls"
|
||||||
|
>
|
||||||
<div class="navbar-brand fd-expanded">
|
<div class="navbar-brand fd-expanded">
|
||||||
|
|
||||||
<!-- Link to queue -->
|
<!-- Link to queue -->
|
||||||
<navbar-item-link to="/" exact>
|
<navbar-item-link to="/" exact>
|
||||||
<span class="icon"><i class="mdi mdi-24px mdi-playlist-play"></i></span>
|
<span class="icon"><i class="mdi mdi-24px mdi-playlist-play" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
|
|
||||||
<!-- Now playing artist/title (not visible on "now playing" page) -->
|
<!-- Now playing artist/title (not visible on "now playing" page) -->
|
||||||
<router-link to="/now-playing" v-if="!is_now_playing_page" class="navbar-item is-expanded is-clipped" active-class="is-active" exact>
|
<router-link
|
||||||
|
v-if="!is_now_playing_page"
|
||||||
|
to="/now-playing"
|
||||||
|
class="navbar-item is-expanded is-clipped"
|
||||||
|
active-class="is-active"
|
||||||
|
exact
|
||||||
|
>
|
||||||
<div class="is-clipped">
|
<div class="is-clipped">
|
||||||
<p class="is-size-7 fd-is-text-clipped">
|
<p class="is-size-7 fd-is-text-clipped">
|
||||||
<strong>{{ now_playing.title }}</strong><br>
|
<strong>{{ now_playing.title }}</strong
|
||||||
{{ now_playing.artist }}<span v-if="now_playing.data_kind === 'url'"> - {{ now_playing.album }}</span>
|
><br />
|
||||||
|
{{ now_playing.artist
|
||||||
|
}}<span v-if="now_playing.data_kind === 'url'">
|
||||||
|
- {{ now_playing.album }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<!-- Skip previous (not visible on "now playing" page) -->
|
<!-- Skip previous (not visible on "now playing" page) -->
|
||||||
<player-button-previous v-if="is_now_playing_page" class="navbar-item fd-margin-left-auto" icon_style="mdi-24px"></player-button-previous>
|
<player-button-previous
|
||||||
<player-button-seek-back v-if="is_now_playing_page" seek_ms="10000" class="navbar-item" icon_style="mdi-24px"></player-button-seek-back>
|
v-if="is_now_playing_page"
|
||||||
|
class="navbar-item fd-margin-left-auto"
|
||||||
|
icon_style="mdi-24px"
|
||||||
|
/>
|
||||||
|
<player-button-seek-back
|
||||||
|
v-if="is_now_playing_page"
|
||||||
|
seek_ms="10000"
|
||||||
|
class="navbar-item"
|
||||||
|
icon_style="mdi-24px"
|
||||||
|
/>
|
||||||
<!-- Play/pause -->
|
<!-- Play/pause -->
|
||||||
<player-button-play-pause class="navbar-item" icon_style="mdi-36px" show_disabled_message></player-button-play-pause>
|
<player-button-play-pause
|
||||||
<player-button-seek-forward v-if="is_now_playing_page" seek_ms="30000" class="navbar-item" icon_style="mdi-24px"></player-button-seek-forward>
|
class="navbar-item"
|
||||||
|
icon_style="mdi-36px"
|
||||||
|
show_disabled_message
|
||||||
|
/>
|
||||||
|
<player-button-seek-forward
|
||||||
|
v-if="is_now_playing_page"
|
||||||
|
seek_ms="30000"
|
||||||
|
class="navbar-item"
|
||||||
|
icon_style="mdi-24px"
|
||||||
|
/>
|
||||||
<!-- Skip next (not visible on "now playing" page) -->
|
<!-- Skip next (not visible on "now playing" page) -->
|
||||||
<player-button-next v-if="is_now_playing_page" class="navbar-item" icon_style="mdi-24px"></player-button-next>
|
<player-button-next
|
||||||
|
v-if="is_now_playing_page"
|
||||||
|
class="navbar-item"
|
||||||
|
icon_style="mdi-24px"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Player menu button (only visible on mobile and tablet) -->
|
<!-- Player menu button (only visible on mobile and tablet) -->
|
||||||
<a class="navbar-item fd-margin-left-auto is-hidden-desktop" @click="show_player_menu = !show_player_menu">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-chevron-up': !show_player_menu, 'mdi-chevron-down': show_player_menu }"></i></span>
|
class="navbar-item fd-margin-left-auto is-hidden-desktop"
|
||||||
|
@click="show_player_menu = !show_player_menu"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi mdi-18px"
|
||||||
|
:class="{
|
||||||
|
'mdi-chevron-up': !show_player_menu,
|
||||||
|
'mdi-chevron-down': show_player_menu
|
||||||
|
}"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- Player menu dropup menu (only visible on desktop) -->
|
<!-- Player menu dropup menu (only visible on desktop) -->
|
||||||
<div class="navbar-item has-dropdown has-dropdown-up fd-margin-left-auto is-hidden-touch"
|
<div
|
||||||
:class="{ 'is-active': show_player_menu }">
|
class="navbar-item has-dropdown has-dropdown-up fd-margin-left-auto is-hidden-touch"
|
||||||
<a class="navbar-link is-arrowless"
|
:class="{ 'is-active': show_player_menu }"
|
||||||
@click="show_player_menu = !show_player_menu">
|
>
|
||||||
<span class="icon"><i class="mdi mdi-18px"
|
<a
|
||||||
:class="{ 'mdi-chevron-up': !show_player_menu, 'mdi-chevron-down': show_player_menu }"></i></span>
|
class="navbar-link is-arrowless"
|
||||||
|
@click="show_player_menu = !show_player_menu"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi mdi-18px"
|
||||||
|
:class="{
|
||||||
|
'mdi-chevron-up': !show_player_menu,
|
||||||
|
'mdi-chevron-down': show_player_menu
|
||||||
|
}"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown is-right is-boxed" style="margin-right: 6px; margin-bottom: 6px; border-radius: 6px;">
|
<div
|
||||||
|
class="navbar-dropdown is-right is-boxed"
|
||||||
|
style="margin-right: 6px; margin-bottom: 6px; border-radius: 6px"
|
||||||
|
>
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<!-- Outputs: master volume -->
|
<!-- Outputs: master volume -->
|
||||||
<div class="level is-mobile">
|
<div class="level is-mobile">
|
||||||
<div class="level-left fd-expanded">
|
<div class="level-left fd-expanded">
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
<div class="level-item" style="flex-grow: 0">
|
||||||
<a class="button is-white is-small" @click="toggle_mute_volume">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-volume-off': player.volume <= 0, 'mdi-volume-high': player.volume > 0 }"></i></span>
|
class="button is-white is-small"
|
||||||
|
@click="toggle_mute_volume"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi mdi-18px"
|
||||||
|
:class="{
|
||||||
|
'mdi-volume-off': player.volume <= 0,
|
||||||
|
'mdi-volume-high': player.volume > 0
|
||||||
|
}"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item fd-expanded">
|
<div class="level-item fd-expanded">
|
||||||
<div class="fd-expanded">
|
<div class="fd-expanded">
|
||||||
<p class="heading">Volume</p>
|
<p class="heading">Volume</p>
|
||||||
<Slider v-model="player.volume"
|
<Slider
|
||||||
|
v-model="player.volume"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="1"
|
:step="1"
|
||||||
:tooltips="false"
|
:tooltips="false"
|
||||||
|
:classes="{ target: 'slider' }"
|
||||||
@change="set_volume"
|
@change="set_volume"
|
||||||
:classes="{ target: 'slider'}" />
|
/>
|
||||||
<!--range-slider
|
<!--range-slider
|
||||||
class="slider fd-has-action"
|
class="slider fd-has-action"
|
||||||
min="0"
|
min="0"
|
||||||
@ -75,28 +150,53 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Outputs: master volume -->
|
<!-- Outputs: master volume -->
|
||||||
<hr class="fd-navbar-divider">
|
<hr class="fd-navbar-divider" />
|
||||||
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
|
<navbar-item-output
|
||||||
|
v-for="output in outputs"
|
||||||
|
:key="output.id"
|
||||||
|
:output="output"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Outputs: stream volume -->
|
<!-- Outputs: stream volume -->
|
||||||
<hr class="fd-navbar-divider">
|
<hr class="fd-navbar-divider" />
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<div class="level is-mobile">
|
<div class="level is-mobile">
|
||||||
<div class="level-left fd-expanded">
|
<div class="level-left fd-expanded">
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
<div class="level-item" style="flex-grow: 0">
|
||||||
<a class="button is-white is-small" :class="{ 'is-loading': loading }"><span class="icon fd-has-action" :class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }" @click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i></span></a>
|
<a
|
||||||
|
class="button is-white is-small"
|
||||||
|
:class="{ 'is-loading': loading }"
|
||||||
|
><span
|
||||||
|
class="icon fd-has-action"
|
||||||
|
:class="{
|
||||||
|
'has-text-grey-light': !playing && !loading,
|
||||||
|
'is-loading': loading
|
||||||
|
}"
|
||||||
|
@click="togglePlay"
|
||||||
|
><i class="mdi mdi-18px mdi-radio-tower" /></span
|
||||||
|
></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item fd-expanded">
|
<div class="level-item fd-expanded">
|
||||||
<div class="fd-expanded">
|
<div class="fd-expanded">
|
||||||
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
|
<p
|
||||||
<Slider v-model="stream_volume"
|
class="heading"
|
||||||
|
:class="{ 'has-text-grey-light': !playing }"
|
||||||
|
>
|
||||||
|
HTTP stream
|
||||||
|
<a href="stream.mp3"
|
||||||
|
><span class="is-lowercase">(stream.mp3)</span></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
v-model="stream_volume"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="1"
|
:step="1"
|
||||||
:tooltips="false"
|
:tooltips="false"
|
||||||
:disabled="!playing"
|
:disabled="!playing"
|
||||||
|
:classes="{ target: 'slider' }"
|
||||||
@change="set_stream_volume"
|
@change="set_stream_volume"
|
||||||
:classes="{ target: 'slider'}" />
|
/>
|
||||||
<!--range-slider
|
<!--range-slider
|
||||||
class="slider fd-has-action"
|
class="slider fd-has-action"
|
||||||
min="0"
|
min="0"
|
||||||
@ -113,14 +213,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Playback controls -->
|
<!-- Playback controls -->
|
||||||
<hr class="fd-navbar-divider">
|
<hr class="fd-navbar-divider" />
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<div class="level is-mobile fd-expanded">
|
<div class="level is-mobile fd-expanded">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<div class="buttons has-addons">
|
<div class="buttons has-addons">
|
||||||
<player-button-repeat class="button"></player-button-repeat>
|
<player-button-repeat class="button" />
|
||||||
<player-button-shuffle class="button"></player-button-shuffle>
|
<player-button-shuffle class="button" />
|
||||||
<player-button-consume class="button"></player-button-consume>
|
<player-button-consume class="button" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -130,40 +230,51 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Player menu (only visible on mobile and tablet) -->
|
<!-- Player menu (only visible on mobile and tablet) -->
|
||||||
<div class="navbar-menu is-hidden-desktop" :class="{ 'is-active': show_player_menu }">
|
<div
|
||||||
<div class="navbar-start">
|
class="navbar-menu is-hidden-desktop"
|
||||||
</div>
|
:class="{ 'is-active': show_player_menu }"
|
||||||
|
>
|
||||||
|
<div class="navbar-start" />
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<!-- Repeat/shuffle/consume -->
|
<!-- Repeat/shuffle/consume -->
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<player-button-repeat class="button" icon_style="mdi-18px"></player-button-repeat>
|
<player-button-repeat class="button" icon_style="mdi-18px" />
|
||||||
<player-button-shuffle class="button" icon_style="mdi-18px"></player-button-shuffle>
|
<player-button-shuffle class="button" icon_style="mdi-18px" />
|
||||||
<player-button-consume class="button" icon_style="mdi-18px"></player-button-consume>
|
<player-button-consume class="button" icon_style="mdi-18px" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="fd-navbar-divider">
|
<hr class="fd-navbar-divider" />
|
||||||
|
|
||||||
<!-- Outputs: master volume -->
|
<!-- Outputs: master volume -->
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<div class="level is-mobile">
|
<div class="level is-mobile">
|
||||||
<div class="level-left fd-expanded">
|
<div class="level-left fd-expanded">
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
<div class="level-item" style="flex-grow: 0">
|
||||||
<a class="button is-white is-small" @click="toggle_mute_volume">
|
<a class="button is-white is-small" @click="toggle_mute_volume">
|
||||||
<span class="icon"><i class="mdi mdi-18px" :class="{ 'mdi-volume-off': player.volume <= 0, 'mdi-volume-high': player.volume > 0 }"></i></span>
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi mdi-18px"
|
||||||
|
:class="{
|
||||||
|
'mdi-volume-off': player.volume <= 0,
|
||||||
|
'mdi-volume-high': player.volume > 0
|
||||||
|
}"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item fd-expanded">
|
<div class="level-item fd-expanded">
|
||||||
<div class="fd-expanded">
|
<div class="fd-expanded">
|
||||||
<p class="heading">Volume</p>
|
<p class="heading">Volume</p>
|
||||||
<Slider v-model="player.volume"
|
<Slider
|
||||||
|
v-model="player.volume"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="1"
|
:step="1"
|
||||||
:tooltips="false"
|
:tooltips="false"
|
||||||
|
:classes="{ target: 'slider' }"
|
||||||
@change="set_volume"
|
@change="set_volume"
|
||||||
:classes="{ target: 'slider'}" />
|
/>
|
||||||
<!--range-slider
|
<!--range-slider
|
||||||
class="slider fd-has-action"
|
class="slider fd-has-action"
|
||||||
min="0"
|
min="0"
|
||||||
@ -179,32 +290,54 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Outputs: speaker volumes -->
|
<!-- Outputs: speaker volumes -->
|
||||||
<navbar-item-output v-for="output in outputs" :key="output.id" :output="output"></navbar-item-output>
|
<navbar-item-output
|
||||||
|
v-for="output in outputs"
|
||||||
|
:key="output.id"
|
||||||
|
:output="output"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Outputs: stream volume -->
|
<!-- Outputs: stream volume -->
|
||||||
<hr class="fd-navbar-divider">
|
<hr class="fd-navbar-divider" />
|
||||||
<div class="navbar-item fd-has-margin-bottom">
|
<div class="navbar-item fd-has-margin-bottom">
|
||||||
<div class="level is-mobile">
|
<div class="level is-mobile">
|
||||||
<div class="level-left fd-expanded">
|
<div class="level-left fd-expanded">
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
<div class="level-item" style="flex-grow: 0">
|
||||||
<a class="button is-white is-small" :class="{ 'is-loading': loading }">
|
<a
|
||||||
<span class="icon fd-has-action"
|
class="button is-white is-small"
|
||||||
:class="{ 'has-text-grey-light': !playing && !loading, 'is-loading': loading }"
|
:class="{ 'is-loading': loading }"
|
||||||
@click="togglePlay"><i class="mdi mdi-18px mdi-radio-tower"></i>
|
>
|
||||||
|
<span
|
||||||
|
class="icon fd-has-action"
|
||||||
|
:class="{
|
||||||
|
'has-text-grey-light': !playing && !loading,
|
||||||
|
'is-loading': loading
|
||||||
|
}"
|
||||||
|
@click="togglePlay"
|
||||||
|
><i class="mdi mdi-18px mdi-radio-tower" />
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item fd-expanded">
|
<div class="level-item fd-expanded">
|
||||||
<div class="fd-expanded">
|
<div class="fd-expanded">
|
||||||
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
|
<p
|
||||||
<Slider v-model="stream_volume"
|
class="heading"
|
||||||
|
:class="{ 'has-text-grey-light': !playing }"
|
||||||
|
>
|
||||||
|
HTTP stream
|
||||||
|
<a href="stream.mp3"
|
||||||
|
><span class="is-lowercase">(stream.mp3)</span></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
v-model="stream_volume"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="1"
|
:step="1"
|
||||||
:tooltips="false"
|
:tooltips="false"
|
||||||
:disabled="!playing"
|
:disabled="!playing"
|
||||||
|
:classes="{ target: 'slider' }"
|
||||||
@change="set_stream_volume"
|
@change="set_stream_volume"
|
||||||
:classes="{ target: 'slider'}" />
|
/>
|
||||||
<!-- range-slider
|
<!-- range-slider
|
||||||
class="slider fd-has-action"
|
class="slider fd-has-action"
|
||||||
min="0"
|
min="0"
|
||||||
@ -258,7 +391,7 @@ export default {
|
|||||||
PlayerButtonSeekBack
|
PlayerButtonSeekBack
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
old_volume: 0,
|
old_volume: 0,
|
||||||
|
|
||||||
@ -273,49 +406,67 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
show_player_menu: {
|
show_player_menu: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_player_menu
|
return this.$store.state.show_player_menu
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
show_burger_menu () {
|
show_burger_menu() {
|
||||||
return this.$store.state.show_burger_menu
|
return this.$store.state.show_burger_menu
|
||||||
},
|
},
|
||||||
|
|
||||||
zindex () {
|
zindex() {
|
||||||
if (this.show_burger_menu) {
|
if (this.show_burger_menu) {
|
||||||
return 'z-index: 20'
|
return 'z-index: 20'
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
},
|
},
|
||||||
|
|
||||||
state () {
|
state() {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
now_playing () {
|
now_playing() {
|
||||||
return this.$store.getters.now_playing
|
return this.$store.getters.now_playing
|
||||||
},
|
},
|
||||||
is_now_playing_page () {
|
is_now_playing_page() {
|
||||||
return this.$route.path === '/now-playing'
|
return this.$route.path === '/now-playing'
|
||||||
},
|
},
|
||||||
outputs () {
|
outputs() {
|
||||||
return this.$store.state.outputs
|
return this.$store.state.outputs
|
||||||
},
|
},
|
||||||
|
|
||||||
player () {
|
player() {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
|
|
||||||
config () {
|
config() {
|
||||||
return this.$store.state.config
|
return this.$store.state.config
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'$store.state.player.volume'() {
|
||||||
|
if (this.player.volume > 0) {
|
||||||
|
this.old_volume = this.player.volume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// on app mounted
|
||||||
|
mounted() {
|
||||||
|
this.setupAudio()
|
||||||
|
},
|
||||||
|
|
||||||
|
// on app destroyed
|
||||||
|
unmounted() {
|
||||||
|
this.closeAudio()
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
on_click_outside_outputs () {
|
on_click_outside_outputs() {
|
||||||
this.show_outputs_menu = false
|
this.show_outputs_menu = false
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -334,21 +485,24 @@ export default {
|
|||||||
setupAudio: function () {
|
setupAudio: function () {
|
||||||
const a = _audio.setupAudio()
|
const a = _audio.setupAudio()
|
||||||
|
|
||||||
a.addEventListener('waiting', e => {
|
a.addEventListener('waiting', (e) => {
|
||||||
this.playing = false
|
this.playing = false
|
||||||
this.loading = true
|
this.loading = true
|
||||||
})
|
})
|
||||||
a.addEventListener('playing', e => {
|
a.addEventListener('playing', (e) => {
|
||||||
this.playing = true
|
this.playing = true
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
a.addEventListener('ended', e => {
|
a.addEventListener('ended', (e) => {
|
||||||
this.playing = false
|
this.playing = false
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
a.addEventListener('error', e => {
|
a.addEventListener('error', (e) => {
|
||||||
this.closeAudio()
|
this.closeAudio()
|
||||||
this.$store.dispatch('add_notification', { text: 'HTTP stream error: failed to load stream or stopped loading due to network problem', type: 'danger' })
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'HTTP stream error: failed to load stream or stopped loading due to network problem',
|
||||||
|
type: 'danger'
|
||||||
|
})
|
||||||
this.playing = false
|
this.playing = false
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
@ -385,27 +539,8 @@ export default {
|
|||||||
this.stream_volume = newVolume
|
this.stream_volume = newVolume
|
||||||
_audio.setVolume(this.stream_volume / 100)
|
_audio.setVolume(this.stream_volume / 100)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'$store.state.player.volume' () {
|
|
||||||
if (this.player.volume > 0) {
|
|
||||||
this.old_volume = this.player.volume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// on app mounted
|
|
||||||
mounted () {
|
|
||||||
this.setupAudio()
|
|
||||||
},
|
|
||||||
|
|
||||||
// on app destroyed
|
|
||||||
destroyed () {
|
|
||||||
this.closeAudio()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<a class="navbar-item" :class="{ 'is-active': is_active }" @click.stop.prevent="open_link()" :href="full_path()">
|
<a
|
||||||
<slot></slot>
|
class="navbar-item"
|
||||||
|
:class="{ 'is-active': is_active }"
|
||||||
|
:href="full_path()"
|
||||||
|
@click.stop.prevent="open_link()"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,7 +20,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_active () {
|
is_active() {
|
||||||
if (this.exact) {
|
if (this.exact) {
|
||||||
return this.$route.path === this.to
|
return this.$route.path === this.to
|
||||||
}
|
}
|
||||||
@ -23,19 +28,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
show_player_menu: {
|
show_player_menu: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_player_menu
|
return this.$store.state.show_player_menu
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
this.$store.commit(types.SHOW_PLAYER_MENU, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
show_burger_menu: {
|
show_burger_menu: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_burger_menu
|
return this.$store.state.show_burger_menu
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,39 @@
|
|||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<div class="level is-mobile">
|
<div class="level is-mobile">
|
||||||
<div class="level-left fd-expanded">
|
<div class="level-left fd-expanded">
|
||||||
<div class="level-item" style="flex-grow: 0;">
|
<div class="level-item" style="flex-grow: 0">
|
||||||
<a class="button is-white is-small">
|
<a class="button is-white is-small">
|
||||||
<span class="icon fd-has-action"
|
<span
|
||||||
:class="{ 'has-text-grey-light': !output.selected }"
|
class="icon fd-has-action"
|
||||||
v-on:click="set_enabled">
|
:class="{ 'has-text-grey-light': !output.selected }"
|
||||||
<i class="mdi mdi-18px" :class="type_class" :title="output.type"></i>
|
@click="set_enabled"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="mdi mdi-18px"
|
||||||
|
:class="type_class"
|
||||||
|
:title="output.type"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item fd-expanded">
|
<div class="level-item fd-expanded">
|
||||||
<div class="fd-expanded">
|
<div class="fd-expanded">
|
||||||
<p class="heading" :class="{ 'has-text-grey-light': !output.selected }">{{ output.name }}</p>
|
<p
|
||||||
<Slider v-model="volume"
|
class="heading"
|
||||||
|
:class="{ 'has-text-grey-light': !output.selected }"
|
||||||
|
>
|
||||||
|
{{ output.name }}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
v-model="volume"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="1"
|
:step="1"
|
||||||
:tooltips="false"
|
:tooltips="false"
|
||||||
:disabled="!output.selected"
|
:disabled="!output.selected"
|
||||||
|
:classes="{ target: 'slider' }"
|
||||||
@change="set_volume"
|
@change="set_volume"
|
||||||
:classes="{ target: 'slider'}" />
|
/>
|
||||||
<!--range-slider
|
<!--range-slider
|
||||||
class="slider fd-has-action"
|
class="slider fd-has-action"
|
||||||
min="0"
|
min="0"
|
||||||
@ -45,15 +58,15 @@ import webapi from '@/webapi'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NavbarItemOutput',
|
name: 'NavbarItemOutput',
|
||||||
components: {
|
components: {
|
||||||
// RangeSlider
|
// RangeSlider
|
||||||
Slider
|
Slider
|
||||||
},
|
},
|
||||||
|
|
||||||
props: ['output'],
|
props: ['output'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
type_class () {
|
type_class() {
|
||||||
if (this.output.type.startsWith('AirPlay')) {
|
if (this.output.type.startsWith('AirPlay')) {
|
||||||
return 'mdi-airplay'
|
return 'mdi-airplay'
|
||||||
} else if (this.output.type === 'Chromecast') {
|
} else if (this.output.type === 'Chromecast') {
|
||||||
@ -65,7 +78,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
volume () {
|
volume() {
|
||||||
return this.output.selected ? this.output.volume : 0
|
return this.output.selected ? this.output.volume : 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -89,5 +102,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,80 +1,134 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="fd-top-navbar navbar is-light is-fixed-top" :style="zindex" role="navigation" aria-label="main navigation">
|
<nav
|
||||||
|
class="fd-top-navbar navbar is-light is-fixed-top"
|
||||||
|
:style="zindex"
|
||||||
|
role="navigation"
|
||||||
|
aria-label="main navigation"
|
||||||
|
>
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<navbar-item-link to="/playlists" v-if="is_visible_playlists">
|
<navbar-item-link v-if="is_visible_playlists" to="/playlists">
|
||||||
<span class="icon"><i class="mdi mdi-library-music"></i></span>
|
<span class="icon"><i class="mdi mdi-library-music" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/music" v-if="is_visible_music">
|
<navbar-item-link v-if="is_visible_music" to="/music">
|
||||||
<span class="icon"><i class="mdi mdi-music"></i></span>
|
<span class="icon"><i class="mdi mdi-music" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/podcasts" v-if="is_visible_podcasts">
|
<navbar-item-link v-if="is_visible_podcasts" to="/podcasts">
|
||||||
<span class="icon"><i class="mdi mdi-microphone"></i></span>
|
<span class="icon"><i class="mdi mdi-microphone" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/audiobooks" v-if="is_visible_audiobooks">
|
<navbar-item-link v-if="is_visible_audiobooks" to="/audiobooks">
|
||||||
<span class="icon"><i class="mdi mdi-book-open-variant"></i></span>
|
<span class="icon"><i class="mdi mdi-book-open-variant" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/radio" v-if="is_visible_radio">
|
<navbar-item-link v-if="is_visible_radio" to="/radio">
|
||||||
<span class="icon"><i class="mdi mdi-radio"></i></span>
|
<span class="icon"><i class="mdi mdi-radio" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/files" v-if="is_visible_files">
|
<navbar-item-link v-if="is_visible_files" to="/files">
|
||||||
<span class="icon"><i class="mdi mdi-folder-open"></i></span>
|
<span class="icon"><i class="mdi mdi-folder-open" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/search" v-if="is_visible_search">
|
<navbar-item-link v-if="is_visible_search" to="/search">
|
||||||
<span class="icon"><i class="mdi mdi-magnify"></i></span>
|
<span class="icon"><i class="mdi mdi-magnify" /></span>
|
||||||
</navbar-item-link>
|
</navbar-item-link>
|
||||||
|
|
||||||
<div class="navbar-burger" @click="show_burger_menu = !show_burger_menu" :class="{ 'is-active': show_burger_menu }">
|
<div
|
||||||
<span></span>
|
class="navbar-burger"
|
||||||
<span></span>
|
:class="{ 'is-active': show_burger_menu }"
|
||||||
<span></span>
|
@click="show_burger_menu = !show_burger_menu"
|
||||||
|
>
|
||||||
|
<span />
|
||||||
|
<span />
|
||||||
|
<span />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-menu" :class="{ 'is-active': show_burger_menu }">
|
<div class="navbar-menu" :class="{ 'is-active': show_burger_menu }">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
|
|
||||||
<!-- Burger menu entries -->
|
<!-- Burger menu entries -->
|
||||||
<div class="navbar-item has-dropdown is-hoverable"
|
<div
|
||||||
:class="{ 'is-active': show_settings_menu }"
|
class="navbar-item has-dropdown is-hoverable"
|
||||||
@click="on_click_outside_settings">
|
:class="{ 'is-active': show_settings_menu }"
|
||||||
|
@click="on_click_outside_settings"
|
||||||
|
>
|
||||||
<a class="navbar-link is-arrowless">
|
<a class="navbar-link is-arrowless">
|
||||||
<span class="icon is-hidden-touch"><i class="mdi mdi-24px mdi-menu"></i></span>
|
<span class="icon is-hidden-touch"
|
||||||
|
><i class="mdi mdi-24px mdi-menu"
|
||||||
|
/></span>
|
||||||
<span class="is-hidden-desktop has-text-weight-bold">OwnTone</span>
|
<span class="is-hidden-desktop has-text-weight-bold">OwnTone</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown is-right">
|
<div class="navbar-dropdown is-right">
|
||||||
|
<navbar-item-link to="/playlists">
|
||||||
|
<span class="icon"><i class="mdi mdi-library-music" /></span>
|
||||||
|
<b>Playlists</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/music" exact>
|
||||||
|
<span class="icon"><i class="mdi mdi-music" /></span>
|
||||||
|
<b>Music</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/artists">
|
||||||
|
<span class="fd-navbar-item-level2">Artists</span>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/albums">
|
||||||
|
<span class="fd-navbar-item-level2">Albums</span>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/genres">
|
||||||
|
<span class="fd-navbar-item-level2">Genres</span>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link v-if="spotify_enabled" to="/music/spotify">
|
||||||
|
<span class="fd-navbar-item-level2">Spotify</span>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/podcasts">
|
||||||
|
<span class="icon"><i class="mdi mdi-microphone" /></span>
|
||||||
|
<b>Podcasts</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/audiobooks">
|
||||||
|
<span class="icon"><i class="mdi mdi-book-open-variant" /></span>
|
||||||
|
<b>Audiobooks</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/radio">
|
||||||
|
<span class="icon"><i class="mdi mdi-radio" /></span>
|
||||||
|
<b>Radio</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/files">
|
||||||
|
<span class="icon"><i class="mdi mdi-folder-open" /></span>
|
||||||
|
<b>Files</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<navbar-item-link to="/search">
|
||||||
|
<span class="icon"><i class="mdi mdi-magnify" /></span>
|
||||||
|
<b>Search</b>
|
||||||
|
</navbar-item-link>
|
||||||
|
<hr class="fd-navbar-divider" />
|
||||||
|
|
||||||
<navbar-item-link to="/playlists"><span class="icon"><i class="mdi mdi-library-music"></i></span> <b>Playlists</b></navbar-item-link>
|
<navbar-item-link to="/settings/webinterface">
|
||||||
<navbar-item-link to="/music" exact><span class="icon"><i class="mdi mdi-music"></i></span> <b>Music</b></navbar-item-link>
|
Settings
|
||||||
<navbar-item-link to="/music/artists"><span class="fd-navbar-item-level2">Artists</span></navbar-item-link>
|
</navbar-item-link>
|
||||||
<navbar-item-link to="/music/albums"><span class="fd-navbar-item-level2">Albums</span></navbar-item-link>
|
<a
|
||||||
<navbar-item-link to="/music/genres"><span class="fd-navbar-item-level2">Genres</span></navbar-item-link>
|
class="navbar-item"
|
||||||
<navbar-item-link to="/music/spotify" v-if="spotify_enabled"><span class="fd-navbar-item-level2">Spotify</span></navbar-item-link>
|
@click.stop.prevent="
|
||||||
<navbar-item-link to="/podcasts"><span class="icon"><i class="mdi mdi-microphone"></i></span> <b>Podcasts</b></navbar-item-link>
|
show_update_dialog = true
|
||||||
<navbar-item-link to="/audiobooks"><span class="icon"><i class="mdi mdi-book-open-variant"></i></span> <b>Audiobooks</b></navbar-item-link>
|
show_settings_menu = false
|
||||||
<navbar-item-link to="/radio"><span class="icon"><i class="mdi mdi-radio"></i></span> <b>Radio</b></navbar-item-link>
|
show_burger_menu = false
|
||||||
<navbar-item-link to="/files"><span class="icon"><i class="mdi mdi-folder-open"></i></span> <b>Files</b></navbar-item-link>
|
"
|
||||||
<navbar-item-link to="/search"><span class="icon"><i class="mdi mdi-magnify"></i></span> <b>Search</b></navbar-item-link>
|
>
|
||||||
<hr class="fd-navbar-divider">
|
|
||||||
|
|
||||||
<navbar-item-link to="/settings/webinterface">Settings</navbar-item-link>
|
|
||||||
<a class="navbar-item" @click.stop.prevent="show_update_dialog = true; show_settings_menu = false; show_burger_menu = false">
|
|
||||||
Update Library
|
Update Library
|
||||||
</a>
|
</a>
|
||||||
<navbar-item-link to="/about">About</navbar-item-link>
|
<navbar-item-link to="/about"> About </navbar-item-link>
|
||||||
|
|
||||||
<div class="navbar-item is-hidden-desktop" style="margin-bottom: 2.5rem;"></div>
|
<div
|
||||||
|
class="navbar-item is-hidden-desktop"
|
||||||
|
style="margin-bottom: 2.5rem"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="is-overlay" v-show="show_settings_menu"
|
<div
|
||||||
style="z-index:10; width: 100vw; height:100vh;"
|
v-show="show_settings_menu"
|
||||||
@click="show_settings_menu = false"></div>
|
class="is-overlay"
|
||||||
|
style="z-index: 10; width: 100vw; height: 100vh"
|
||||||
|
@click="show_settings_menu = false"
|
||||||
|
/>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -86,82 +140,103 @@ export default {
|
|||||||
name: 'NavbarTop',
|
name: 'NavbarTop',
|
||||||
components: { NavbarItemLink },
|
components: { NavbarItemLink },
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_settings_menu: false
|
show_settings_menu: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_visible_playlists () {
|
is_visible_playlists() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_playlists').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_playlists'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
is_visible_music () {
|
is_visible_music() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_music').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_music'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
is_visible_podcasts () {
|
is_visible_podcasts() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_podcasts').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_podcasts'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
is_visible_audiobooks () {
|
is_visible_audiobooks() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_audiobooks').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_audiobooks'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
is_visible_radio () {
|
is_visible_radio() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_radio').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_radio'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
is_visible_files () {
|
is_visible_files() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_files').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_files'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
is_visible_search () {
|
is_visible_search() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_menu_item_search').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_menu_item_search'
|
||||||
|
).value
|
||||||
},
|
},
|
||||||
|
|
||||||
player () {
|
player() {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
|
|
||||||
config () {
|
config() {
|
||||||
return this.$store.state.config
|
return this.$store.state.config
|
||||||
},
|
},
|
||||||
|
|
||||||
library () {
|
library() {
|
||||||
return this.$store.state.library
|
return this.$store.state.library
|
||||||
},
|
},
|
||||||
|
|
||||||
audiobooks () {
|
audiobooks() {
|
||||||
return this.$store.state.audiobooks_count
|
return this.$store.state.audiobooks_count
|
||||||
},
|
},
|
||||||
|
|
||||||
podcasts () {
|
podcasts() {
|
||||||
return this.$store.state.podcasts_count
|
return this.$store.state.podcasts_count
|
||||||
},
|
},
|
||||||
|
|
||||||
spotify_enabled () {
|
spotify_enabled() {
|
||||||
return this.$store.state.spotify.webapi_token_valid
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
},
|
},
|
||||||
|
|
||||||
show_burger_menu: {
|
show_burger_menu: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_burger_menu
|
return this.$store.state.show_burger_menu
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
this.$store.commit(types.SHOW_BURGER_MENU, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
show_player_menu () {
|
show_player_menu() {
|
||||||
return this.$store.state.show_player_menu
|
return this.$store.state.show_player_menu
|
||||||
},
|
},
|
||||||
|
|
||||||
show_update_dialog: {
|
show_update_dialog: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.show_update_dialog
|
return this.$store.state.show_update_dialog
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.SHOW_UPDATE_DIALOG, value)
|
this.$store.commit(types.SHOW_UPDATE_DIALOG, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
zindex () {
|
zindex() {
|
||||||
if (this.show_player_menu) {
|
if (this.show_player_menu) {
|
||||||
return 'z-index: 20'
|
return 'z-index: 20'
|
||||||
}
|
}
|
||||||
@ -169,19 +244,18 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
watch: {
|
||||||
on_click_outside_settings () {
|
$route(to, from) {
|
||||||
this.show_settings_menu = !this.show_settings_menu
|
this.show_settings_menu = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
methods: {
|
||||||
$route (to, from) {
|
on_click_outside_settings() {
|
||||||
this.show_settings_menu = false
|
this.show_settings_menu = !this.show_settings_menu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="fd-notifications" v-if="notifications.length > 0">
|
<section v-if="notifications.length > 0" class="fd-notifications">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-half">
|
<div class="column is-half">
|
||||||
<div class="notification has-shadow " v-for="notification in notifications" :key="notification.id" :class="['notification', notification.type ? `is-${notification.type}` : '']">
|
<div
|
||||||
<button class="delete" v-on:click="remove(notification)"></button>
|
v-for="notification in notifications"
|
||||||
|
:key="notification.id"
|
||||||
|
class="notification has-shadow"
|
||||||
|
:class="[
|
||||||
|
'notification',
|
||||||
|
notification.type ? `is-${notification.type}` : ''
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<button class="delete" @click="remove(notification)" />
|
||||||
{{ notification.text }}
|
{{ notification.text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -16,14 +24,14 @@ import * as types from '@/store/mutation_types'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Notifications',
|
name: 'Notifications',
|
||||||
components: { },
|
components: {},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return { showNav: false }
|
return { showNav: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
notifications () {
|
notifications() {
|
||||||
return this.$store.state.notifications.list
|
return this.$store.state.notifications.list
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="toggle_consume_mode" :class="{ 'is-warning': is_consume }">
|
<a :class="{ 'is-warning': is_consume }" @click="toggle_consume_mode">
|
||||||
<span class="icon"><i class="mdi mdi-fire" :class="icon_style"></i></span>
|
<span class="icon"><i class="mdi mdi-fire" :class="icon_style" /></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_consume () {
|
is_consume() {
|
||||||
return this.$store.state.player.consume
|
return this.$store.state.player.consume
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -28,5 +28,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="play_next" :disabled="disabled">
|
<a :disabled="disabled" @click="play_next">
|
||||||
<span class="icon"><i class="mdi mdi-skip-forward" :class="icon_style"></i></span>
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-skip-forward" :class="icon_style"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
disabled () {
|
disabled() {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -32,5 +34,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="toggle_play_pause" :disabled="disabled">
|
<a :disabled="disabled" @click="toggle_play_pause">
|
||||||
<span class="icon"><i class="mdi" :class="[icon_style, { 'mdi-play': !is_playing, 'mdi-pause': is_playing && is_pause_allowed, 'mdi-stop': is_playing && !is_pause_allowed }]"></i></span>
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi"
|
||||||
|
:class="[
|
||||||
|
icon_style,
|
||||||
|
{
|
||||||
|
'mdi-play': !is_playing,
|
||||||
|
'mdi-pause': is_playing && is_pause_allowed,
|
||||||
|
'mdi-stop': is_playing && !is_pause_allowed
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -16,16 +27,18 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_playing () {
|
is_playing() {
|
||||||
return this.$store.state.player.state === 'play'
|
return this.$store.state.player.state === 'play'
|
||||||
},
|
},
|
||||||
|
|
||||||
is_pause_allowed () {
|
is_pause_allowed() {
|
||||||
return (this.$store.getters.now_playing &&
|
return (
|
||||||
this.$store.getters.now_playing.data_kind !== 'pipe')
|
this.$store.getters.now_playing &&
|
||||||
|
this.$store.getters.now_playing.data_kind !== 'pipe'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
disabled () {
|
disabled() {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -34,7 +47,12 @@ export default {
|
|||||||
toggle_play_pause: function () {
|
toggle_play_pause: function () {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
if (this.show_disabled_message) {
|
if (this.show_disabled_message) {
|
||||||
this.$store.dispatch('add_notification', { text: 'Queue is empty', type: 'info', topic: 'connection', timeout: 2000 })
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'Queue is empty',
|
||||||
|
type: 'info',
|
||||||
|
topic: 'connection',
|
||||||
|
timeout: 2000
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -51,5 +69,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="play_previous" :disabled="disabled">
|
<a :disabled="disabled" @click="play_previous">
|
||||||
<span class="icon"><i class="mdi mdi-skip-backward" :class="icon_style"></i></span>
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-skip-backward" :class="icon_style"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
disabled () {
|
disabled() {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
return !this.$store.state.queue || this.$store.state.queue.count <= 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -32,5 +34,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="toggle_repeat_mode" :class="{ 'is-warning': !is_repeat_off }">
|
<a :class="{ 'is-warning': !is_repeat_off }" @click="toggle_repeat_mode">
|
||||||
<span class="icon"><i class="mdi" :class="[icon_style, { 'mdi-repeat': is_repeat_all, 'mdi-repeat-once': is_repeat_single, 'mdi-repeat-off': is_repeat_off }]"></i></span>
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi"
|
||||||
|
:class="[
|
||||||
|
icon_style,
|
||||||
|
{
|
||||||
|
'mdi-repeat': is_repeat_all,
|
||||||
|
'mdi-repeat-once': is_repeat_single,
|
||||||
|
'mdi-repeat-off': is_repeat_off
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,13 +26,13 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_repeat_all () {
|
is_repeat_all() {
|
||||||
return this.$store.state.player.repeat === 'all'
|
return this.$store.state.player.repeat === 'all'
|
||||||
},
|
},
|
||||||
is_repeat_single () {
|
is_repeat_single() {
|
||||||
return this.$store.state.player.repeat === 'single'
|
return this.$store.state.player.repeat === 'single'
|
||||||
},
|
},
|
||||||
is_repeat_off () {
|
is_repeat_off() {
|
||||||
return !this.is_repeat_all && !this.is_repeat_single
|
return !this.is_repeat_all && !this.is_repeat_single
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -40,5 +51,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="seek" :disabled="disabled" v-if="visible">
|
<a v-if="visible" :disabled="disabled" @click="seek">
|
||||||
<span class="icon"><i class="mdi mdi-rewind" :class="icon_style"></i></span>
|
<span class="icon"><i class="mdi mdi-rewind" :class="icon_style" /></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -12,17 +12,21 @@ export default {
|
|||||||
props: ['seek_ms', 'icon_style'],
|
props: ['seek_ms', 'icon_style'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
now_playing () {
|
now_playing() {
|
||||||
return this.$store.getters.now_playing
|
return this.$store.getters.now_playing
|
||||||
},
|
},
|
||||||
is_stopped () {
|
is_stopped() {
|
||||||
return this.$store.state.player.state === 'stop'
|
return this.$store.state.player.state === 'stop'
|
||||||
},
|
},
|
||||||
disabled () {
|
disabled() {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0 || this.is_stopped ||
|
return (
|
||||||
this.now_playing.data_kind === 'pipe'
|
!this.$store.state.queue ||
|
||||||
|
this.$store.state.queue.count <= 0 ||
|
||||||
|
this.is_stopped ||
|
||||||
|
this.now_playing.data_kind === 'pipe'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
visible () {
|
visible() {
|
||||||
return ['podcast', 'audiobook'].includes(this.now_playing.media_kind)
|
return ['podcast', 'audiobook'].includes(this.now_playing.media_kind)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="seek" :disabled="disabled" v-if="visible">
|
<a v-if="visible" :disabled="disabled" @click="seek">
|
||||||
<span class="icon"><i class="mdi mdi-fast-forward" :class="icon_style"></i></span>
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-fast-forward" :class="icon_style"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -12,17 +14,21 @@ export default {
|
|||||||
props: ['seek_ms', 'icon_style'],
|
props: ['seek_ms', 'icon_style'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
now_playing () {
|
now_playing() {
|
||||||
return this.$store.getters.now_playing
|
return this.$store.getters.now_playing
|
||||||
},
|
},
|
||||||
is_stopped () {
|
is_stopped() {
|
||||||
return this.$store.state.player.state === 'stop'
|
return this.$store.state.player.state === 'stop'
|
||||||
},
|
},
|
||||||
disabled () {
|
disabled() {
|
||||||
return !this.$store.state.queue || this.$store.state.queue.count <= 0 || this.is_stopped ||
|
return (
|
||||||
this.now_playing.data_kind === 'pipe'
|
!this.$store.state.queue ||
|
||||||
|
this.$store.state.queue.count <= 0 ||
|
||||||
|
this.is_stopped ||
|
||||||
|
this.now_playing.data_kind === 'pipe'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
visible () {
|
visible() {
|
||||||
return ['podcast', 'audiobook'].includes(this.now_playing.media_kind)
|
return ['podcast', 'audiobook'].includes(this.now_playing.media_kind)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="toggle_shuffle_mode" :class="{ 'is-warning': is_shuffle }">
|
<a :class="{ 'is-warning': is_shuffle }" @click="toggle_shuffle_mode">
|
||||||
<span class="icon"><i class="mdi" :class="[icon_style, { 'mdi-shuffle': is_shuffle, 'mdi-shuffle-disabled': !is_shuffle }]"></i></span>
|
<span class="icon"
|
||||||
|
><i
|
||||||
|
class="mdi"
|
||||||
|
:class="[
|
||||||
|
icon_style,
|
||||||
|
{ 'mdi-shuffle': is_shuffle, 'mdi-shuffle-disabled': !is_shuffle }
|
||||||
|
]"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,7 +22,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
is_shuffle () {
|
is_shuffle() {
|
||||||
return this.$store.state.player.shuffle
|
return this.$store.state.player.shuffle
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -28,5 +35,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="width > 0" class="progress-bar mt-2" :style="{ width: width_percent }" />
|
<div
|
||||||
|
v-if="width > 0"
|
||||||
|
class="progress-bar mt-2"
|
||||||
|
:style="{ width: width_percent }"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -8,13 +12,13 @@ export default {
|
|||||||
props: ['max', 'value'],
|
props: ['max', 'value'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
width () {
|
width() {
|
||||||
if (this.value > 0 && this.max > 0) {
|
if (this.value > 0 && this.max > 0) {
|
||||||
return parseInt(this.value * 100 / this.max)
|
return parseInt((this.value * 100) / this.max)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
width_percent () {
|
width_percent() {
|
||||||
return this.width + '%'
|
return this.width + '%'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="checkbox"
|
<input
|
||||||
:checked="value"
|
ref="settings_checkbox"
|
||||||
@change="set_update_timer"
|
type="checkbox"
|
||||||
ref="settings_checkbox">
|
:checked="value"
|
||||||
<slot name="label"></slot>
|
@change="set_update_timer"
|
||||||
<i class="is-size-7"
|
/>
|
||||||
:class="{
|
<slot name="label" />
|
||||||
'has-text-info': statusUpdate === 'success',
|
<i
|
||||||
'has-text-danger': statusUpdate === 'error'
|
class="is-size-7"
|
||||||
}"> {{ info }}</i>
|
:class="{
|
||||||
|
'has-text-info': statusUpdate === 'success',
|
||||||
|
'has-text-danger': statusUpdate === 'error'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ info }}</i
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
<p class="help" v-if="$slots['info']">
|
<p v-if="$slots['info']" class="help">
|
||||||
<slot name="info"></slot>
|
<slot name="info" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -27,7 +33,7 @@ export default {
|
|||||||
|
|
||||||
props: ['category_name', 'option_name'],
|
props: ['category_name', 'option_name'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
timerDelay: 2000,
|
timerDelay: 2000,
|
||||||
timerId: -1,
|
timerId: -1,
|
||||||
@ -38,22 +44,26 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
category () {
|
category() {
|
||||||
return this.$store.state.settings.categories.find(elem => elem.name === this.category_name)
|
return this.$store.state.settings.categories.find(
|
||||||
|
(elem) => elem.name === this.category_name
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
option () {
|
option() {
|
||||||
if (!this.category) {
|
if (!this.category) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
return this.category.options.find(elem => elem.name === this.option_name)
|
return this.category.options.find(
|
||||||
|
(elem) => elem.name === this.option_name
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
value () {
|
value() {
|
||||||
return this.option.value
|
return this.option.value
|
||||||
},
|
},
|
||||||
|
|
||||||
info () {
|
info() {
|
||||||
if (this.statusUpdate === 'success') {
|
if (this.statusUpdate === 'success') {
|
||||||
return '(setting saved)'
|
return '(setting saved)'
|
||||||
} else if (this.statusUpdate === 'error') {
|
} else if (this.statusUpdate === 'error') {
|
||||||
@ -64,7 +74,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
set_update_timer () {
|
set_update_timer() {
|
||||||
if (this.timerId > 0) {
|
if (this.timerId > 0) {
|
||||||
window.clearTimeout(this.timerId)
|
window.clearTimeout(this.timerId)
|
||||||
this.timerId = -1
|
this.timerId = -1
|
||||||
@ -77,7 +87,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update_setting () {
|
update_setting() {
|
||||||
this.timerId = -1
|
this.timerId = -1
|
||||||
|
|
||||||
const newValue = this.$refs.settings_checkbox.checked
|
const newValue = this.$refs.settings_checkbox.checked
|
||||||
@ -92,15 +102,19 @@ export default {
|
|||||||
name: this.option_name,
|
name: this.option_name,
|
||||||
value: newValue
|
value: newValue
|
||||||
}
|
}
|
||||||
webapi.settings_update(this.category.name, option).then(() => {
|
webapi
|
||||||
this.$store.commit(types.UPDATE_SETTINGS_OPTION, option)
|
.settings_update(this.category.name, option)
|
||||||
this.statusUpdate = 'success'
|
.then(() => {
|
||||||
}).catch(() => {
|
this.$store.commit(types.UPDATE_SETTINGS_OPTION, option)
|
||||||
this.statusUpdate = 'error'
|
this.statusUpdate = 'success'
|
||||||
this.$refs.settings_checkbox.checked = this.value
|
})
|
||||||
}).finally(() => {
|
.catch(() => {
|
||||||
this.timerId = window.setTimeout(this.clear_status, this.timerDelay)
|
this.statusUpdate = 'error'
|
||||||
})
|
this.$refs.settings_checkbox.checked = this.value
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.timerId = window.setTimeout(this.clear_status, this.timerDelay)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
clear_status: function () {
|
clear_status: function () {
|
||||||
@ -110,5 +124,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,25 +2,31 @@
|
|||||||
<fieldset :disabled="disabled">
|
<fieldset :disabled="disabled">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label has-text-weight-normal">
|
<label class="label has-text-weight-normal">
|
||||||
<slot name="label"></slot>
|
<slot name="label" />
|
||||||
<i class="is-size-7"
|
<i
|
||||||
:class="{
|
class="is-size-7"
|
||||||
'has-text-info': statusUpdate === 'success',
|
:class="{
|
||||||
'has-text-danger': statusUpdate === 'error'
|
'has-text-info': statusUpdate === 'success',
|
||||||
}"> {{ info }}</i>
|
'has-text-danger': statusUpdate === 'error'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ info }}</i
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input"
|
<input
|
||||||
type="number"
|
ref="settings_number"
|
||||||
min="0"
|
class="input"
|
||||||
style="width: 10em;"
|
type="number"
|
||||||
:placeholder="placeholder"
|
min="0"
|
||||||
:value="value"
|
style="width: 10em"
|
||||||
@input="set_update_timer"
|
:placeholder="placeholder"
|
||||||
ref="settings_number">
|
:value="value"
|
||||||
|
@input="set_update_timer"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p class="help" v-if="$slots['info']">
|
<p v-if="$slots['info']" class="help">
|
||||||
<slot name="info"></slot>
|
<slot name="info" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -35,7 +41,7 @@ export default {
|
|||||||
|
|
||||||
props: ['category_name', 'option_name', 'placeholder', 'disabled'],
|
props: ['category_name', 'option_name', 'placeholder', 'disabled'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
timerDelay: 2000,
|
timerDelay: 2000,
|
||||||
timerId: -1,
|
timerId: -1,
|
||||||
@ -45,22 +51,26 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
category () {
|
category() {
|
||||||
return this.$store.state.settings.categories.find(elem => elem.name === this.category_name)
|
return this.$store.state.settings.categories.find(
|
||||||
|
(elem) => elem.name === this.category_name
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
option () {
|
option() {
|
||||||
if (!this.category) {
|
if (!this.category) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
return this.category.options.find(elem => elem.name === this.option_name)
|
return this.category.options.find(
|
||||||
|
(elem) => elem.name === this.option_name
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
value () {
|
value() {
|
||||||
return this.option.value
|
return this.option.value
|
||||||
},
|
},
|
||||||
|
|
||||||
info () {
|
info() {
|
||||||
if (this.statusUpdate === 'success') {
|
if (this.statusUpdate === 'success') {
|
||||||
return '(setting saved)'
|
return '(setting saved)'
|
||||||
} else if (this.statusUpdate === 'error') {
|
} else if (this.statusUpdate === 'error') {
|
||||||
@ -71,7 +81,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
set_update_timer () {
|
set_update_timer() {
|
||||||
if (this.timerId > 0) {
|
if (this.timerId > 0) {
|
||||||
window.clearTimeout(this.timerId)
|
window.clearTimeout(this.timerId)
|
||||||
this.timerId = -1
|
this.timerId = -1
|
||||||
@ -84,7 +94,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update_setting () {
|
update_setting() {
|
||||||
this.timerId = -1
|
this.timerId = -1
|
||||||
|
|
||||||
const newValue = this.$refs.settings_number.value
|
const newValue = this.$refs.settings_number.value
|
||||||
@ -98,15 +108,19 @@ export default {
|
|||||||
name: this.option_name,
|
name: this.option_name,
|
||||||
value: parseInt(newValue, 10)
|
value: parseInt(newValue, 10)
|
||||||
}
|
}
|
||||||
webapi.settings_update(this.category.name, option).then(() => {
|
webapi
|
||||||
this.$store.commit(types.UPDATE_SETTINGS_OPTION, option)
|
.settings_update(this.category.name, option)
|
||||||
this.statusUpdate = 'success'
|
.then(() => {
|
||||||
}).catch(() => {
|
this.$store.commit(types.UPDATE_SETTINGS_OPTION, option)
|
||||||
this.statusUpdate = 'error'
|
this.statusUpdate = 'success'
|
||||||
this.$refs.settings_number.value = this.value
|
})
|
||||||
}).finally(() => {
|
.catch(() => {
|
||||||
this.timerId = window.setTimeout(this.clear_status, this.timerDelay)
|
this.statusUpdate = 'error'
|
||||||
})
|
this.$refs.settings_number.value = this.value
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.timerId = window.setTimeout(this.clear_status, this.timerDelay)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
clear_status: function () {
|
clear_status: function () {
|
||||||
@ -116,5 +130,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,21 +2,29 @@
|
|||||||
<fieldset :disabled="disabled">
|
<fieldset :disabled="disabled">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label has-text-weight-normal">
|
<label class="label has-text-weight-normal">
|
||||||
<slot name="label"></slot>
|
<slot name="label" />
|
||||||
<i class="is-size-7"
|
<i
|
||||||
:class="{
|
class="is-size-7"
|
||||||
'has-text-info': statusUpdate === 'success',
|
:class="{
|
||||||
'has-text-danger': statusUpdate === 'error'
|
'has-text-info': statusUpdate === 'success',
|
||||||
}"> {{ info }}</i>
|
'has-text-danger': statusUpdate === 'error'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ info }}</i
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" :placeholder="placeholder"
|
<input
|
||||||
:value="value"
|
ref="settings_text"
|
||||||
@input="set_update_timer"
|
class="input"
|
||||||
ref="settings_text">
|
type="text"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="value"
|
||||||
|
@input="set_update_timer"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p class="help" v-if="$slots['info']">
|
<p v-if="$slots['info']" class="help">
|
||||||
<slot name="info"></slot>
|
<slot name="info" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -31,7 +39,7 @@ export default {
|
|||||||
|
|
||||||
props: ['category_name', 'option_name', 'placeholder', 'disabled'],
|
props: ['category_name', 'option_name', 'placeholder', 'disabled'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
timerDelay: 2000,
|
timerDelay: 2000,
|
||||||
timerId: -1,
|
timerId: -1,
|
||||||
@ -42,22 +50,26 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
category () {
|
category() {
|
||||||
return this.$store.state.settings.categories.find(elem => elem.name === this.category_name)
|
return this.$store.state.settings.categories.find(
|
||||||
|
(elem) => elem.name === this.category_name
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
option () {
|
option() {
|
||||||
if (!this.category) {
|
if (!this.category) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
return this.category.options.find(elem => elem.name === this.option_name)
|
return this.category.options.find(
|
||||||
|
(elem) => elem.name === this.option_name
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
value () {
|
value() {
|
||||||
return this.option.value
|
return this.option.value
|
||||||
},
|
},
|
||||||
|
|
||||||
info () {
|
info() {
|
||||||
if (this.statusUpdate === 'success') {
|
if (this.statusUpdate === 'success') {
|
||||||
return '(setting saved)'
|
return '(setting saved)'
|
||||||
} else if (this.statusUpdate === 'error') {
|
} else if (this.statusUpdate === 'error') {
|
||||||
@ -68,7 +80,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
set_update_timer () {
|
set_update_timer() {
|
||||||
if (this.timerId > 0) {
|
if (this.timerId > 0) {
|
||||||
window.clearTimeout(this.timerId)
|
window.clearTimeout(this.timerId)
|
||||||
this.timerId = -1
|
this.timerId = -1
|
||||||
@ -81,7 +93,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update_setting () {
|
update_setting() {
|
||||||
this.timerId = -1
|
this.timerId = -1
|
||||||
|
|
||||||
const newValue = this.$refs.settings_text.value
|
const newValue = this.$refs.settings_text.value
|
||||||
@ -95,15 +107,19 @@ export default {
|
|||||||
name: this.option_name,
|
name: this.option_name,
|
||||||
value: newValue
|
value: newValue
|
||||||
}
|
}
|
||||||
webapi.settings_update(this.category.name, option).then(() => {
|
webapi
|
||||||
this.$store.commit(types.UPDATE_SETTINGS_OPTION, option)
|
.settings_update(this.category.name, option)
|
||||||
this.statusUpdate = 'success'
|
.then(() => {
|
||||||
}).catch(() => {
|
this.$store.commit(types.UPDATE_SETTINGS_OPTION, option)
|
||||||
this.statusUpdate = 'error'
|
this.statusUpdate = 'success'
|
||||||
this.$refs.settings_text.value = this.value
|
})
|
||||||
}).finally(() => {
|
.catch(() => {
|
||||||
this.timerId = window.setTimeout(this.clear_status, this.timerDelay)
|
this.statusUpdate = 'error'
|
||||||
})
|
this.$refs.settings_text.value = this.value
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.timerId = window.setTimeout(this.clear_status, this.timerDelay)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
clear_status: function () {
|
clear_status: function () {
|
||||||
@ -113,5 +129,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<div class="media-left fd-has-action"
|
<div v-if="$slots['artwork']" class="media-left fd-has-action">
|
||||||
v-if="$slots['artwork']">
|
<slot name="artwork" />
|
||||||
<slot name="artwork"></slot>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6">{{ album.name }}</h1>
|
<h1 class="title is-6">
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artists[0].name }}</b></h2>
|
{{ album.name }}
|
||||||
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">({{ album.album_type }}, {{ $filters.time(album.release_date, 'L') }})</h2>
|
</h1>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey">
|
||||||
|
<b>{{ album.artists[0].name }}</b>
|
||||||
|
</h2>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">
|
||||||
|
({{ album.album_type }}, {{ $filters.time(album.release_date, 'L') }})
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -22,5 +27,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_artist">
|
<div class="media-content fd-has-action is-clipped" @click="open_artist">
|
||||||
<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">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -22,5 +24,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<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="open_playlist">
|
||||||
<h1 class="title is-6">{{ playlist.name }}</h1>
|
<h1 class="title is-6">
|
||||||
<h2 class="subtitle is-7">{{ playlist.owner.display_name }}</h2>
|
{{ playlist.name }}
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-7">
|
||||||
|
{{ playlist.owner.display_name }}
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -17,11 +21,12 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_playlist: function () {
|
open_playlist: function () {
|
||||||
this.$router.push({ path: '/music/spotify/playlists/' + this.playlist.id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/playlists/' + this.playlist.id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,14 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<div class="media-content fd-has-action is-clipped" v-on:click="play">
|
<div class="media-content fd-has-action is-clipped" @click="play">
|
||||||
<h1 class="title is-6" :class="{ 'has-text-grey-light': track.is_playable === false }">{{ track.name }}</h1>
|
<h1
|
||||||
<h2 class="subtitle is-7" :class="{ 'has-text-grey': track.is_playable, 'has-text-grey-light': track.is_playable === false }"><b>{{ track.artists[0].name }}</b></h2>
|
class="title is-6"
|
||||||
<h2 class="subtitle is-7" v-if="track.is_playable === false">
|
:class="{ 'has-text-grey-light': track.is_playable === false }"
|
||||||
(Track is not playable<span v-if="track.restrictions && track.restrictions.reason">, restriction reason: {{ track.restrictions.reason }}</span>)
|
>
|
||||||
|
{{ track.name }}
|
||||||
|
</h1>
|
||||||
|
<h2
|
||||||
|
class="subtitle is-7"
|
||||||
|
:class="{
|
||||||
|
'has-text-grey': track.is_playable,
|
||||||
|
'has-text-grey-light': track.is_playable === false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<b>{{ track.artists[0].name }}</b>
|
||||||
|
</h2>
|
||||||
|
<h2 v-if="track.is_playable === false" class="subtitle is-7">
|
||||||
|
(Track is not playable<span
|
||||||
|
v-if="track.restrictions && track.restrictions.reason"
|
||||||
|
>, restriction reason: {{ track.restrictions.reason }}</span
|
||||||
|
>)
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -29,5 +45,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,25 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<figure class="image is-square fd-has-margin-bottom" v-show="artwork_visible">
|
<figure
|
||||||
<img :src="artwork_url" @load="artwork_loaded" @error="artwork_error" class="fd-has-shadow">
|
v-show="artwork_visible"
|
||||||
|
class="image is-square fd-has-margin-bottom"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="artwork_url"
|
||||||
|
class="fd-has-shadow"
|
||||||
|
@load="artwork_loaded"
|
||||||
|
@error="artwork_error"
|
||||||
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_album">{{ album.name }}</a>
|
<a class="has-text-link" @click="open_album">{{
|
||||||
|
album.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Album artist</span>
|
<span class="heading">Album artist</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artists[0].name }}</a>
|
<a class="title is-6 has-text-link" @click="open_artist">{{
|
||||||
|
album.artists[0].name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ $filters.time(album.release_date, 'L') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(album.release_date, 'L')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Type</span>
|
<span class="heading">Type</span>
|
||||||
@ -29,18 +43,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +74,7 @@ export default {
|
|||||||
name: 'SpotifyModalDialogAlbum',
|
name: 'SpotifyModalDialogAlbum',
|
||||||
props: ['show', 'album'],
|
props: ['show', 'album'],
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
artwork_visible: false
|
artwork_visible: false
|
||||||
}
|
}
|
||||||
@ -89,7 +110,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
open_artist: function () {
|
open_artist: function () {
|
||||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/artists/' + this.album.artists[0].id
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
artwork_loaded: function () {
|
artwork_loaded: function () {
|
||||||
@ -103,5 +126,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_artist">{{ artist.name }}</a>
|
<a class="has-text-link" @click="open_artist">{{
|
||||||
|
artist.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Popularity / Followers</span>
|
<span class="heading">Popularity / Followers</span>
|
||||||
<span class="title is-6">{{ artist.popularity }} / {{ artist.followers.total }}</span>
|
<span class="title is-6"
|
||||||
|
>{{ artist.popularity }} /
|
||||||
|
{{ artist.followers.total }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Genres</span>
|
<span class="heading">Genres</span>
|
||||||
@ -22,18 +27,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -69,5 +81,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="title is-4">
|
<p class="title is-4">
|
||||||
<a class="has-text-link" @click="open_playlist">{{ playlist.name }}</a>
|
<a class="has-text-link" @click="open_playlist">{{
|
||||||
|
playlist.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Owner</span>
|
<span class="heading">Owner</span>
|
||||||
<span class="title is-6">{{ playlist.owner.display_name }}</span>
|
<span class="title is-6">{{
|
||||||
|
playlist.owner.display_name
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Tracks</span>
|
<span class="heading">Tracks</span>
|
||||||
@ -26,18 +30,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -67,11 +78,12 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
open_playlist: function () {
|
open_playlist: function () {
|
||||||
this.$router.push({ path: '/music/spotify/playlists/' + this.playlist.id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/playlists/' + this.playlist.id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="modal is-active" v-if="show">
|
<div v-if="show" class="modal is-active">
|
||||||
<div class="modal-background" @click="$emit('close')"></div>
|
<div class="modal-background" @click="$emit('close')" />
|
||||||
<div class="modal-content fd-modal-card">
|
<div class="modal-content fd-modal-card">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
@ -15,23 +15,33 @@
|
|||||||
<div class="content is-small">
|
<div class="content is-small">
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Album</span>
|
<span class="heading">Album</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_album">{{ album.name }}</a>
|
<a class="title is-6 has-text-link" @click="open_album">{{
|
||||||
|
album.name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Album artist</span>
|
<span class="heading">Album artist</span>
|
||||||
<a class="title is-6 has-text-link" @click="open_artist">{{ album.artists[0].name }}</a>
|
<a class="title is-6 has-text-link" @click="open_artist">{{
|
||||||
|
album.artists[0].name
|
||||||
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ $filters.time(album.release_date, 'L') }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.time(album.release_date, 'L')
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Track / Disc</span>
|
<span class="heading">Track / Disc</span>
|
||||||
<span class="title is-6">{{ track.track_number }} / {{ track.disc_number }}</span>
|
<span class="title is-6"
|
||||||
|
>{{ track.track_number }} / {{ track.disc_number }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Length</span>
|
<span class="heading">Length</span>
|
||||||
<span class="title is-6">{{ $filters.duration(track.duration_ms) }}</span>
|
<span class="title is-6">{{
|
||||||
|
$filters.duration(track.duration_ms)
|
||||||
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Path</span>
|
<span class="heading">Path</span>
|
||||||
@ -41,18 +51,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="card-footer">
|
<footer class="card-footer">
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-plus" /></span>
|
||||||
|
<span class="is-size-7">Add</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
<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>
|
<span class="icon"><i class="mdi mdi-playlist-play" /></span>
|
||||||
|
<span class="is-size-7">Add Next</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="card-footer-item has-text-dark" @click="play">
|
<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>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span class="is-size-7">Play</span>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
<button
|
||||||
|
class="modal-close is-large"
|
||||||
|
aria-label="close"
|
||||||
|
@click="$emit('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -86,11 +103,12 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
open_artist: function () {
|
open_artist: function () {
|
||||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
this.$router.push({
|
||||||
|
path: '/music/spotify/artists/' + this.album.artists[0].id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -5,18 +5,30 @@
|
|||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
<div class="tabs is-centered is-small">
|
<div class="tabs is-centered is-small">
|
||||||
<ul>
|
<ul>
|
||||||
<router-link to="/audiobooks/artists" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/audiobooks/artists"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-artist"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-artist"
|
||||||
|
/></span>
|
||||||
<span class="">Authors</span>
|
<span class="">Authors</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/audiobooks/albums" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/audiobooks/albums"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-album"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-album"
|
||||||
|
/></span>
|
||||||
<span class="">Audiobooks</span>
|
<span class="">Audiobooks</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -35,5 +47,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -5,50 +5,85 @@
|
|||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
<div class="tabs is-centered is-small">
|
<div class="tabs is-centered is-small">
|
||||||
<ul>
|
<ul>
|
||||||
<router-link to="/music/browse" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/music/browse"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-web"></i></span>
|
<span class="icon is-small"><i class="mdi mdi-web" /></span>
|
||||||
<span class="">Browse</span>
|
<span class="">Browse</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/music/artists" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/music/artists"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-artist"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-artist"
|
||||||
|
/></span>
|
||||||
<span class="">Artists</span>
|
<span class="">Artists</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/music/albums" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/music/albums"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-album"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-album"
|
||||||
|
/></span>
|
||||||
<span class="">Albums</span>
|
<span class="">Albums</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/music/genres" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/music/genres"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-speaker"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-speaker"
|
||||||
|
/></span>
|
||||||
<span class="">Genres</span>
|
<span class="">Genres</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/music/composers" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/music/composers"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-book-open-page-variant"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-book-open-page-variant"
|
||||||
|
/></span>
|
||||||
<span class="">Composers</span>
|
<span class="">Composers</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/music/spotify" v-if="spotify_enabled" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-if="spotify_enabled"
|
||||||
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/music/spotify"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-spotify"
|
||||||
|
/></span>
|
||||||
<span class="">Spotify</span>
|
<span class="">Spotify</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -66,12 +101,11 @@ export default {
|
|||||||
name: 'TabsMusic',
|
name: 'TabsMusic',
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
spotify_enabled () {
|
spotify_enabled() {
|
||||||
return this.$store.state.spotify.webapi_token_valid
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="section fd-remove-padding-bottom" v-if="spotify_enabled">
|
<section v-if="spotify_enabled" 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">
|
||||||
@ -7,13 +7,17 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li :class="{ 'is-active': $route.path === '/search/library' }">
|
<li :class="{ 'is-active': $route.path === '/search/library' }">
|
||||||
<a @click="search_library">
|
<a @click="search_library">
|
||||||
<span class="icon is-small"><i class="mdi mdi-library-books"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-library-books"
|
||||||
|
/></span>
|
||||||
<span class="">Library</span>
|
<span class="">Library</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li :class="{ 'is-active': $route.path === '/search/spotify' }">
|
<li :class="{ 'is-active': $route.path === '/search/spotify' }">
|
||||||
<a @click="search_spotify">
|
<a @click="search_spotify">
|
||||||
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
|
<span class="icon is-small"
|
||||||
|
><i class="mdi mdi-spotify"
|
||||||
|
/></span>
|
||||||
<span class="">Spotify</span>
|
<span class="">Spotify</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -32,7 +36,7 @@ export default {
|
|||||||
props: ['query'],
|
props: ['query'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
spotify_enabled () {
|
spotify_enabled() {
|
||||||
return this.$store.state.spotify.webapi_token_valid
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -68,5 +72,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -5,29 +5,45 @@
|
|||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
<div class="tabs is-centered is-small">
|
<div class="tabs is-centered is-small">
|
||||||
<ul>
|
<ul>
|
||||||
<router-link to="/settings/webinterface" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/settings/webinterface"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="">Webinterface</span>
|
<span class="">Webinterface</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/settings/remotes-outputs" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/settings/remotes-outputs"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="">Remotes & Outputs</span>
|
<span class="">Remotes & Outputs</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/settings/artwork" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/settings/artwork"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="">Artwork</span>
|
<span class="">Artwork</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/settings/online-services" custom v-slot="{ navigate, isActive }">
|
<router-link
|
||||||
<li :class="{'is-active': isActive}">
|
v-slot="{ navigate, isActive }"
|
||||||
|
to="/settings/online-services"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<li :class="{ 'is-active': isActive }">
|
||||||
<a @click="navigate" @keypress.enter="navigate">
|
<a @click="navigate" @keypress.enter="navigate">
|
||||||
<span class="">Online Services</span>
|
<span class="">Online Services</span>
|
||||||
</a>
|
</a>
|
||||||
@ -45,10 +61,8 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'TabsSettings',
|
name: 'TabsSettings',
|
||||||
|
|
||||||
computed: {
|
computed: {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
|
|
||||||
export default class Albums {
|
export default class Albums {
|
||||||
constructor (items, options = { hideSingles: false, hideSpotify: false, sort: 'Name', group: false }) {
|
constructor(
|
||||||
|
items,
|
||||||
|
options = {
|
||||||
|
hideSingles: false,
|
||||||
|
hideSpotify: false,
|
||||||
|
sort: 'Name',
|
||||||
|
group: false
|
||||||
|
}
|
||||||
|
) {
|
||||||
this.items = items
|
this.items = items
|
||||||
this.options = options
|
this.options = options
|
||||||
this.grouped = {}
|
this.grouped = {}
|
||||||
@ -10,13 +17,13 @@ export default class Albums {
|
|||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
this.createSortedAndFilteredList()
|
this.createSortedAndFilteredList()
|
||||||
this.createGroupedList()
|
this.createGroupedList()
|
||||||
this.createIndexList()
|
this.createIndexList()
|
||||||
}
|
}
|
||||||
|
|
||||||
getAlbumIndex (album) {
|
getAlbumIndex(album) {
|
||||||
if (this.options.sort === 'Recently added') {
|
if (this.options.sort === 'Recently added') {
|
||||||
return album.time_added.substring(0, 4)
|
return album.time_added.substring(0, 4)
|
||||||
} else if (this.options.sort === 'Recently added (browse)') {
|
} else if (this.options.sort === 'Recently added (browse)') {
|
||||||
@ -29,24 +36,27 @@ export default class Albums {
|
|||||||
return album.name_sort.charAt(0).toUpperCase()
|
return album.name_sort.charAt(0).toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
getRecentlyAddedBrowseIndex (recentlyAdded) {
|
getRecentlyAddedBrowseIndex(recentlyAdded) {
|
||||||
if (!recentlyAdded) {
|
if (!recentlyAdded) {
|
||||||
return '0000'
|
return '0000'
|
||||||
}
|
}
|
||||||
|
|
||||||
const diff = new Date().getTime() - new Date(recentlyAdded).getTime()
|
const diff = new Date().getTime() - new Date(recentlyAdded).getTime()
|
||||||
|
|
||||||
if (diff < 86400000) { // 24h
|
if (diff < 86400000) {
|
||||||
|
// 24h
|
||||||
return 'Today'
|
return 'Today'
|
||||||
} else if (diff < 604800000) { // 7 days
|
} else if (diff < 604800000) {
|
||||||
|
// 7 days
|
||||||
return 'Last week'
|
return 'Last week'
|
||||||
} else if (diff < 2592000000) { // 30 days
|
} else if (diff < 2592000000) {
|
||||||
|
// 30 days
|
||||||
return 'Last month'
|
return 'Last month'
|
||||||
}
|
}
|
||||||
return recentlyAdded.substring(0, 4)
|
return recentlyAdded.substring(0, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
isAlbumVisible (album) {
|
isAlbumVisible(album) {
|
||||||
if (this.options.hideSingles && album.track_count <= 2) {
|
if (this.options.hideSingles && album.track_count <= 2) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -56,18 +66,30 @@ export default class Albums {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
createIndexList () {
|
createIndexList() {
|
||||||
this.indexList = [...new Set(this.sortedAndFiltered
|
this.indexList = [
|
||||||
.map(album => this.getAlbumIndex(album)))]
|
...new Set(
|
||||||
|
this.sortedAndFiltered.map((album) => this.getAlbumIndex(album))
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
createSortedAndFilteredList () {
|
createSortedAndFilteredList() {
|
||||||
let albumsSorted = this.items
|
let albumsSorted = this.items
|
||||||
if (this.options.hideSingles || this.options.hideSpotify || this.options.hideOther) {
|
if (
|
||||||
albumsSorted = albumsSorted.filter(album => this.isAlbumVisible(album))
|
this.options.hideSingles ||
|
||||||
|
this.options.hideSpotify ||
|
||||||
|
this.options.hideOther
|
||||||
|
) {
|
||||||
|
albumsSorted = albumsSorted.filter((album) => this.isAlbumVisible(album))
|
||||||
}
|
}
|
||||||
if (this.options.sort === 'Recently added' || this.options.sort === 'Recently added (browse)') {
|
if (
|
||||||
albumsSorted = [...albumsSorted].sort((a, b) => b.time_added.localeCompare(a.time_added))
|
this.options.sort === 'Recently added' ||
|
||||||
|
this.options.sort === 'Recently added (browse)'
|
||||||
|
) {
|
||||||
|
albumsSorted = [...albumsSorted].sort((a, b) =>
|
||||||
|
b.time_added.localeCompare(a.time_added)
|
||||||
|
)
|
||||||
} else if (this.options.sort === 'Recently released') {
|
} else if (this.options.sort === 'Recently released') {
|
||||||
albumsSorted = [...albumsSorted].sort((a, b) => {
|
albumsSorted = [...albumsSorted].sort((a, b) => {
|
||||||
if (!a.date_released) {
|
if (!a.date_released) {
|
||||||
@ -92,13 +114,13 @@ export default class Albums {
|
|||||||
this.sortedAndFiltered = albumsSorted
|
this.sortedAndFiltered = albumsSorted
|
||||||
}
|
}
|
||||||
|
|
||||||
createGroupedList () {
|
createGroupedList() {
|
||||||
if (!this.options.group) {
|
if (!this.options.group) {
|
||||||
this.grouped = {}
|
this.grouped = {}
|
||||||
}
|
}
|
||||||
this.grouped = this.sortedAndFiltered.reduce((r, album) => {
|
this.grouped = this.sortedAndFiltered.reduce((r, album) => {
|
||||||
const idx = this.getAlbumIndex(album)
|
const idx = this.getAlbumIndex(album)
|
||||||
r[idx] = [...r[idx] || [], album]
|
r[idx] = [...(r[idx] || []), album]
|
||||||
return r
|
return r
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
|
|
||||||
export default class Artists {
|
export default class Artists {
|
||||||
constructor (items, options = { hideSingles: false, hideSpotify: false, sort: 'Name', group: false }) {
|
constructor(
|
||||||
|
items,
|
||||||
|
options = {
|
||||||
|
hideSingles: false,
|
||||||
|
hideSpotify: false,
|
||||||
|
sort: 'Name',
|
||||||
|
group: false
|
||||||
|
}
|
||||||
|
) {
|
||||||
this.items = items
|
this.items = items
|
||||||
this.options = options
|
this.options = options
|
||||||
this.grouped = {}
|
this.grouped = {}
|
||||||
@ -10,21 +17,24 @@ export default class Artists {
|
|||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
this.createSortedAndFilteredList()
|
this.createSortedAndFilteredList()
|
||||||
this.createGroupedList()
|
this.createGroupedList()
|
||||||
this.createIndexList()
|
this.createIndexList()
|
||||||
}
|
}
|
||||||
|
|
||||||
getArtistIndex (artist) {
|
getArtistIndex(artist) {
|
||||||
if (this.options.sort === 'Name') {
|
if (this.options.sort === 'Name') {
|
||||||
return artist.name_sort.charAt(0).toUpperCase()
|
return artist.name_sort.charAt(0).toUpperCase()
|
||||||
}
|
}
|
||||||
return artist.time_added.substring(0, 4)
|
return artist.time_added.substring(0, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
isArtistVisible (artist) {
|
isArtistVisible(artist) {
|
||||||
if (this.options.hideSingles && artist.track_count <= (artist.album_count * 2)) {
|
if (
|
||||||
|
this.options.hideSingles &&
|
||||||
|
artist.track_count <= artist.album_count * 2
|
||||||
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (this.options.hideSpotify && artist.data_kind === 'spotify') {
|
if (this.options.hideSpotify && artist.data_kind === 'spotify') {
|
||||||
@ -33,29 +43,40 @@ export default class Artists {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
createIndexList () {
|
createIndexList() {
|
||||||
this.indexList = [...new Set(this.sortedAndFiltered
|
this.indexList = [
|
||||||
.map(artist => this.getArtistIndex(artist)))]
|
...new Set(
|
||||||
|
this.sortedAndFiltered.map((artist) => this.getArtistIndex(artist))
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
createSortedAndFilteredList () {
|
createSortedAndFilteredList() {
|
||||||
let artistsSorted = this.items
|
let artistsSorted = this.items
|
||||||
if (this.options.hideSingles || this.options.hideSpotify || this.options.hideOther) {
|
if (
|
||||||
artistsSorted = artistsSorted.filter(artist => this.isArtistVisible(artist))
|
this.options.hideSingles ||
|
||||||
|
this.options.hideSpotify ||
|
||||||
|
this.options.hideOther
|
||||||
|
) {
|
||||||
|
artistsSorted = artistsSorted.filter((artist) =>
|
||||||
|
this.isArtistVisible(artist)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (this.options.sort === 'Recently added') {
|
if (this.options.sort === 'Recently added') {
|
||||||
artistsSorted = [...artistsSorted].sort((a, b) => b.time_added.localeCompare(a.time_added))
|
artistsSorted = [...artistsSorted].sort((a, b) =>
|
||||||
|
b.time_added.localeCompare(a.time_added)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
this.sortedAndFiltered = artistsSorted
|
this.sortedAndFiltered = artistsSorted
|
||||||
}
|
}
|
||||||
|
|
||||||
createGroupedList () {
|
createGroupedList() {
|
||||||
if (!this.options.group) {
|
if (!this.options.group) {
|
||||||
this.grouped = {}
|
this.grouped = {}
|
||||||
}
|
}
|
||||||
this.grouped = this.sortedAndFiltered.reduce((r, artist) => {
|
this.grouped = this.sortedAndFiltered.reduce((r, artist) => {
|
||||||
const idx = this.getArtistIndex(artist)
|
const idx = this.getArtistIndex(artist)
|
||||||
r[idx] = [...r[idx] || [], artist]
|
r[idx] = [...(r[idx] || []), artist]
|
||||||
return r
|
return r
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
|
|
||||||
export default class Composers {
|
export default class Composers {
|
||||||
constructor (items, options = { hideSingles: false, hideSpotify: false, sort: 'Name', group: false }) {
|
constructor(
|
||||||
|
items,
|
||||||
|
options = {
|
||||||
|
hideSingles: false,
|
||||||
|
hideSpotify: false,
|
||||||
|
sort: 'Name',
|
||||||
|
group: false
|
||||||
|
}
|
||||||
|
) {
|
||||||
this.items = items
|
this.items = items
|
||||||
this.options = options
|
this.options = options
|
||||||
this.grouped = {}
|
this.grouped = {}
|
||||||
@ -10,21 +17,24 @@ export default class Composers {
|
|||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
this.createSortedAndFilteredList()
|
this.createSortedAndFilteredList()
|
||||||
this.createGroupedList()
|
this.createGroupedList()
|
||||||
this.createIndexList()
|
this.createIndexList()
|
||||||
}
|
}
|
||||||
|
|
||||||
getComposerIndex (composer) {
|
getComposerIndex(composer) {
|
||||||
if (this.options.sort === 'Name') {
|
if (this.options.sort === 'Name') {
|
||||||
return composer.name_sort.charAt(0).toUpperCase()
|
return composer.name_sort.charAt(0).toUpperCase()
|
||||||
}
|
}
|
||||||
return composer.time_added.substring(0, 4)
|
return composer.time_added.substring(0, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
isComposerVisible (composer) {
|
isComposerVisible(composer) {
|
||||||
if (this.options.hideSingles && composer.track_count <= (composer.album_count * 2)) {
|
if (
|
||||||
|
this.options.hideSingles &&
|
||||||
|
composer.track_count <= composer.album_count * 2
|
||||||
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (this.options.hideSpotify && composer.data_kind === 'spotify') {
|
if (this.options.hideSpotify && composer.data_kind === 'spotify') {
|
||||||
@ -33,29 +43,42 @@ export default class Composers {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
createIndexList () {
|
createIndexList() {
|
||||||
this.indexList = [...new Set(this.sortedAndFiltered
|
this.indexList = [
|
||||||
.map(composer => this.getComposerIndex(composer)))]
|
...new Set(
|
||||||
|
this.sortedAndFiltered.map((composer) =>
|
||||||
|
this.getComposerIndex(composer)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
createSortedAndFilteredList () {
|
createSortedAndFilteredList() {
|
||||||
let composersSorted = this.items
|
let composersSorted = this.items
|
||||||
if (this.options.hideSingles || this.options.hideSpotify || this.options.hideOther) {
|
if (
|
||||||
composersSorted = composersSorted.filter(composer => this.isComposerVisible(composer))
|
this.options.hideSingles ||
|
||||||
|
this.options.hideSpotify ||
|
||||||
|
this.options.hideOther
|
||||||
|
) {
|
||||||
|
composersSorted = composersSorted.filter((composer) =>
|
||||||
|
this.isComposerVisible(composer)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (this.options.sort === 'Recently added') {
|
if (this.options.sort === 'Recently added') {
|
||||||
composersSorted = [...composersSorted].sort((a, b) => b.time_added.localeCompare(a.time_added))
|
composersSorted = [...composersSorted].sort((a, b) =>
|
||||||
|
b.time_added.localeCompare(a.time_added)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
this.sortedAndFiltered = composersSorted
|
this.sortedAndFiltered = composersSorted
|
||||||
}
|
}
|
||||||
|
|
||||||
createGroupedList () {
|
createGroupedList() {
|
||||||
if (!this.options.group) {
|
if (!this.options.group) {
|
||||||
this.grouped = {}
|
this.grouped = {}
|
||||||
}
|
}
|
||||||
this.grouped = this.sortedAndFiltered.reduce((r, composer) => {
|
this.grouped = this.sortedAndFiltered.reduce((r, composer) => {
|
||||||
const idx = this.getComposerIndex(composer)
|
const idx = this.getComposerIndex(composer)
|
||||||
r[idx] = [...r[idx] || [], composer]
|
r[idx] = [...(r[idx] || []), composer]
|
||||||
return r
|
return r
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
@ -6,51 +6,67 @@
|
|||||||
|
|
||||||
import stringToColor from 'string-to-color'
|
import stringToColor from 'string-to-color'
|
||||||
|
|
||||||
|
function is_background_light(background_color) {
|
||||||
function is_background_light (background_color) {
|
|
||||||
// Based on https://stackoverflow.com/a/44615197
|
// Based on https://stackoverflow.com/a/44615197
|
||||||
const hex = background_color.replace(/#/, '')
|
const hex = background_color.replace(/#/, '')
|
||||||
const r = parseInt(hex.substr(0, 2), 16)
|
const r = parseInt(hex.substr(0, 2), 16)
|
||||||
const g = parseInt(hex.substr(2, 2), 16)
|
const g = parseInt(hex.substr(2, 2), 16)
|
||||||
const b = parseInt(hex.substr(4, 2), 16)
|
const b = parseInt(hex.substr(4, 2), 16)
|
||||||
|
|
||||||
const luma = [
|
const luma = [0.299 * r, 0.587 * g, 0.114 * b].reduce((a, b) => a + b) / 255
|
||||||
0.299 * r,
|
|
||||||
0.587 * g,
|
|
||||||
0.114 * b
|
|
||||||
].reduce((a, b) => a + b) / 255
|
|
||||||
|
|
||||||
return luma > 0.5
|
return luma > 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
function calc_text_color (background_color) {
|
function calc_text_color(background_color) {
|
||||||
return is_background_light(background_color) ? '#000000' : '#ffffff'
|
return is_background_light(background_color) ? '#000000' : '#ffffff'
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSVG (data) {
|
function createSVG(data) {
|
||||||
const svg = '<svg width="' + data.width + '" height="' + data.height + '" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + data.width + ' ' + data.height + '" preserveAspectRatio="none">' +
|
const svg =
|
||||||
'<defs>' +
|
'<svg width="' +
|
||||||
'<style type="text/css">' +
|
data.width +
|
||||||
' #holder text {' +
|
'" height="' +
|
||||||
' fill: ' + data.textColor + ';' +
|
data.height +
|
||||||
' font-family: ' + data.fontFamily + ';' +
|
'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' +
|
||||||
' font-size: ' + data.fontSize + 'px;' +
|
data.width +
|
||||||
' font-weight: ' + data.fontWeight + ';' +
|
' ' +
|
||||||
' }' +
|
data.height +
|
||||||
' </style>' +
|
'" preserveAspectRatio="none">' +
|
||||||
'</defs>' +
|
'<defs>' +
|
||||||
'<g id="holder">' +
|
'<style type="text/css">' +
|
||||||
' <rect width="100%" height="100%" fill="' + data.backgroundColor + '"></rect>' +
|
' #holder text {' +
|
||||||
' <g>' +
|
' fill: ' +
|
||||||
' <text text-anchor="middle" x="50%" y="50%" dy=".3em">' + data.caption + '</text>' +
|
data.textColor +
|
||||||
' </g>' +
|
';' +
|
||||||
'</g>' +
|
' font-family: ' +
|
||||||
'</svg>'
|
data.fontFamily +
|
||||||
|
';' +
|
||||||
|
' font-size: ' +
|
||||||
|
data.fontSize +
|
||||||
|
'px;' +
|
||||||
|
' font-weight: ' +
|
||||||
|
data.fontWeight +
|
||||||
|
';' +
|
||||||
|
' }' +
|
||||||
|
' </style>' +
|
||||||
|
'</defs>' +
|
||||||
|
'<g id="holder">' +
|
||||||
|
' <rect width="100%" height="100%" fill="' +
|
||||||
|
data.backgroundColor +
|
||||||
|
'"></rect>' +
|
||||||
|
' <g>' +
|
||||||
|
' <text text-anchor="middle" x="50%" y="50%" dy=".3em">' +
|
||||||
|
data.caption +
|
||||||
|
'</text>' +
|
||||||
|
' </g>' +
|
||||||
|
'</g>' +
|
||||||
|
'</svg>'
|
||||||
|
|
||||||
return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg)
|
return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSVG (caption, alt_text, params) {
|
function renderSVG(caption, alt_text, params) {
|
||||||
const background_color = stringToColor(alt_text)
|
const background_color = stringToColor(alt_text)
|
||||||
const text_color = calc_text_color(background_color)
|
const text_color = calc_text_color(background_color)
|
||||||
const paramsSVG = {
|
const paramsSVG = {
|
||||||
@ -66,4 +82,4 @@ function renderSVG (caption, alt_text, params) {
|
|||||||
return createSVG(paramsSVG)
|
return createSVG(paramsSVG)
|
||||||
}
|
}
|
||||||
|
|
||||||
export { renderSVG }
|
export { renderSVG }
|
||||||
|
@ -2,7 +2,7 @@ import { createApp } from 'vue'
|
|||||||
import store from './store'
|
import store from './store'
|
||||||
import { router } from './router'
|
import { router } from './router'
|
||||||
import VueProgressBar from '@aacassandra/vue3-progressbar'
|
import VueProgressBar from '@aacassandra/vue3-progressbar'
|
||||||
import VueClickAway from "vue3-click-away"
|
import VueClickAway from 'vue3-click-away'
|
||||||
import VueLazyLoad from 'vue3-lazyload'
|
import VueLazyLoad from 'vue3-lazyload'
|
||||||
import VueScrollTo from 'vue-scrollto'
|
import VueScrollTo from 'vue-scrollto'
|
||||||
import { filters } from './filter'
|
import { filters } from './filter'
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
@import 'bulma/bulma.sass';
|
@import 'bulma/bulma.sass';
|
||||||
@import 'bulma-switch';
|
@import 'bulma-switch';
|
||||||
|
|
||||||
|
|
||||||
/* Volume slider */
|
/* Volume slider */
|
||||||
.slider {
|
.slider {
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
@ -13,9 +12,9 @@
|
|||||||
--slider-height: 4px;
|
--slider-height: 4px;
|
||||||
--slider-connect-bg: hsl(0, 0%, 21%);
|
--slider-connect-bg: hsl(0, 0%, 21%);
|
||||||
--slider-tooltip-bg: hsl(0, 0%, 21%);
|
--slider-tooltip-bg: hsl(0, 0%, 21%);
|
||||||
--slider-handle-ring-color: #3B82F630;
|
--slider-handle-ring-color: #3b82f630;
|
||||||
--slider-handle-shadow: 0.5px 0.5px 0.5px 0.5px rgba(0,0,0,.32);
|
--slider-handle-shadow: 0.5px 0.5px 0.5px 0.5px rgba(0, 0, 0, 0.32);
|
||||||
--slider-handle-shadow-active: 0.5px 0.5px 0.5px 0.5px rgba(0,0,0,.42);
|
--slider-handle-shadow-active: 0.5px 0.5px 0.5px 0.5px rgba(0, 0, 0, 0.42);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now playing progress bar */
|
/* Now playing progress bar */
|
||||||
@ -33,8 +32,8 @@
|
|||||||
--slider-handle-width: 10px;
|
--slider-handle-width: 10px;
|
||||||
--slider-handle-height: 10px;
|
--slider-handle-height: 10px;
|
||||||
--slider-handle-radius: 9999px;
|
--slider-handle-radius: 9999px;
|
||||||
--slider-handle-shadow: 0.5px 0.5px 0.5px 0.5px rgba(0,0,0,.32);
|
--slider-handle-shadow: 0.5px 0.5px 0.5px 0.5px rgba(0, 0, 0, 0.32);
|
||||||
--slider-handle-shadow-active: 0.5px 0.5px 0.5px 0.5px rgba(0,0,0,.42);
|
--slider-handle-shadow-active: 0.5px 0.5px 0.5px 0.5px rgba(0, 0, 0, 0.42);
|
||||||
--slider-handle-ring-width: 3px;
|
--slider-handle-ring-width: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +54,7 @@
|
|||||||
a.navbar-item {
|
a.navbar-item {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
padding: .5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fd-expanded {
|
.fd-expanded {
|
||||||
@ -181,7 +180,8 @@ section.hero + section.fd-content {
|
|||||||
/* Use object-fit to properly size the cover artwork: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit */
|
/* Use object-fit to properly size the cover artwork: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit */
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
object-position: center bottom;
|
object-position: center bottom;
|
||||||
filter: drop-shadow(0px 0px 1px rgba(0,0,0,.3)) drop-shadow(0px 0px 10px rgba(0,0,0,.3));
|
filter: drop-shadow(0px 0px 1px rgba(0, 0, 0, 0.3))
|
||||||
|
drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.3));
|
||||||
|
|
||||||
/* Allow flex item to grow/shrink to fill the whole container size */
|
/* Allow flex item to grow/shrink to fill the whole container size */
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -199,11 +199,11 @@ section.hero + section.fd-content {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.sortable-chosen .media-right {
|
.sortable-chosen .media-right {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
.sortable-ghost h1, .sortable-ghost h2 {
|
.sortable-ghost h1,
|
||||||
|
.sortable-ghost h2 {
|
||||||
color: hsl(348, 100%, 61%) !important;
|
color: hsl(348, 100%, 61%) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,15 +214,17 @@ section.hero + section.fd-content {
|
|||||||
|
|
||||||
/* Transition effect */
|
/* Transition effect */
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity .2s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
.fade-enter-active {
|
.fade-enter-active {
|
||||||
transition: opacity .5s ease;
|
transition: opacity 0.5s ease;
|
||||||
}
|
}
|
||||||
.fade-enter-from, .fade-leave-to {
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.fade-enter-to, .fade-leave-from {
|
.fade-enter-to,
|
||||||
|
.fade-leave-from {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +254,7 @@ section.hero + section.fd-content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover {
|
||||||
background-color: hsl(0, 0%, 96%)
|
background-color: hsl(0, 0%, 96%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-item .fd-navbar-item-level2 {
|
.navbar-item .fd-navbar-item-level2 {
|
||||||
@ -276,18 +278,17 @@ hr.fd-navbar-divider {
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
@include mobile {
|
@include mobile {
|
||||||
&.fd-is-centered-mobile {
|
&.fd-is-centered-mobile {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
&:not(.has-addons) {
|
&:not(.has-addons) {
|
||||||
.button:not(.is-fullwidth) {
|
.button:not(.is-fullwidth) {
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,11 +307,11 @@ hr.fd-navbar-divider {
|
|||||||
|
|
||||||
.fd-overlay-fullscreen {
|
.fd-overlay-fullscreen {
|
||||||
@extend .is-overlay;
|
@extend .is-overlay;
|
||||||
z-index:25;
|
z-index: 25;
|
||||||
background-color: rgba(10, 10, 10, 0.2);
|
background-color: rgba(10, 10, 10, 0.2);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-body {
|
.hero-body {
|
||||||
padding: 1.5rem !important;
|
padding: 1.5rem !important;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-four-fifths has-text-centered-mobile">
|
<div class="column is-four-fifths has-text-centered-mobile">
|
||||||
<p class="heading"><b>OwnTone</b> - version {{ config.version }}</p>
|
<p class="heading"><b>OwnTone</b> - version {{ config.version }}</p>
|
||||||
<h1 class="title is-4">{{ config.library_name }}</h1>
|
<h1 class="title is-4">
|
||||||
|
{{ config.library_name }}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -25,8 +27,14 @@
|
|||||||
|
|
||||||
<!-- Right side -->
|
<!-- Right side -->
|
||||||
<div class="level-right">
|
<div class="level-right">
|
||||||
<div v-if="library.updating"><a class="button is-small is-loading">Update</a></div>
|
<div v-if="library.updating">
|
||||||
<div v-else><a @click="showUpdateDialog()" class="button is-small">Update</a></div>
|
<a class="button is-small is-loading">Update</a>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<a class="button is-small" @click="showUpdateDialog()"
|
||||||
|
>Update</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
@ -34,27 +42,50 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Artists</th>
|
<th>Artists</th>
|
||||||
<td class="has-text-right">{{ $filters.number(library.artists) }}</td>
|
<td class="has-text-right">
|
||||||
|
{{ $filters.number(library.artists) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Albums</th>
|
<th>Albums</th>
|
||||||
<td class="has-text-right">{{ $filters.number(library.albums) }}</td>
|
<td class="has-text-right">
|
||||||
|
{{ $filters.number(library.albums) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Tracks</th>
|
<th>Tracks</th>
|
||||||
<td class="has-text-right">{{ $filters.number(library.songs) }}</td>
|
<td class="has-text-right">
|
||||||
|
{{ $filters.number(library.songs) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Total playtime</th>
|
<th>Total playtime</th>
|
||||||
<td class="has-text-right">{{ $filters.duration(library.db_playtime * 1000, 'y [years], d [days], h [hours], m [minutes]') }}</td>
|
<td class="has-text-right">
|
||||||
|
{{
|
||||||
|
$filters.duration(
|
||||||
|
library.db_playtime * 1000,
|
||||||
|
'y [years], d [days], h [hours], m [minutes]'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Library updated</th>
|
<th>Library updated</th>
|
||||||
<td class="has-text-right">{{ $filters.timeFromNow(library.updated_at) }} <span class="has-text-grey">({{ $filters.time(library.updated_at, 'lll') }})</span></td>
|
<td class="has-text-right">
|
||||||
|
{{ $filters.timeFromNow(library.updated_at) }}
|
||||||
|
<span class="has-text-grey"
|
||||||
|
>({{ $filters.time(library.updated_at, 'lll') }})</span
|
||||||
|
>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Uptime</th>
|
<th>Uptime</th>
|
||||||
<td class="has-text-right">{{ $filters.timeFromNow(library.started_at, true) }} <span class="has-text-grey">({{ $filters.time(library.started_at, 'll') }})</span></td>
|
<td class="has-text-right">
|
||||||
|
{{ $filters.timeFromNow(library.started_at, true) }}
|
||||||
|
<span class="has-text-grey"
|
||||||
|
>({{ $filters.time(library.started_at, 'll') }})</span
|
||||||
|
>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -68,8 +99,20 @@
|
|||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-four-fifths">
|
<div class="column is-four-fifths">
|
||||||
<div class="content has-text-centered-mobile">
|
<div class="content has-text-centered-mobile">
|
||||||
<p class="is-size-7">Compiled with support for {{ config.buildoptions.join(', ') }}.</p>
|
<p class="is-size-7">
|
||||||
<p class="is-size-7">Web interface built with <a href="http://bulma.io">Bulma</a>, <a href="https://materialdesignicons.com/">Material Design Icons</a>, <a href="https://vuejs.org/">Vue.js</a>, <a href="https://github.com/mzabriskie/axios">axios</a> and <a href="https://github.com/owntone/owntone-server/network/dependencies">more</a>.</p>
|
Compiled with support for {{ config.buildoptions.join(', ') }}.
|
||||||
|
</p>
|
||||||
|
<p class="is-size-7">
|
||||||
|
Web interface built with <a href="http://bulma.io">Bulma</a>,
|
||||||
|
<a href="https://materialdesignicons.com/"
|
||||||
|
>Material Design Icons</a
|
||||||
|
>, <a href="https://vuejs.org/">Vue.js</a>,
|
||||||
|
<a href="https://github.com/mzabriskie/axios">axios</a> and
|
||||||
|
<a
|
||||||
|
href="https://github.com/owntone/owntone-server/network/dependencies"
|
||||||
|
>more</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -84,7 +127,7 @@ import * as types from '@/store/mutation_types'
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAbout',
|
name: 'PageAbout',
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
show_update_dropdown: false,
|
show_update_dropdown: false,
|
||||||
show_update_library: false
|
show_update_library: false
|
||||||
@ -92,24 +135,23 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
config () {
|
config() {
|
||||||
return this.$store.state.config
|
return this.$store.state.config
|
||||||
},
|
},
|
||||||
library () {
|
library() {
|
||||||
return this.$store.state.library
|
return this.$store.state.library
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onClickOutside (event) {
|
onClickOutside(event) {
|
||||||
this.show_update_dropdown = false
|
this.show_update_dropdown = false
|
||||||
},
|
},
|
||||||
showUpdateDialog () {
|
showUpdateDialog() {
|
||||||
this.$store.commit(types.SHOW_UPDATE_DIALOG, true)
|
this.$store.commit(types.SHOW_UPDATE_DIALOG, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,31 +1,48 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-hero>
|
<content-with-hero>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<h1 class="title is-5">{{ album.name }}</h1>
|
<h1 class="title is-5">
|
||||||
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artist }}</a></h2>
|
{{ album.name }}
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-6 has-text-link has-text-weight-normal">
|
||||||
|
<a class="has-text-link" @click="open_artist">{{ album.artist }}</a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div class="buttons fd-is-centered-mobile fd-has-margin-top">
|
<div class="buttons fd-is-centered-mobile fd-has-margin-top">
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_album_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<p class="image is-square fd-has-shadow fd-has-action">
|
<p class="image is-square fd-has-shadow fd-has-action">
|
||||||
<cover-artwork
|
<cover-artwork
|
||||||
:artwork_url="album.artwork_url"
|
:artwork_url="album.artwork_url"
|
||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
@click="show_album_details_modal = true" />
|
@click="show_album_details_modal = true"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.track_count }} tracks</p>
|
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">
|
||||||
<list-tracks :tracks="tracks" :uris="album.uri"></list-tracks>
|
{{ album.track_count }} tracks
|
||||||
<modal-dialog-album :show="show_album_details_modal" :album="album" @close="show_album_details_modal = false" />
|
</p>
|
||||||
|
<list-tracks :tracks="tracks" :uris="album.uri" />
|
||||||
|
<modal-dialog-album
|
||||||
|
:show="show_album_details_modal"
|
||||||
|
:album="album"
|
||||||
|
@close="show_album_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-hero>
|
</content-with-hero>
|
||||||
</template>
|
</template>
|
||||||
@ -55,7 +72,20 @@ export default {
|
|||||||
name: 'PageAlbum',
|
name: 'PageAlbum',
|
||||||
components: { ContentWithHero, ListTracks, ModalDialogAlbum, CoverArtwork },
|
components: { ContentWithHero, ListTracks, ModalDialogAlbum, CoverArtwork },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
tracks: [],
|
tracks: [],
|
||||||
@ -73,22 +103,8 @@ export default {
|
|||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.album.uri, true)
|
webapi.player_play_uri(this.album.uri, true)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,43 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="albums_list.indexList"></index-button-list>
|
<index-button-list :index="albums_list.indexList" />
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p class="heading" style="margin-bottom: 24px;">Filter</p>
|
<p class="heading" style="margin-bottom: 24px">Filter</p>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="switchHideSingles" type="checkbox" name="switchHideSingles" class="switch" v-model="hide_singles">
|
<input
|
||||||
|
id="switchHideSingles"
|
||||||
|
v-model="hide_singles"
|
||||||
|
type="checkbox"
|
||||||
|
name="switchHideSingles"
|
||||||
|
class="switch"
|
||||||
|
/>
|
||||||
<label for="switchHideSingles">Hide singles</label>
|
<label for="switchHideSingles">Hide singles</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="help">If active, hides singles and albums with tracks that only appear in playlists.</p>
|
<p class="help">
|
||||||
|
If active, hides singles and albums with tracks that only appear
|
||||||
|
in playlists.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="field" v-if="spotify_enabled">
|
<div v-if="spotify_enabled" class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="switchHideSpotify" type="checkbox" name="switchHideSpotify" class="switch" v-model="hide_spotify">
|
<input
|
||||||
|
id="switchHideSpotify"
|
||||||
|
v-model="hide_spotify"
|
||||||
|
type="checkbox"
|
||||||
|
name="switchHideSpotify"
|
||||||
|
class="switch"
|
||||||
|
/>
|
||||||
<label for="switchHideSpotify">Hide albums from Spotify</label>
|
<label for="switchHideSpotify">Hide albums from Spotify</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="help">If active, hides albums that only appear in your Spotify library.</p>
|
<p class="help">
|
||||||
|
If active, hides albums that only appear in your Spotify
|
||||||
|
library.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p class="heading" style="margin-bottom: 24px;">Sort by</p>
|
<p class="heading" style="margin-bottom: 24px">Sort by</p>
|
||||||
<dropdown-menu v-model="sort" :options="sort_options"></dropdown-menu>
|
<dropdown-menu v-model="sort" :options="sort_options" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Albums</p>
|
<p class="title is-4">Albums</p>
|
||||||
<p class="heading">{{ albums_list.sortedAndFiltered.length }} Albums</p>
|
<p class="heading">{{ albums_list.sortedAndFiltered.length }} Albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right />
|
||||||
</template>
|
<template #content>
|
||||||
<template v-slot:content>
|
<list-albums :albums="albums_list" />
|
||||||
<list-albums :albums="albums_list"></list-albums>
|
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -60,17 +77,46 @@ const dataObject = {
|
|||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
vm.albums = response.data
|
vm.albums = response.data
|
||||||
vm.index_list = [...new Set(vm.albums.items
|
vm.index_list = [
|
||||||
.filter(album => !vm.$store.state.hide_singles || album.track_count > 2)
|
...new Set(
|
||||||
.map(album => album.name_sort.charAt(0).toUpperCase()))]
|
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',
|
||||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListAlbums, DropdownMenu },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
TabsMusic,
|
||||||
|
IndexButtonList,
|
||||||
|
ListAlbums,
|
||||||
|
DropdownMenu
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
if (this.albums.items.length > 0) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
albums: { items: [] },
|
albums: { items: [] },
|
||||||
sort_options: ['Name', 'Recently added', 'Recently released']
|
sort_options: ['Name', 'Recently added', 'Recently released']
|
||||||
@ -78,7 +124,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
albums_list () {
|
albums_list() {
|
||||||
return new Albums(this.albums.items, {
|
return new Albums(this.albums.items, {
|
||||||
hideSingles: this.hide_singles,
|
hideSingles: this.hide_singles,
|
||||||
hideSpotify: this.hide_spotify,
|
hideSpotify: this.hide_spotify,
|
||||||
@ -87,33 +133,33 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
spotify_enabled () {
|
spotify_enabled() {
|
||||||
return this.$store.state.spotify.webapi_token_valid
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_singles: {
|
hide_singles: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.hide_singles
|
return this.$store.state.hide_singles
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.HIDE_SINGLES, value)
|
this.$store.commit(types.HIDE_SINGLES, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_spotify: {
|
hide_spotify: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.hide_spotify
|
return this.$store.state.hide_spotify
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.HIDE_SPOTIFY, value)
|
this.$store.commit(types.HIDE_SPOTIFY, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
sort: {
|
sort: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.albums_sort
|
return this.$store.state.albums_sort
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.ALBUMS_SORT, value)
|
this.$store.commit(types.ALBUMS_SORT, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,26 +169,8 @@ export default {
|
|||||||
scrollToTop: function () {
|
scrollToTop: function () {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
if (this.albums.items.length > 0) {
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,30 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p class="heading" style="margin-bottom: 24px;">Sort by</p>
|
<p class="heading" style="margin-bottom: 24px">Sort by</p>
|
||||||
<dropdown-menu v-model="sort" :options="sort_options"></dropdown-menu>
|
<dropdown-menu v-model="sort" :options="sort_options" />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
<template v-slot:heading-left>
|
|
||||||
<p class="title is-4">{{ artist.name }}</p>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-left>
|
||||||
|
<p class="title is-4">
|
||||||
|
{{ artist.name }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_artist_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #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">
|
||||||
<list-albums :albums="albums_list"></list-albums>
|
{{ artist.album_count }} albums |
|
||||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
<a class="has-text-link" @click="open_tracks"
|
||||||
|
>{{ artist.track_count }} tracks</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<list-albums :albums="albums_list" />
|
||||||
|
<modal-dialog-artist
|
||||||
|
:show="show_artist_details_modal"
|
||||||
|
:artist="artist"
|
||||||
|
@close="show_artist_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -54,9 +71,27 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageArtist',
|
name: 'PageArtist',
|
||||||
components: { ContentWithHeading, ListAlbums, ModalDialogArtist, DropdownMenu },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListAlbums,
|
||||||
|
ModalDialogArtist,
|
||||||
|
DropdownMenu
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
artist: {},
|
artist: {},
|
||||||
albums: { items: [] },
|
albums: { items: [] },
|
||||||
@ -67,7 +102,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
albums_list () {
|
albums_list() {
|
||||||
return new Albums(this.albums.items, {
|
return new Albums(this.albums.items, {
|
||||||
sort: this.sort,
|
sort: this.sort,
|
||||||
group: false
|
group: false
|
||||||
@ -75,10 +110,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
sort: {
|
sort: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.artist_albums_sort
|
return this.$store.state.artist_albums_sort
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.ARTIST_ALBUMS_SORT, value)
|
this.$store.commit(types.ARTIST_ALBUMS_SORT, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,28 +121,19 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_tracks: function () {
|
open_tracks: function () {
|
||||||
this.$router.push({ path: '/music/artists/' + this.artist.id + '/tracks' })
|
this.$router.push({
|
||||||
|
path: '/music/artists/' + this.artist.id + '/tracks'
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,26 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="index_list"></index-button-list>
|
<index-button-list :index="index_list" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ artist.name }}</p>
|
<p class="title is-4">
|
||||||
|
{{ artist.name }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_artist_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #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">
|
||||||
<list-tracks :tracks="tracks.items" :uris="track_uris"></list-tracks>
|
<a class="has-text-link" @click="open_artist"
|
||||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
>{{ artist.album_count }} albums</a
|
||||||
|
>
|
||||||
|
| {{ artist.track_count }} tracks
|
||||||
|
</p>
|
||||||
|
<list-tracks :tracks="tracks.items" :uris="track_uris" />
|
||||||
|
<modal-dialog-artist
|
||||||
|
:show="show_artist_details_modal"
|
||||||
|
:artist="artist"
|
||||||
|
@close="show_artist_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -49,9 +66,27 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageArtistTracks',
|
name: 'PageArtistTracks',
|
||||||
components: { ContentWithHeading, ListTracks, IndexButtonList, ModalDialogArtist },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListTracks,
|
||||||
|
IndexButtonList,
|
||||||
|
ModalDialogArtist
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
artist: {},
|
artist: {},
|
||||||
tracks: { items: [] },
|
tracks: { items: [] },
|
||||||
@ -61,13 +96,18 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.tracks.items
|
return [
|
||||||
.map(track => track.title_sort.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.tracks.items.map((track) =>
|
||||||
|
track.title_sort.charAt(0).toUpperCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
track_uris () {
|
track_uris() {
|
||||||
return this.tracks.items.map(a => a.uri).join(',')
|
return this.tracks.items.map((a) => a.uri).join(',')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -78,24 +118,13 @@ 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,43 +1,62 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="artists_list.indexList"></index-button-list>
|
<index-button-list :index="artists_list.indexList" />
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p class="heading" style="margin-bottom: 24px;">Filter</p>
|
<p class="heading" style="margin-bottom: 24px">Filter</p>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="switchHideSingles" type="checkbox" name="switchHideSingles" class="switch" v-model="hide_singles">
|
<input
|
||||||
|
id="switchHideSingles"
|
||||||
|
v-model="hide_singles"
|
||||||
|
type="checkbox"
|
||||||
|
name="switchHideSingles"
|
||||||
|
class="switch"
|
||||||
|
/>
|
||||||
<label for="switchHideSingles">Hide singles</label>
|
<label for="switchHideSingles">Hide singles</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="help">If active, hides artists that only appear on singles or playlists.</p>
|
<p class="help">
|
||||||
|
If active, hides artists that only appear on singles or
|
||||||
|
playlists.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="field" v-if="spotify_enabled">
|
<div v-if="spotify_enabled" class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="switchHideSpotify" type="checkbox" name="switchHideSpotify" class="switch" v-model="hide_spotify">
|
<input
|
||||||
|
id="switchHideSpotify"
|
||||||
|
v-model="hide_spotify"
|
||||||
|
type="checkbox"
|
||||||
|
name="switchHideSpotify"
|
||||||
|
class="switch"
|
||||||
|
/>
|
||||||
<label for="switchHideSpotify">Hide artists from Spotify</label>
|
<label for="switchHideSpotify">Hide artists from Spotify</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="help">If active, hides artists that only appear in your Spotify library.</p>
|
<p class="help">
|
||||||
|
If active, hides artists that only appear in your Spotify
|
||||||
|
library.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p class="heading" style="margin-bottom: 24px;">Sort by</p>
|
<p class="heading" style="margin-bottom: 24px">Sort by</p>
|
||||||
<dropdown-menu v-model="sort" :options="sort_options"></dropdown-menu>
|
<dropdown-menu v-model="sort" :options="sort_options" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Artists</p>
|
<p class="title is-4">Artists</p>
|
||||||
<p class="heading">{{ artists_list.sortedAndFiltered.length }} Artists</p>
|
<p class="heading">
|
||||||
|
{{ artists_list.sortedAndFiltered.length }} Artists
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right />
|
||||||
</template>
|
<template #content>
|
||||||
<template v-slot:content>
|
<list-artists :artists="artists_list" />
|
||||||
<list-artists :artists="artists_list"></list-artists>
|
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -65,9 +84,32 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageArtists',
|
name: 'PageArtists',
|
||||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListArtists, DropdownMenu },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
TabsMusic,
|
||||||
|
IndexButtonList,
|
||||||
|
ListArtists,
|
||||||
|
DropdownMenu
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
if (this.artists.items.length > 0) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
artists: { items: [] },
|
artists: { items: [] },
|
||||||
sort_options: ['Name', 'Recently added']
|
sort_options: ['Name', 'Recently added']
|
||||||
@ -75,7 +117,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
artists_list () {
|
artists_list() {
|
||||||
return new Artists(this.artists.items, {
|
return new Artists(this.artists.items, {
|
||||||
hideSingles: this.hide_singles,
|
hideSingles: this.hide_singles,
|
||||||
hideSpotify: this.hide_spotify,
|
hideSpotify: this.hide_spotify,
|
||||||
@ -84,33 +126,33 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
spotify_enabled () {
|
spotify_enabled() {
|
||||||
return this.$store.state.spotify.webapi_token_valid
|
return this.$store.state.spotify.webapi_token_valid
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_singles: {
|
hide_singles: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.hide_singles
|
return this.$store.state.hide_singles
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.HIDE_SINGLES, value)
|
this.$store.commit(types.HIDE_SINGLES, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_spotify: {
|
hide_spotify: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.hide_spotify
|
return this.$store.state.hide_spotify
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.HIDE_SPOTIFY, value)
|
this.$store.commit(types.HIDE_SPOTIFY, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
sort: {
|
sort: {
|
||||||
get () {
|
get() {
|
||||||
return this.$store.state.artists_sort
|
return this.$store.state.artists_sort
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$store.commit(types.ARTISTS_SORT, value)
|
this.$store.commit(types.ARTISTS_SORT, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,26 +162,8 @@ export default {
|
|||||||
scrollToTop: function () {
|
scrollToTop: function () {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
if (this.artists.items.length > 0) {
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,31 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-hero>
|
<content-with-hero>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<h1 class="title is-5">{{ album.name }}</h1>
|
<h1 class="title is-5">
|
||||||
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artist }}</a></h2>
|
{{ album.name }}
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-6 has-text-link has-text-weight-normal">
|
||||||
|
<a class="has-text-link" @click="open_artist">{{ album.artist }}</a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div class="buttons fd-is-centered-mobile fd-has-margin-top">
|
<div class="buttons fd-is-centered-mobile fd-has-margin-top">
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span>Play</span>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span>Play</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_album_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<p class="image is-square fd-has-shadow fd-has-action">
|
<p class="image is-square fd-has-shadow fd-has-action">
|
||||||
<cover-artwork
|
<cover-artwork
|
||||||
:artwork_url="album.artwork_url"
|
:artwork_url="album.artwork_url"
|
||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
@click="show_album_details_modal = true" />
|
@click="show_album_details_modal = true"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.track_count }} tracks</p>
|
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">
|
||||||
<list-tracks :tracks="tracks" :uris="album.uri"></list-tracks>
|
{{ album.track_count }} tracks
|
||||||
<modal-dialog-album :show="show_album_details_modal" :album="album" :media_kind="'audiobook'" @close="show_album_details_modal = false" />
|
</p>
|
||||||
|
<list-tracks :tracks="tracks" :uris="album.uri" />
|
||||||
|
<modal-dialog-album
|
||||||
|
:show="show_album_details_modal"
|
||||||
|
:album="album"
|
||||||
|
:media_kind="'audiobook'"
|
||||||
|
@close="show_album_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-hero>
|
</content-with-hero>
|
||||||
</template>
|
</template>
|
||||||
@ -55,7 +73,20 @@ export default {
|
|||||||
name: 'PageAudiobooksAlbum',
|
name: 'PageAudiobooksAlbum',
|
||||||
components: { ContentWithHero, ListTracks, ModalDialogAlbum, CoverArtwork },
|
components: { ContentWithHero, ListTracks, ModalDialogAlbum, CoverArtwork },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
tracks: [],
|
tracks: [],
|
||||||
@ -82,22 +113,8 @@ export default {
|
|||||||
this.selected_track = track
|
this.selected_track = track
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-audiobooks></tabs-audiobooks>
|
<tabs-audiobooks />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="albums_list.indexList"></index-button-list>
|
<index-button-list :index="albums_list.indexList" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Audiobooks</p>
|
<p class="title is-4">Audiobooks</p>
|
||||||
<p class="heading">{{ albums_list.sortedAndFiltered.length }} Audiobooks</p>
|
<p class="heading">
|
||||||
|
{{ albums_list.sortedAndFiltered.length }} Audiobooks
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="albums_list"></list-albums>
|
<list-albums :albums="albums_list" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -37,16 +39,34 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageAudiobooksAlbums',
|
name: 'PageAudiobooksAlbums',
|
||||||
components: { TabsAudiobooks, ContentWithHeading, IndexButtonList, ListAlbums },
|
components: {
|
||||||
|
TabsAudiobooks,
|
||||||
|
ContentWithHeading,
|
||||||
|
IndexButtonList,
|
||||||
|
ListAlbums
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
albums: { items: [] }
|
albums: { items: [] }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
albums_list () {
|
albums_list() {
|
||||||
return new Albums(this.albums.items, {
|
return new Albums(this.albums.items, {
|
||||||
sort: 'Name',
|
sort: 'Name',
|
||||||
group: true
|
group: true
|
||||||
@ -54,23 +74,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,22 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ artist.name }}</p>
|
<p class="title is-4">
|
||||||
|
{{ artist.name }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_artist_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_artist_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">{{ artist.album_count }} albums</p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-albums :albums="albums.items"></list-albums>
|
{{ artist.album_count }} albums
|
||||||
<modal-dialog-artist :show="show_artist_details_modal" :artist="artist" @close="show_artist_details_modal = false" />
|
</p>
|
||||||
|
<list-albums :albums="albums.items" />
|
||||||
|
<modal-dialog-artist
|
||||||
|
:show="show_artist_details_modal"
|
||||||
|
:artist="artist"
|
||||||
|
@close="show_artist_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -45,7 +59,20 @@ export default {
|
|||||||
name: 'PageAudiobooksArtist',
|
name: 'PageAudiobooksArtist',
|
||||||
components: { ContentWithHeading, ListAlbums, ModalDialogArtist },
|
components: { ContentWithHeading, ListAlbums, ModalDialogArtist },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
artist: {},
|
artist: {},
|
||||||
albums: {},
|
albums: {},
|
||||||
@ -56,24 +83,13 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.albums.items.map(a => a.uri).join(','), false)
|
webapi.player_play_uri(
|
||||||
|
this.albums.items.map((a) => a.uri).join(','),
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-audiobooks></tabs-audiobooks>
|
<tabs-audiobooks />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="artists_list.indexList"></index-button-list>
|
<index-button-list :index="artists_list.indexList" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Authors</p>
|
<p class="title is-4">Authors</p>
|
||||||
<p class="heading">{{ artists_list.sortedAndFiltered.length }} Authors</p>
|
<p class="heading">
|
||||||
|
{{ artists_list.sortedAndFiltered.length }} Authors
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right />
|
||||||
</template>
|
<template #content>
|
||||||
<template v-slot:content>
|
<list-artists :artists="artists_list" />
|
||||||
<list-artists :artists="artists_list"></list-artists>
|
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -39,16 +40,34 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageAudiobooksArtists',
|
name: 'PageAudiobooksArtists',
|
||||||
components: { ContentWithHeading, TabsAudiobooks, IndexButtonList, ListArtists },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
TabsAudiobooks,
|
||||||
|
IndexButtonList,
|
||||||
|
ListArtists
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
artists: { items: [] }
|
artists: { items: [] }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
artists_list () {
|
artists_list() {
|
||||||
return new Artists(this.artists.items, {
|
return new Artists(this.artists.items, {
|
||||||
sort: 'Name',
|
sort: 'Name',
|
||||||
group: true
|
group: true
|
||||||
@ -56,23 +75,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<!-- Recently added -->
|
<!-- Recently added -->
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Recently added</p>
|
<p class="title is-4">Recently added</p>
|
||||||
<p class="heading">albums</p>
|
<p class="heading">albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="recently_added.items"></list-albums>
|
<list-albums :albums="recently_added.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_browse('recently_added')">Show more</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_browse('recently_added')"
|
||||||
|
>Show more</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
@ -22,17 +26,21 @@
|
|||||||
|
|
||||||
<!-- Recently played -->
|
<!-- Recently played -->
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Recently played</p>
|
<p class="title is-4">Recently played</p>
|
||||||
<p class="heading">tracks</p>
|
<p class="heading">tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-tracks :tracks="recently_played.items"></list-tracks>
|
<list-tracks :tracks="recently_played.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_browse('recently_played')">Show more</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_browse('recently_played')"
|
||||||
|
>Show more</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
@ -50,8 +58,18 @@ import webapi from '@/webapi'
|
|||||||
const dataObject = {
|
const dataObject = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
webapi.search({ type: 'album', expression: 'time_added after 8 weeks ago and media_kind is music having track_count > 3 order by time_added desc', limit: 3 }),
|
webapi.search({
|
||||||
webapi.search({ type: 'track', expression: 'time_played after 8 weeks ago and media_kind is music order by time_played desc', limit: 3 })
|
type: 'album',
|
||||||
|
expression:
|
||||||
|
'time_added after 8 weeks ago and media_kind is music having track_count > 3 order by time_added desc',
|
||||||
|
limit: 3
|
||||||
|
}),
|
||||||
|
webapi.search({
|
||||||
|
type: 'track',
|
||||||
|
expression:
|
||||||
|
'time_played after 8 weeks ago and media_kind is music order by time_played desc',
|
||||||
|
limit: 3
|
||||||
|
})
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -65,7 +83,20 @@ export default {
|
|||||||
name: 'PageBrowse',
|
name: 'PageBrowse',
|
||||||
components: { ContentWithHeading, TabsMusic, ListAlbums, ListTracks },
|
components: { ContentWithHeading, TabsMusic, ListAlbums, ListTracks },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
recently_added: { items: [] },
|
recently_added: { items: [] },
|
||||||
recently_played: { items: [] },
|
recently_played: { items: [] },
|
||||||
@ -79,22 +110,8 @@ export default {
|
|||||||
open_browse: function (type) {
|
open_browse: function (type) {
|
||||||
this.$router.push({ path: '/music/browse/' + type })
|
this.$router.push({ path: '/music/browse/' + type })
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Recently added</p>
|
<p class="title is-4">Recently added</p>
|
||||||
<p class="heading">albums</p>
|
<p class="heading">albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="albums_list"></list-albums>
|
<list-albums :albums="albums_list" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -27,7 +27,8 @@ const dataObject = {
|
|||||||
const limit = store.getters.settings_option_recently_added_limit
|
const limit = store.getters.settings_option_recently_added_limit
|
||||||
return webapi.search({
|
return webapi.search({
|
||||||
type: 'album',
|
type: 'album',
|
||||||
expression: 'media_kind is music having track_count > 3 order by time_added desc',
|
expression:
|
||||||
|
'media_kind is music having track_count > 3 order by time_added desc',
|
||||||
limit: limit
|
limit: limit
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -41,14 +42,27 @@ export default {
|
|||||||
name: 'PageBrowseType',
|
name: 'PageBrowseType',
|
||||||
components: { ContentWithHeading, TabsMusic, ListAlbums },
|
components: { ContentWithHeading, TabsMusic, ListAlbums },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
recently_added: { items: [] }
|
recently_added: { items: [] }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
albums_list () {
|
albums_list() {
|
||||||
return new Albums(this.recently_added.items, {
|
return new Albums(this.recently_added.items, {
|
||||||
hideSingles: false,
|
hideSingles: false,
|
||||||
hideSpotify: false,
|
hideSpotify: false,
|
||||||
@ -56,22 +70,8 @@ export default {
|
|||||||
group: true
|
group: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Recently played</p>
|
<p class="title is-4">Recently played</p>
|
||||||
<p class="heading">tracks</p>
|
<p class="heading">tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-tracks :tracks="recently_played.items"></list-tracks>
|
<list-tracks :tracks="recently_played.items" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -24,7 +24,8 @@ const dataObject = {
|
|||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.search({
|
return webapi.search({
|
||||||
type: 'track',
|
type: 'track',
|
||||||
expression: 'time_played after 8 weeks ago and media_kind is music order by time_played desc',
|
expression:
|
||||||
|
'time_played after 8 weeks ago and media_kind is music order by time_played desc',
|
||||||
limit: 50
|
limit: 50
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -38,26 +39,25 @@ export default {
|
|||||||
name: 'PageBrowseType',
|
name: 'PageBrowseType',
|
||||||
components: { ContentWithHeading, TabsMusic, ListTracks },
|
components: { ContentWithHeading, TabsMusic, ListTracks },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
return {
|
|
||||||
recently_played: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load(to).then((response) => {
|
||||||
next(vm => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeRouteUpdate (to, from, next) {
|
beforeRouteUpdate(to, from, next) {
|
||||||
const vm = this
|
const vm = this
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load(to).then((response) => {
|
||||||
dataObject.set(vm, response)
|
dataObject.set(vm, response)
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
recently_played: {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,33 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="index_list"></index-button-list>
|
<index-button-list :index="index_list" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ name }}</p>
|
<p class="title is-4">
|
||||||
|
{{ name }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_composer_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">{{ composer_albums.total }} albums | <a class="has-text-link" @click="open_tracks">tracks</a></p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-item-albums v-for="album in composer_albums.items" :key="album.id" :album="album" @click="open_album(album)">
|
{{ composer_albums.total }} albums |
|
||||||
|
<a class="has-text-link" @click="open_tracks">tracks</a>
|
||||||
|
</p>
|
||||||
|
<list-item-albums
|
||||||
|
v-for="album in composer_albums.items"
|
||||||
|
:key="album.id"
|
||||||
|
:album="album"
|
||||||
|
@click="open_album(album)"
|
||||||
|
>
|
||||||
<template slot:actions>
|
<template slot:actions>
|
||||||
<a @click="open_dialog(album)">
|
<a @click="open_dialog(album)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-albums>
|
</list-item-albums>
|
||||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
<modal-dialog-album
|
||||||
<modal-dialog-composer :show="show_composer_details_modal" :composer="{ 'name': name }" @close="show_composer_details_modal = false" />
|
:show="show_details_modal"
|
||||||
|
:album="selected_album"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
|
<modal-dialog-composer
|
||||||
|
:show="show_composer_details_modal"
|
||||||
|
:composer="{ name: name }"
|
||||||
|
@close="show_composer_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -53,9 +79,27 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageComposer',
|
name: 'PageComposer',
|
||||||
components: { ContentWithHeading, ListItemAlbums, ModalDialogAlbum, ModalDialogComposer },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListItemAlbums,
|
||||||
|
ModalDialogAlbum,
|
||||||
|
ModalDialogComposer
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
composer_albums: { items: [] },
|
composer_albums: { items: [] },
|
||||||
@ -67,20 +111,31 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.composer_albums.items
|
return [
|
||||||
.map(album => album.name_sort.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.composer_albums.items.map((album) =>
|
||||||
|
album.name_sort.charAt(0).toUpperCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_tracks: function () {
|
open_tracks: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
this.$router.push({ name: 'ComposerTracks', params: { composer: this.name } })
|
this.$router.push({
|
||||||
|
name: 'ComposerTracks',
|
||||||
|
params: { composer: this.name }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression('composer is "' + this.name + '" and media_kind is music', true)
|
webapi.player_play_expression(
|
||||||
|
'composer is "' + this.name + '" and media_kind is music',
|
||||||
|
true
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
open_album: function (album) {
|
open_album: function (album) {
|
||||||
@ -91,22 +146,8 @@ export default {
|
|||||||
this.selected_album = album
|
this.selected_album = album
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,33 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="index_list"></index-button-list>
|
<index-button-list :index="index_list" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ composer }}</p>
|
<p class="title is-4">
|
||||||
|
{{ composer }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_composer_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_albums">albums</a> | {{ tracks.total }} tracks</p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-item-track v-for="(track, index) in rated_tracks" :key="track.id" :track="track" @click="play_track(index)">
|
<a class="has-text-link" @click="open_albums">albums</a> |
|
||||||
<template v-slot:actions>
|
{{ tracks.total }} tracks
|
||||||
|
</p>
|
||||||
|
<list-item-track
|
||||||
|
v-for="(track, index) in rated_tracks"
|
||||||
|
:key="track.id"
|
||||||
|
:track="track"
|
||||||
|
@click="play_track(index)"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
<a @click.prevent.stop="open_dialog(track)">
|
<a @click.prevent.stop="open_dialog(track)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-track>
|
</list-item-track>
|
||||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
<modal-dialog-track
|
||||||
<modal-dialog-composer :show="show_composer_details_modal" :composer="{ 'name': composer }" @close="show_composer_details_modal = false" />
|
:show="show_details_modal"
|
||||||
|
:track="selected_track"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
|
<modal-dialog-composer
|
||||||
|
:show="show_composer_details_modal"
|
||||||
|
:composer="{ name: composer }"
|
||||||
|
@close="show_composer_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -53,9 +79,27 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageComposerTracks',
|
name: 'PageComposerTracks',
|
||||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack, ModalDialogComposer },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListItemTrack,
|
||||||
|
ModalDialogTrack,
|
||||||
|
ModalDialogComposer
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
tracks: { items: [] },
|
tracks: { items: [] },
|
||||||
composer: '',
|
composer: '',
|
||||||
@ -70,28 +114,45 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.tracks.items
|
return [
|
||||||
.map(track => track.title_sort.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.tracks.items.map((track) =>
|
||||||
|
track.title_sort.charAt(0).toUpperCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
rated_tracks () {
|
rated_tracks() {
|
||||||
return this.tracks.items.filter(track => track.rating >= this.min_rating)
|
return this.tracks.items.filter(
|
||||||
|
(track) => track.rating >= this.min_rating
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_albums: function () {
|
open_albums: function () {
|
||||||
this.show_details_modal = false
|
this.show_details_modal = false
|
||||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: this.composer } })
|
this.$router.push({
|
||||||
|
name: 'ComposerAlbums',
|
||||||
|
params: { composer: this.composer }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression('composer is "' + this.composer + '" and media_kind is music', true)
|
webapi.player_play_expression(
|
||||||
|
'composer is "' + this.composer + '" and media_kind is music',
|
||||||
|
true
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
play_track: function (position) {
|
play_track: function (position) {
|
||||||
webapi.player_play_expression('composer is "' + this.composer + '" and media_kind is music', false, position)
|
webapi.player_play_expression(
|
||||||
|
'composer is "' + this.composer + '" and media_kind is music',
|
||||||
|
false,
|
||||||
|
position
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
show_rating: function (rating) {
|
show_rating: function (rating) {
|
||||||
@ -105,22 +166,8 @@ export default {
|
|||||||
this.selected_track = track
|
this.selected_track = track
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="composers_list.indexList"></index-button-list>
|
<index-button-list :index="composers_list.indexList" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ heading }}</p>
|
<p class="title is-4">
|
||||||
|
{{ heading }}
|
||||||
|
</p>
|
||||||
<p class="heading">{{ composers.total }} composers</p>
|
<p class="heading">{{ composers.total }} composers</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-composers :composers="composers_list"></list-composers>
|
<list-composers :composers="composers_list" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +47,20 @@ export default {
|
|||||||
name: 'PageComposers',
|
name: 'PageComposers',
|
||||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListComposers },
|
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListComposers },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
composers: { items: [] },
|
composers: { items: [] },
|
||||||
heading: '',
|
heading: '',
|
||||||
@ -56,12 +71,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.composers.items
|
return [
|
||||||
.map(composer => composer.name.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.composers.items.map((composer) =>
|
||||||
|
composer.name.charAt(0).toUpperCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
composers_list () {
|
composers_list() {
|
||||||
return new Composers(this.composers.items, {
|
return new Composers(this.composers.items, {
|
||||||
sort: 'Name',
|
sort: 'Name',
|
||||||
group: true
|
group: true
|
||||||
@ -71,29 +91,18 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_composer: function (composer) {
|
open_composer: function (composer) {
|
||||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: composer.name } })
|
this.$router.push({
|
||||||
|
name: 'ComposerAlbums',
|
||||||
|
params: { composer: composer.name }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_dialog: function (composer) {
|
open_dialog: function (composer) {
|
||||||
this.selected_composer = composer
|
this.selected_composer = composer
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,72 +1,117 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Files</p>
|
<p class="title is-4">Files</p>
|
||||||
<p class="title is-7 has-text-grey">{{ current_directory }}</p>
|
<p class="title is-7 has-text-grey">
|
||||||
|
{{ current_directory }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="open_directory_dialog({ 'path': current_directory })">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="open_directory_dialog({ path: current_directory })"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span>Play</span>
|
<span class="icon"><i class="mdi mdi-play" /></span>
|
||||||
|
<span>Play</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="media" v-if="$route.query.directory" @click="open_parent_directory()">
|
<div
|
||||||
|
v-if="$route.query.directory"
|
||||||
|
class="media"
|
||||||
|
@click="open_parent_directory()"
|
||||||
|
>
|
||||||
<figure class="media-left fd-has-action">
|
<figure class="media-left fd-has-action">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-subdirectory-arrow-left"></i>
|
<i class="mdi mdi-subdirectory-arrow-left" />
|
||||||
</span>
|
</span>
|
||||||
</figure>
|
</figure>
|
||||||
<div class="media-content fd-has-action is-clipped">
|
<div class="media-content fd-has-action is-clipped">
|
||||||
<h1 class="title is-6">..</h1>
|
<h1 class="title is-6">..</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<list-item-directory v-for="directory in files.directories" :key="directory.path" :directory="directory" @click="open_directory(directory)">
|
<list-item-directory
|
||||||
<template v-slot:actions>
|
v-for="directory in files.directories"
|
||||||
<a @click="open_directory_dialog(directory)">
|
:key="directory.path"
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
:directory="directory"
|
||||||
</a>
|
@click="open_directory(directory)"
|
||||||
</template>
|
>
|
||||||
|
<template #actions>
|
||||||
|
<a @click="open_directory_dialog(directory)">
|
||||||
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
</list-item-directory>
|
</list-item-directory>
|
||||||
|
|
||||||
<list-item-playlist v-for="playlist in files.playlists.items" :key="playlist.id" :playlist="playlist" @click="open_playlist(playlist)">
|
<list-item-playlist
|
||||||
<template v-slot:icon>
|
v-for="playlist in files.playlists.items"
|
||||||
|
:key="playlist.id"
|
||||||
|
:playlist="playlist"
|
||||||
|
@click="open_playlist(playlist)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-library-music"></i>
|
<i class="mdi mdi-library-music" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:actions>
|
<template #actions>
|
||||||
<a @click="open_playlist_dialog(playlist)">
|
<a @click="open_playlist_dialog(playlist)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-playlist>
|
</list-item-playlist>
|
||||||
|
|
||||||
<list-item-track v-for="(track, index) in files.tracks.items" :key="track.id" :track="track" @click="play_track(index)">
|
<list-item-track
|
||||||
<template v-slot:icon>
|
v-for="(track, index) in files.tracks.items"
|
||||||
|
:key="track.id"
|
||||||
|
:track="track"
|
||||||
|
@click="play_track(index)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-file-outline"></i>
|
<i class="mdi mdi-file-outline" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:actions>
|
<template #actions>
|
||||||
<a @click="open_track_dialog(track)">
|
<a @click="open_track_dialog(track)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-track>
|
</list-item-track>
|
||||||
|
|
||||||
<modal-dialog-directory :show="show_directory_details_modal" :directory="selected_directory" @close="show_directory_details_modal = false" />
|
<modal-dialog-directory
|
||||||
<modal-dialog-playlist :show="show_playlist_details_modal" :playlist="selected_playlist" @close="show_playlist_details_modal = false" />
|
:show="show_directory_details_modal"
|
||||||
<modal-dialog-track :show="show_track_details_modal" :track="selected_track" @close="show_track_details_modal = false" />
|
:directory="selected_directory"
|
||||||
|
@close="show_directory_details_modal = false"
|
||||||
|
/>
|
||||||
|
<modal-dialog-playlist
|
||||||
|
:show="show_playlist_details_modal"
|
||||||
|
:playlist="selected_playlist"
|
||||||
|
@close="show_playlist_details_modal = false"
|
||||||
|
/>
|
||||||
|
<modal-dialog-track
|
||||||
|
:show="show_track_details_modal"
|
||||||
|
:track="selected_track"
|
||||||
|
@close="show_track_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -95,7 +140,9 @@ const dataObject = {
|
|||||||
vm.files = response.data
|
vm.files = response.data
|
||||||
} else {
|
} else {
|
||||||
vm.files = {
|
vm.files = {
|
||||||
directories: vm.$store.state.config.directories.map(dir => { return { path: dir } }),
|
directories: vm.$store.state.config.directories.map((dir) => {
|
||||||
|
return { path: dir }
|
||||||
|
}),
|
||||||
tracks: { items: [] },
|
tracks: { items: [] },
|
||||||
playlists: { items: [] }
|
playlists: { items: [] }
|
||||||
}
|
}
|
||||||
@ -105,11 +152,36 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageFiles',
|
name: 'PageFiles',
|
||||||
components: { ContentWithHeading, ListItemDirectory, ListItemPlaylist, ListItemTrack, ModalDialogDirectory, ModalDialogPlaylist, ModalDialogTrack },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListItemDirectory,
|
||||||
|
ListItemPlaylist,
|
||||||
|
ListItemTrack,
|
||||||
|
ModalDialogDirectory,
|
||||||
|
ModalDialogPlaylist,
|
||||||
|
ModalDialogTrack
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
files: { directories: [], tracks: { items: [] }, playlists: { items: [] } },
|
files: {
|
||||||
|
directories: [],
|
||||||
|
tracks: { items: [] },
|
||||||
|
playlists: { items: [] }
|
||||||
|
},
|
||||||
|
|
||||||
show_directory_details_modal: false,
|
show_directory_details_modal: false,
|
||||||
selected_directory: {},
|
selected_directory: {},
|
||||||
@ -123,7 +195,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
current_directory () {
|
current_directory() {
|
||||||
if (this.$route.query && this.$route.query.directory) {
|
if (this.$route.query && this.$route.query.directory) {
|
||||||
return this.$route.query.directory
|
return this.$route.query.directory
|
||||||
}
|
}
|
||||||
@ -133,16 +205,33 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_parent_directory: function () {
|
open_parent_directory: function () {
|
||||||
const parent = this.current_directory.slice(0, this.current_directory.lastIndexOf('/'))
|
const parent = this.current_directory.slice(
|
||||||
if (parent === '' || this.$store.state.config.directories.includes(this.current_directory)) {
|
0,
|
||||||
|
this.current_directory.lastIndexOf('/')
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
parent === '' ||
|
||||||
|
this.$store.state.config.directories.includes(this.current_directory)
|
||||||
|
) {
|
||||||
this.$router.push({ path: '/files' })
|
this.$router.push({ path: '/files' })
|
||||||
} else {
|
} else {
|
||||||
this.$router.push({ path: '/files', query: { directory: this.current_directory.slice(0, this.current_directory.lastIndexOf('/')) } })
|
this.$router.push({
|
||||||
|
path: '/files',
|
||||||
|
query: {
|
||||||
|
directory: this.current_directory.slice(
|
||||||
|
0,
|
||||||
|
this.current_directory.lastIndexOf('/')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
open_directory: function (directory) {
|
open_directory: function (directory) {
|
||||||
this.$router.push({ path: '/files', query: { directory: directory.path } })
|
this.$router.push({
|
||||||
|
path: '/files',
|
||||||
|
query: { directory: directory.path }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_directory_dialog: function (directory) {
|
open_directory_dialog: function (directory) {
|
||||||
@ -151,11 +240,18 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression('path starts with "' + this.current_directory + '" order by path asc', false)
|
webapi.player_play_expression(
|
||||||
|
'path starts with "' + this.current_directory + '" order by path asc',
|
||||||
|
false
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
play_track: function (position) {
|
play_track: function (position) {
|
||||||
webapi.player_play_uri(this.files.tracks.items.map(a => a.uri).join(','), false, position)
|
webapi.player_play_uri(
|
||||||
|
this.files.tracks.items.map((a) => a.uri).join(','),
|
||||||
|
false,
|
||||||
|
position
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
open_track_dialog: function (track) {
|
open_track_dialog: function (track) {
|
||||||
@ -171,22 +267,8 @@ export default {
|
|||||||
this.selected_playlist = playlist
|
this.selected_playlist = playlist
|
||||||
this.show_playlist_details_modal = true
|
this.show_playlist_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="index_list"></index-button-list>
|
<index-button-list :index="index_list" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ name }}</p>
|
<p class="title is-4">
|
||||||
|
{{ name }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_genre_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_genre_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">{{ genre_albums.total }} albums | <a class="has-text-link" @click="open_tracks">tracks</a></p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-albums :albums="genre_albums.items"></list-albums>
|
{{ genre_albums.total }} albums |
|
||||||
<modal-dialog-genre :show="show_genre_details_modal" :genre="{ 'name': name }" @close="show_genre_details_modal = false" />
|
<a class="has-text-link" @click="open_tracks">tracks</a>
|
||||||
|
</p>
|
||||||
|
<list-albums :albums="genre_albums.items" />
|
||||||
|
<modal-dialog-genre
|
||||||
|
:show="show_genre_details_modal"
|
||||||
|
:genre="{ name: name }"
|
||||||
|
@close="show_genre_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -46,9 +61,27 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageGenre',
|
name: 'PageGenre',
|
||||||
components: { ContentWithHeading, IndexButtonList, ListAlbums, ModalDialogGenre },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
IndexButtonList,
|
||||||
|
ListAlbums,
|
||||||
|
ModalDialogGenre
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
genre_albums: { items: [] },
|
genre_albums: { items: [] },
|
||||||
@ -58,9 +91,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.genre_albums.items
|
return [
|
||||||
.map(album => album.name.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.genre_albums.items.map((album) =>
|
||||||
|
album.name.charAt(0).toUpperCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -71,29 +109,18 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression('genre is "' + this.name + '" and media_kind is music', true)
|
webapi.player_play_expression(
|
||||||
|
'genre is "' + this.name + '" and media_kind is music',
|
||||||
|
true
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
open_dialog: function (album) {
|
open_dialog: function (album) {
|
||||||
this.selected_album = album
|
this.selected_album = album
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="index_list"></index-button-list>
|
<index-button-list :index="index_list" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ genre }}</p>
|
<p class="title is-4">
|
||||||
|
{{ genre }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_genre_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_genre_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_genre">albums</a> | {{ tracks.total }} tracks</p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-tracks :tracks="tracks.items" :expression="expression"></list-tracks>
|
<a class="has-text-link" @click="open_genre">albums</a> |
|
||||||
<modal-dialog-genre :show="show_genre_details_modal" :genre="{ 'name': genre }" @close="show_genre_details_modal = false" />
|
{{ tracks.total }} tracks
|
||||||
|
</p>
|
||||||
|
<list-tracks :tracks="tracks.items" :expression="expression" />
|
||||||
|
<modal-dialog-genre
|
||||||
|
:show="show_genre_details_modal"
|
||||||
|
:genre="{ name: genre }"
|
||||||
|
@close="show_genre_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -46,9 +61,27 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageGenreTracks',
|
name: 'PageGenreTracks',
|
||||||
components: { ContentWithHeading, ListTracks, IndexButtonList, ModalDialogGenre },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListTracks,
|
||||||
|
IndexButtonList,
|
||||||
|
ModalDialogGenre
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
tracks: { items: [] },
|
tracks: { items: [] },
|
||||||
genre: '',
|
genre: '',
|
||||||
@ -58,12 +91,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.tracks.items
|
return [
|
||||||
.map(track => track.title_sort.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.tracks.items.map((track) =>
|
||||||
|
track.title_sort.charAt(0).toUpperCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
expression () {
|
expression() {
|
||||||
return 'genre is "' + this.genre + '" and media_kind is music'
|
return 'genre is "' + this.genre + '" and media_kind is music'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -77,22 +115,8 @@ export default {
|
|||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_expression(this.expression, true)
|
webapi.player_play_expression(this.expression, true)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,24 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-music></tabs-music>
|
<tabs-music />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:options>
|
<template #options>
|
||||||
<index-button-list :index="index_list"></index-button-list>
|
<index-button-list :index="index_list" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-left>
|
<template #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 v-slot:content>
|
<template #content>
|
||||||
<list-item-genre v-for="genre in genres.items" :key="genre.name" :genre="genre" @click="open_genre(genre)">
|
<list-item-genre
|
||||||
<template v-slot:actions>
|
v-for="genre in genres.items"
|
||||||
|
:key="genre.name"
|
||||||
|
:genre="genre"
|
||||||
|
@click="open_genre(genre)"
|
||||||
|
>
|
||||||
|
<template #actions>
|
||||||
<a @click.prevent.stop="open_dialog(genre)">
|
<a @click.prevent.stop="open_dialog(genre)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-genre>
|
</list-item-genre>
|
||||||
<modal-dialog-genre :show="show_details_modal" :genre="selected_genre" @close="show_details_modal = false" />
|
<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>
|
||||||
@ -44,9 +55,28 @@ const dataObject = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageGenres',
|
name: 'PageGenres',
|
||||||
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemGenre, ModalDialogGenre },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
TabsMusic,
|
||||||
|
IndexButtonList,
|
||||||
|
ListItemGenre,
|
||||||
|
ModalDialogGenre
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
genres: { items: [] },
|
genres: { items: [] },
|
||||||
|
|
||||||
@ -56,9 +86,12 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
index_list () {
|
index_list() {
|
||||||
return [...new Set(this.genres.items
|
return [
|
||||||
.map(genre => genre.name.charAt(0).toUpperCase()))]
|
...new Set(
|
||||||
|
this.genres.items.map((genre) => genre.name.charAt(0).toUpperCase())
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -71,22 +104,8 @@ export default {
|
|||||||
this.selected_genre = genre
|
this.selected_genre = genre
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -2,23 +2,27 @@
|
|||||||
<section>
|
<section>
|
||||||
<div v-if="now_playing.id > 0" class="fd-is-fullheight">
|
<div v-if="now_playing.id > 0" class="fd-is-fullheight">
|
||||||
<div class="fd-is-expanded">
|
<div class="fd-is-expanded">
|
||||||
<cover-artwork @click="open_dialog(now_playing)"
|
<cover-artwork
|
||||||
:artwork_url="now_playing.artwork_url"
|
:artwork_url="now_playing.artwork_url"
|
||||||
:artist="now_playing.artist"
|
:artist="now_playing.artist"
|
||||||
:album="now_playing.album"
|
:album="now_playing.album"
|
||||||
class="fd-cover-image fd-has-action" />
|
class="fd-cover-image fd-has-action"
|
||||||
|
@click="open_dialog(now_playing)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="fd-has-padding-left-right">
|
<div class="fd-has-padding-left-right">
|
||||||
<div class="container has-text-centered">
|
<div class="container has-text-centered">
|
||||||
<p class="control has-text-centered fd-progress-now-playing">
|
<p class="control has-text-centered fd-progress-now-playing">
|
||||||
<Slider v-model="item_progress_ms"
|
<Slider
|
||||||
|
v-model="item_progress_ms"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="state.item_length_ms"
|
:max="state.item_length_ms"
|
||||||
:step="1000"
|
:step="1000"
|
||||||
:tooltips="false"
|
:tooltips="false"
|
||||||
:disabled="state.state === 'stop'"
|
:disabled="state.state === 'stop'"
|
||||||
|
:classes="{ target: 'seek-slider' }"
|
||||||
@change="seek"
|
@change="seek"
|
||||||
:classes="{ target: 'seek-slider'}" />
|
/>
|
||||||
<!--range-slider
|
<!--range-slider
|
||||||
class="seek-slider fd-has-action"
|
class="seek-slider fd-has-action"
|
||||||
min="0"
|
min="0"
|
||||||
@ -30,7 +34,10 @@
|
|||||||
</range-slider-->
|
</range-slider-->
|
||||||
</p>
|
</p>
|
||||||
<p class="content">
|
<p class="content">
|
||||||
<span>{{ $filters.duration(item_progress_ms) }} / {{ $filters.duration(now_playing.length_ms) }}</span>
|
<span
|
||||||
|
>{{ $filters.duration(item_progress_ms) }} /
|
||||||
|
{{ $filters.duration(now_playing.length_ms) }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,8 +49,11 @@
|
|||||||
<h2 class="title is-6">
|
<h2 class="title is-6">
|
||||||
{{ now_playing.artist }}
|
{{ now_playing.artist }}
|
||||||
</h2>
|
</h2>
|
||||||
<h2 class="subtitle is-6 has-text-grey has-text-weight-bold" v-if="composer">
|
<h2
|
||||||
{{ composer }}
|
v-if="composer"
|
||||||
|
class="subtitle is-6 has-text-grey has-text-weight-bold"
|
||||||
|
>
|
||||||
|
{{ composer }}
|
||||||
</h2>
|
</h2>
|
||||||
<h3 class="subtitle is-6">
|
<h3 class="subtitle is-6">
|
||||||
{{ now_playing.album }}
|
{{ now_playing.album }}
|
||||||
@ -52,18 +62,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="fd-is-fullheight">
|
<div v-else class="fd-is-fullheight">
|
||||||
<div class="fd-is-expanded fd-has-padding-left-right" style="flex-direction: column;">
|
<div
|
||||||
|
class="fd-is-expanded fd-has-padding-left-right"
|
||||||
|
style="flex-direction: column"
|
||||||
|
>
|
||||||
<div class="content has-text-centered">
|
<div class="content has-text-centered">
|
||||||
<h1 class="title is-5">
|
<h1 class="title is-5">Your play queue is empty</h1>
|
||||||
Your play queue is empty
|
<p>Add some tracks by browsing your library</p>
|
||||||
</h1>
|
|
||||||
<p>
|
|
||||||
Add some tracks by browsing your library
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
<modal-dialog-queue-item
|
||||||
|
:show="show_details_modal"
|
||||||
|
:item="selected_item"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -79,12 +92,12 @@ export default {
|
|||||||
name: 'PageNowPlaying',
|
name: 'PageNowPlaying',
|
||||||
components: {
|
components: {
|
||||||
ModalDialogQueueItem,
|
ModalDialogQueueItem,
|
||||||
// RangeSlider,
|
// RangeSlider,
|
||||||
Slider,
|
Slider,
|
||||||
CoverArtwork
|
CoverArtwork
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
item_progress_ms: 0,
|
item_progress_ms: 0,
|
||||||
interval_id: 0,
|
interval_id: 0,
|
||||||
@ -94,7 +107,57 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created () {
|
computed: {
|
||||||
|
state() {
|
||||||
|
return this.$store.state.player
|
||||||
|
},
|
||||||
|
|
||||||
|
now_playing() {
|
||||||
|
return this.$store.getters.now_playing
|
||||||
|
},
|
||||||
|
|
||||||
|
settings_option_show_composer_now_playing() {
|
||||||
|
return this.$store.getters.settings_option_show_composer_now_playing
|
||||||
|
},
|
||||||
|
|
||||||
|
settings_option_show_composer_for_genre() {
|
||||||
|
return this.$store.getters.settings_option_show_composer_for_genre
|
||||||
|
},
|
||||||
|
|
||||||
|
composer() {
|
||||||
|
if (this.settings_option_show_composer_now_playing) {
|
||||||
|
if (
|
||||||
|
!this.settings_option_show_composer_for_genre ||
|
||||||
|
(this.now_playing.genre &&
|
||||||
|
this.settings_option_show_composer_for_genre
|
||||||
|
.toLowerCase()
|
||||||
|
.split(',')
|
||||||
|
.findIndex(
|
||||||
|
(elem) =>
|
||||||
|
this.now_playing.genre.toLowerCase().indexOf(elem.trim()) >= 0
|
||||||
|
) >= 0)
|
||||||
|
) {
|
||||||
|
return this.now_playing.composer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
state() {
|
||||||
|
if (this.interval_id > 0) {
|
||||||
|
window.clearTimeout(this.interval_id)
|
||||||
|
this.interval_id = 0
|
||||||
|
}
|
||||||
|
this.item_progress_ms = this.state.item_progress_ms
|
||||||
|
if (this.state.state === 'play') {
|
||||||
|
this.interval_id = window.setInterval(this.tick, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
this.item_progress_ms = this.state.item_progress_ms
|
this.item_progress_ms = this.state.item_progress_ms
|
||||||
webapi.player_status().then(({ data }) => {
|
webapi.player_status().then(({ data }) => {
|
||||||
this.$store.commit(types.UPDATE_PLAYER_STATUS, data)
|
this.$store.commit(types.UPDATE_PLAYER_STATUS, data)
|
||||||
@ -104,44 +167,13 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyed () {
|
unmounted() {
|
||||||
if (this.interval_id > 0) {
|
if (this.interval_id > 0) {
|
||||||
window.clearTimeout(this.interval_id)
|
window.clearTimeout(this.interval_id)
|
||||||
this.interval_id = 0
|
this.interval_id = 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
state () {
|
|
||||||
return this.$store.state.player
|
|
||||||
},
|
|
||||||
|
|
||||||
now_playing () {
|
|
||||||
return this.$store.getters.now_playing
|
|
||||||
},
|
|
||||||
|
|
||||||
settings_option_show_composer_now_playing () {
|
|
||||||
return this.$store.getters.settings_option_show_composer_now_playing
|
|
||||||
},
|
|
||||||
|
|
||||||
settings_option_show_composer_for_genre () {
|
|
||||||
return this.$store.getters.settings_option_show_composer_for_genre
|
|
||||||
},
|
|
||||||
|
|
||||||
composer () {
|
|
||||||
if (this.settings_option_show_composer_now_playing) {
|
|
||||||
if (!this.settings_option_show_composer_for_genre ||
|
|
||||||
(this.now_playing.genre &&
|
|
||||||
this.settings_option_show_composer_for_genre.toLowerCase()
|
|
||||||
.split(',')
|
|
||||||
.findIndex(elem => this.now_playing.genre.toLowerCase().indexOf(elem.trim()) >= 0) >= 0)) {
|
|
||||||
return this.now_playing.composer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
tick: function () {
|
tick: function () {
|
||||||
this.item_progress_ms += 1000
|
this.item_progress_ms += 1000
|
||||||
@ -157,22 +189,8 @@ export default {
|
|||||||
this.selected_item = item
|
this.selected_item = item
|
||||||
this.show_details_modal = true
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'state' () {
|
|
||||||
if (this.interval_id > 0) {
|
|
||||||
window.clearTimeout(this.interval_id)
|
|
||||||
this.interval_id = 0
|
|
||||||
}
|
|
||||||
this.item_progress_ms = this.state.item_progress_ms
|
|
||||||
if (this.state.state === 'play') {
|
|
||||||
this.interval_id = window.setInterval(this.tick, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,22 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<div class="title is-4">{{ playlist.name }}</div>
|
<div class="title is-4">
|
||||||
|
{{ playlist.name }}
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_playlist_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_playlist_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle" /></span>
|
||||||
|
<span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">{{ tracks.length }} tracks</p>
|
<p class="heading has-text-centered-mobile">{{ tracks.length }} tracks</p>
|
||||||
<list-tracks :tracks="tracks" :uris="uris"></list-tracks>
|
<list-tracks :tracks="tracks" :uris="uris" />
|
||||||
<modal-dialog-playlist :show="show_playlist_details_modal" :playlist="playlist" :uris="uris" @close="show_playlist_details_modal = false" />
|
<modal-dialog-playlist
|
||||||
|
:show="show_playlist_details_modal"
|
||||||
|
:playlist="playlist"
|
||||||
|
:uris="uris"
|
||||||
|
@close="show_playlist_details_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -45,7 +58,20 @@ export default {
|
|||||||
name: 'PagePlaylist',
|
name: 'PagePlaylist',
|
||||||
components: { ContentWithHeading, ListTracks, ModalDialogPlaylist },
|
components: { ContentWithHeading, ListTracks, ModalDialogPlaylist },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
playlist: {},
|
playlist: {},
|
||||||
tracks: [],
|
tracks: [],
|
||||||
@ -55,9 +81,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
uris () {
|
uris() {
|
||||||
if (this.playlist.random) {
|
if (this.playlist.random) {
|
||||||
return this.tracks.map(a => a.uri).join(',')
|
return this.tracks.map((a) => a.uri).join(',')
|
||||||
}
|
}
|
||||||
return this.playlist.uri
|
return this.playlist.uri
|
||||||
}
|
}
|
||||||
@ -67,22 +93,8 @@ export default {
|
|||||||
play: function () {
|
play: function () {
|
||||||
webapi.player_play_uri(this.uris, true)
|
webapi.player_play_uri(this.uris, true)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">{{ playlist.name }}</p>
|
<p class="title is-4">
|
||||||
|
{{ playlist.name }}
|
||||||
|
</p>
|
||||||
<p class="heading">{{ playlists.total }} playlists</p>
|
<p class="heading">{{ playlists.total }} playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-playlists :playlists="playlists.items"></list-playlists>
|
<list-playlists :playlists="playlists.items" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -33,27 +35,26 @@ export default {
|
|||||||
name: 'PagePlaylists',
|
name: 'PagePlaylists',
|
||||||
components: { ContentWithHeading, ListPlaylists },
|
components: { ContentWithHeading, ListPlaylists },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
return {
|
|
||||||
playlist: {},
|
|
||||||
playlists: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load(to).then((response) => {
|
||||||
next(vm => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeRouteUpdate (to, from, next) {
|
beforeRouteUpdate(to, from, next) {
|
||||||
const vm = this
|
const vm = this
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load(to).then((response) => {
|
||||||
dataObject.set(vm, response)
|
dataObject.set(vm, response)
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
playlist: {},
|
||||||
|
playlists: {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,31 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<div class="title is-4">{{ album.name }}
|
<div class="title is-4">
|
||||||
|
{{ album.name }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
<a
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
class="button is-small is-light is-rounded"
|
||||||
|
@click="show_album_details_modal = true"
|
||||||
|
>
|
||||||
|
<span class="icon"
|
||||||
|
><i class="mdi mdi-dots-horizontal mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-play"></i>
|
<i class="mdi mdi-play" />
|
||||||
</span>
|
</span>
|
||||||
<span>Play</span>
|
<span>Play</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-item-track v-for="track in tracks" :key="track.id" :track="track" @click="play_track(track)">
|
{{ album.track_count }} tracks
|
||||||
<template v-slot:progress>
|
</p>
|
||||||
|
<list-item-track
|
||||||
|
v-for="track in tracks"
|
||||||
|
:key="track.id"
|
||||||
|
:track="track"
|
||||||
|
@click="play_track(track)"
|
||||||
|
>
|
||||||
|
<template #progress>
|
||||||
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
|
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:actions>
|
<template #actions>
|
||||||
<a @click.prevent.stop="open_dialog(track)">
|
<a @click.prevent.stop="open_dialog(track)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-track>
|
</list-item-track>
|
||||||
@ -33,7 +48,8 @@
|
|||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:track="selected_track"
|
:track="selected_track"
|
||||||
@close="show_details_modal = false"
|
@close="show_details_modal = false"
|
||||||
@play-count-changed="reload_tracks" />
|
@play-count-changed="reload_tracks"
|
||||||
|
/>
|
||||||
<modal-dialog-album
|
<modal-dialog-album
|
||||||
:show="show_album_details_modal"
|
:show="show_album_details_modal"
|
||||||
:album="album"
|
:album="album"
|
||||||
@ -41,16 +57,22 @@
|
|||||||
:new_tracks="new_tracks"
|
:new_tracks="new_tracks"
|
||||||
@close="show_album_details_modal = false"
|
@close="show_album_details_modal = false"
|
||||||
@play-count-changed="reload_tracks"
|
@play-count-changed="reload_tracks"
|
||||||
@remove-podcast="open_remove_podcast_dialog" />
|
@remove-podcast="open_remove_podcast_dialog"
|
||||||
|
/>
|
||||||
<modal-dialog
|
<modal-dialog
|
||||||
:show="show_remove_podcast_modal"
|
:show="show_remove_podcast_modal"
|
||||||
title="Remove podcast"
|
title="Remove podcast"
|
||||||
delete_action="Remove"
|
delete_action="Remove"
|
||||||
@close="show_remove_podcast_modal = false"
|
@close="show_remove_podcast_modal = false"
|
||||||
@delete="remove_podcast">
|
@delete="remove_podcast"
|
||||||
<template v-slot:modal-content>
|
>
|
||||||
|
<template #modal-content>
|
||||||
<p>Permanently remove this podcast from your library?</p>
|
<p>Permanently remove this podcast from your library?</p>
|
||||||
<p class="is-size-7">(This will also remove the RSS playlist <b>{{ rss_playlist_to_remove.name }}</b>.)</p>
|
<p class="is-size-7">
|
||||||
|
(This will also remove the RSS playlist
|
||||||
|
<b>{{ rss_playlist_to_remove.name }}</b
|
||||||
|
>.)
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</modal-dialog>
|
</modal-dialog>
|
||||||
</template>
|
</template>
|
||||||
@ -91,7 +113,20 @@ export default {
|
|||||||
ProgressBar
|
ProgressBar
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
tracks: [],
|
tracks: [],
|
||||||
@ -107,8 +142,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
new_tracks () {
|
new_tracks() {
|
||||||
return this.tracks.filter(track => track.play_count === 0).length
|
return this.tracks.filter((track) => track.play_count === 0).length
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -129,9 +164,12 @@ export default {
|
|||||||
open_remove_podcast_dialog: function () {
|
open_remove_podcast_dialog: function () {
|
||||||
this.show_album_details_modal = false
|
this.show_album_details_modal = false
|
||||||
webapi.library_track_playlists(this.tracks[0].id).then(({ data }) => {
|
webapi.library_track_playlists(this.tracks[0].id).then(({ data }) => {
|
||||||
const rssPlaylists = data.items.filter(pl => pl.type === 'rss')
|
const rssPlaylists = data.items.filter((pl) => pl.type === 'rss')
|
||||||
if (rssPlaylists.length !== 1) {
|
if (rssPlaylists.length !== 1) {
|
||||||
this.$store.dispatch('add_notification', { text: 'Podcast cannot be removed. Probably it was not added as an RSS playlist.', type: 'danger' })
|
this.$store.dispatch('add_notification', {
|
||||||
|
text: 'Podcast cannot be removed. Probably it was not added as an RSS playlist.',
|
||||||
|
type: 'danger'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,9 +180,11 @@ export default {
|
|||||||
|
|
||||||
remove_podcast: function () {
|
remove_podcast: function () {
|
||||||
this.show_remove_podcast_modal = false
|
this.show_remove_podcast_modal = false
|
||||||
webapi.library_playlist_delete(this.rss_playlist_to_remove.id).then(() => {
|
webapi
|
||||||
this.$router.replace({ path: '/podcasts' })
|
.library_playlist_delete(this.rss_playlist_to_remove.id)
|
||||||
})
|
.then(() => {
|
||||||
|
this.$router.replace({ path: '/podcasts' })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
reload_tracks: function () {
|
reload_tracks: function () {
|
||||||
@ -152,22 +192,8 @@ export default {
|
|||||||
this.tracks = data.tracks.items
|
this.tracks = data.tracks.items
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,64 +1,78 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading v-if="new_episodes.items.length > 0">
|
<content-with-heading v-if="new_episodes.items.length > 0">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">New episodes</p>
|
<p class="title is-4">New episodes</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small" @click="mark_all_played">
|
<a class="button is-small" @click="mark_all_played">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-pencil"></i>
|
<i class="mdi mdi-pencil" />
|
||||||
</span>
|
</span>
|
||||||
<span>Mark All Played</span>
|
<span>Mark All Played</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-item-track v-for="track in new_episodes.items" :key="track.id" :track="track" @click="play_track(track)">
|
<list-item-track
|
||||||
<template v-slot:progress>
|
v-for="track in new_episodes.items"
|
||||||
|
:key="track.id"
|
||||||
|
:track="track"
|
||||||
|
@click="play_track(track)"
|
||||||
|
>
|
||||||
|
<template #progress>
|
||||||
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
|
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:actions>
|
<template #actions>
|
||||||
<a @click="open_track_dialog(track)">
|
<a @click="open_track_dialog(track)">
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<span class="icon has-text-dark"
|
||||||
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</list-item-track>
|
</list-item-track>
|
||||||
<modal-dialog-track :show="show_track_details_modal" :track="selected_track" @close="show_track_details_modal = false" @play-count-changed="reload_new_episodes" />
|
<modal-dialog-track
|
||||||
|
:show="show_track_details_modal"
|
||||||
|
:track="selected_track"
|
||||||
|
@close="show_track_details_modal = false"
|
||||||
|
@play-count-changed="reload_new_episodes"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Podcasts</p>
|
<p class="title is-4">Podcasts</p>
|
||||||
<p class="heading">{{ albums.total }} podcasts</p>
|
<p class="heading">{{ albums.total }} podcasts</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a v-if="rss.tracks > 0" class="button is-small" @click="update_rss">
|
<a v-if="rss.tracks > 0" class="button is-small" @click="update_rss">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-refresh"></i>
|
<i class="mdi mdi-refresh" />
|
||||||
</span>
|
</span>
|
||||||
<span>Update</span>
|
<span>Update</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small" @click="open_add_podcast_dialog">
|
<a class="button is-small" @click="open_add_podcast_dialog">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-rss"></i>
|
<i class="mdi mdi-rss" />
|
||||||
</span>
|
</span>
|
||||||
<span>Add Podcast</span>
|
<span>Add Podcast</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="albums.items"
|
<list-albums
|
||||||
@play-count-changed="reload_new_episodes()"
|
:albums="albums.items"
|
||||||
@podcast-deleted="reload_podcasts()">
|
@play-count-changed="reload_new_episodes()"
|
||||||
</list-albums>
|
@podcast-deleted="reload_podcasts()"
|
||||||
|
/>
|
||||||
<modal-dialog-add-rss
|
<modal-dialog-add-rss
|
||||||
:show="show_url_modal"
|
:show="show_url_modal"
|
||||||
@close="show_url_modal = false"
|
@close="show_url_modal = false"
|
||||||
@podcast-added="reload_podcasts()" />
|
@podcast-added="reload_podcasts()"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -99,7 +113,20 @@ export default {
|
|||||||
ProgressBar
|
ProgressBar
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
next((vm) => dataObject.set(vm, response))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
const vm = this
|
||||||
|
dataObject.load(to).then((response) => {
|
||||||
|
dataObject.set(vm, response)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
albums: { items: [] },
|
albums: { items: [] },
|
||||||
new_episodes: { items: [] },
|
new_episodes: { items: [] },
|
||||||
@ -112,7 +139,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
rss () {
|
rss() {
|
||||||
return this.$store.state.rss_count
|
return this.$store.state.rss_count
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -128,10 +155,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mark_all_played: function () {
|
mark_all_played: function () {
|
||||||
this.new_episodes.items.forEach(ep => {
|
this.new_episodes.items.forEach((ep) => {
|
||||||
webapi.library_track_update(ep.id, { play_count: 'increment' })
|
webapi.library_track_update(ep.id, { play_count: 'increment' })
|
||||||
})
|
})
|
||||||
this.new_episodes.items = { }
|
this.new_episodes.items = {}
|
||||||
},
|
},
|
||||||
|
|
||||||
open_add_podcast_dialog: function (item) {
|
open_add_podcast_dialog: function (item) {
|
||||||
@ -155,22 +182,8 @@ export default {
|
|||||||
this.$store.commit(types.UPDATE_DIALOG_SCAN_KIND, 'rss')
|
this.$store.commit(types.UPDATE_DIALOG_SCAN_KIND, 'rss')
|
||||||
this.$store.commit(types.SHOW_UPDATE_DIALOG, true)
|
this.$store.commit(types.SHOW_UPDATE_DIALOG, true)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
next(vm => dataObject.set(vm, response))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeRouteUpdate (to, from, next) {
|
|
||||||
const vm = this
|
|
||||||
dataObject.load(to).then((response) => {
|
|
||||||
dataObject.set(vm, response)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,66 +1,103 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="heading">{{ queue.count }} tracks</p>
|
<p class="heading">{{ queue.count }} tracks</p>
|
||||||
<p class="title is-4">Queue</p>
|
<p class="title is-4">Queue</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:heading-right>
|
<template #heading-right>
|
||||||
<div class="buttons is-centered">
|
<div class="buttons is-centered">
|
||||||
<a class="button is-small" :class="{ 'is-info': show_only_next_items }" @click="update_show_next_items">
|
<a
|
||||||
|
class="button is-small"
|
||||||
|
:class="{ 'is-info': show_only_next_items }"
|
||||||
|
@click="update_show_next_items"
|
||||||
|
>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-arrow-collapse-down"></i>
|
<i class="mdi mdi-arrow-collapse-down" />
|
||||||
</span>
|
</span>
|
||||||
<span>Hide previous</span>
|
<span>Hide previous</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small" @click="open_add_stream_dialog">
|
<a class="button is-small" @click="open_add_stream_dialog">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-web"></i>
|
<i class="mdi mdi-web" />
|
||||||
</span>
|
</span>
|
||||||
<span>Add Stream</span>
|
<span>Add Stream</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small" :class="{ 'is-info': edit_mode }" @click="edit_mode = !edit_mode">
|
<a
|
||||||
|
class="button is-small"
|
||||||
|
:class="{ 'is-info': edit_mode }"
|
||||||
|
@click="edit_mode = !edit_mode"
|
||||||
|
>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-pencil"></i>
|
<i class="mdi mdi-pencil" />
|
||||||
</span>
|
</span>
|
||||||
<span>Edit</span>
|
<span>Edit</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small" @click="queue_clear">
|
<a class="button is-small" @click="queue_clear">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-delete-empty"></i>
|
<i class="mdi mdi-delete-empty" />
|
||||||
</span>
|
</span>
|
||||||
<span>Clear</span>
|
<span>Clear</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button is-small" v-if="is_queue_save_allowed" :disabled="queue_items.length === 0" @click="save_dialog">
|
<a
|
||||||
|
v-if="is_queue_save_allowed"
|
||||||
|
class="button is-small"
|
||||||
|
:disabled="queue_items.length === 0"
|
||||||
|
@click="save_dialog"
|
||||||
|
>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="mdi mdi-content-save"></i>
|
<i class="mdi mdi-content-save" />
|
||||||
</span>
|
</span>
|
||||||
<span>Save</span>
|
<span>Save</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<draggable v-model="queue_items" handle=".handle" item-key="id" @end="move_item">
|
<draggable
|
||||||
|
v-model="queue_items"
|
||||||
|
handle=".handle"
|
||||||
|
item-key="id"
|
||||||
|
@end="move_item"
|
||||||
|
>
|
||||||
<template #item="{ element, index }">
|
<template #item="{ element, index }">
|
||||||
<list-item-queue-item
|
<list-item-queue-item
|
||||||
:item="element"
|
:item="element"
|
||||||
:position="index"
|
: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">
|
:edit_mode="edit_mode"
|
||||||
<template v-slot:actions>
|
>
|
||||||
<a @click.prevent.stop="open_dialog(element)" v-if="!edit_mode">
|
<template #actions>
|
||||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
<a v-if="!edit_mode" @click.prevent.stop="open_dialog(element)">
|
||||||
</a>
|
<span class="icon has-text-dark"
|
||||||
<a @click.prevent.stop="remove(element)" v-if="element.id !== state.item_id && edit_mode">
|
><i class="mdi mdi-dots-vertical mdi-18px"
|
||||||
<span class="icon has-text-grey"><i class="mdi mdi-delete mdi-18px"></i></span>
|
/></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
<a
|
||||||
</list-item-queue-item>
|
v-if="element.id !== state.item_id && edit_mode"
|
||||||
|
@click.prevent.stop="remove(element)"
|
||||||
|
>
|
||||||
|
<span class="icon has-text-grey"
|
||||||
|
><i class="mdi mdi-delete mdi-18px"
|
||||||
|
/></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</list-item-queue-item>
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
<modal-dialog-queue-item :show="show_details_modal" :item="selected_item" @close="show_details_modal = false" />
|
<modal-dialog-queue-item
|
||||||
<modal-dialog-add-url-stream :show="show_url_modal" @close="show_url_modal = false" />
|
:show="show_details_modal"
|
||||||
<modal-dialog-playlist-save v-if="is_queue_save_allowed" :show="show_pls_save_modal" @close="show_pls_save_modal = false" />
|
:item="selected_item"
|
||||||
|
@close="show_details_modal = false"
|
||||||
|
/>
|
||||||
|
<modal-dialog-add-url-stream
|
||||||
|
:show="show_url_modal"
|
||||||
|
@close="show_url_modal = false"
|
||||||
|
/>
|
||||||
|
<modal-dialog-playlist-save
|
||||||
|
v-if="is_queue_save_allowed"
|
||||||
|
:show="show_pls_save_modal"
|
||||||
|
@close="show_pls_save_modal = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</template>
|
</template>
|
||||||
@ -77,9 +114,16 @@ import draggable from 'vuedraggable'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageQueue',
|
name: 'PageQueue',
|
||||||
components: { ContentWithHeading, ListItemQueueItem, draggable, ModalDialogQueueItem, ModalDialogAddUrlStream, ModalDialogPlaylistSave },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ListItemQueueItem,
|
||||||
|
draggable,
|
||||||
|
ModalDialogQueueItem,
|
||||||
|
ModalDialogAddUrlStream,
|
||||||
|
ModalDialogPlaylistSave
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
edit_mode: false,
|
edit_mode: false,
|
||||||
|
|
||||||
@ -91,24 +135,33 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
state () {
|
state() {
|
||||||
return this.$store.state.player
|
return this.$store.state.player
|
||||||
},
|
},
|
||||||
is_queue_save_allowed () {
|
is_queue_save_allowed() {
|
||||||
return this.$store.state.config.allow_modifying_stored_playlists && this.$store.state.config.default_playlist_directory
|
return (
|
||||||
|
this.$store.state.config.allow_modifying_stored_playlists &&
|
||||||
|
this.$store.state.config.default_playlist_directory
|
||||||
|
)
|
||||||
},
|
},
|
||||||
queue () {
|
queue() {
|
||||||
return this.$store.state.queue
|
return this.$store.state.queue
|
||||||
},
|
},
|
||||||
queue_items: {
|
queue_items: {
|
||||||
get () { return this.$store.state.queue.items },
|
get() {
|
||||||
set (value) { /* Do nothing? Send move request in @end event */ }
|
return this.$store.state.queue.items
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
/* Do nothing? Send move request in @end event */
|
||||||
|
}
|
||||||
},
|
},
|
||||||
current_position () {
|
current_position() {
|
||||||
const nowPlaying = this.$store.getters.now_playing
|
const nowPlaying = this.$store.getters.now_playing
|
||||||
return nowPlaying === undefined || nowPlaying.position === undefined ? -1 : this.$store.getters.now_playing.position
|
return nowPlaying === undefined || nowPlaying.position === undefined
|
||||||
|
? -1
|
||||||
|
: this.$store.getters.now_playing.position
|
||||||
},
|
},
|
||||||
show_only_next_items () {
|
show_only_next_items() {
|
||||||
return this.$store.state.show_only_next_items
|
return this.$store.state.show_only_next_items
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -127,7 +180,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
move_item: function (e) {
|
move_item: function (e) {
|
||||||
const oldPosition = !this.show_only_next_items ? e.oldIndex : e.oldIndex + this.current_position
|
const oldPosition = !this.show_only_next_items
|
||||||
|
? e.oldIndex
|
||||||
|
: e.oldIndex + this.current_position
|
||||||
const item = this.queue_items[oldPosition]
|
const item = this.queue_items[oldPosition]
|
||||||
const newPosition = item.position + (e.newIndex - e.oldIndex)
|
const newPosition = item.position + (e.newIndex - e.oldIndex)
|
||||||
if (newPosition !== oldPosition) {
|
if (newPosition !== oldPosition) {
|
||||||
@ -153,5 +208,4 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Radio</p>
|
<p class="title is-4">Radio</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p class="heading has-text-centered-mobile">{{ tracks.total }} tracks</p>
|
<p class="heading has-text-centered-mobile">
|
||||||
<list-tracks :tracks="tracks.items"></list-tracks>
|
{{ tracks.total }} tracks
|
||||||
|
</p>
|
||||||
|
<list-tracks :tracks="tracks.items" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
@ -31,26 +33,25 @@ export default {
|
|||||||
name: 'PageRadioStreams',
|
name: 'PageRadioStreams',
|
||||||
components: { ContentWithHeading, ListTracks },
|
components: { ContentWithHeading, ListTracks },
|
||||||
|
|
||||||
data () {
|
beforeRouteEnter(to, from, next) {
|
||||||
return {
|
|
||||||
tracks: { items: [] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load(to).then((response) => {
|
||||||
next(vm => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeRouteUpdate (to, from, next) {
|
beforeRouteUpdate(to, from, next) {
|
||||||
const vm = this
|
const vm = this
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load(to).then((response) => {
|
||||||
dataObject.set(vm, response)
|
dataObject.set(vm, response)
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tracks: { items: [] }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -5,91 +5,122 @@
|
|||||||
<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">
|
||||||
<form v-on:submit.prevent="new_search">
|
<form @submit.prevent="new_search">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p class="control is-expanded has-icons-left">
|
<p class="control is-expanded has-icons-left">
|
||||||
<input class="input is-rounded is-shadowless" type="text" placeholder="Search" v-model="search_query" ref="search_field" autocomplete="off">
|
<input
|
||||||
|
ref="search_field"
|
||||||
|
v-model="search_query"
|
||||||
|
class="input is-rounded is-shadowless"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="mdi mdi-magnify"></i>
|
<i class="mdi mdi-magnify" />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="help has-text-centered">Tip: you can search by a smart playlist query language <a href="https://github.com/owntone/owntone-server/blob/master/README_SMARTPL.md" target="_blank">expression</a> if you prefix it
|
<p class="help has-text-centered">
|
||||||
with <code>query:</code>.
|
Tip: you can search by a smart playlist query language
|
||||||
|
<a
|
||||||
|
href="https://github.com/owntone/owntone-server/blob/master/README_SMARTPL.md"
|
||||||
|
target="_blank"
|
||||||
|
>expression</a
|
||||||
|
>
|
||||||
|
if you prefix it with <code>query:</code>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="tags" style="margin-top: 16px;">
|
<div class="tags" style="margin-top: 16px">
|
||||||
<a class="tag" v-for="recent_search in recent_searches" :key="recent_search" @click="open_recent_search(recent_search)">{{ recent_search }}</a>
|
<a
|
||||||
|
v-for="recent_search in recent_searches"
|
||||||
|
:key="recent_search"
|
||||||
|
class="tag"
|
||||||
|
@click="open_recent_search(recent_search)"
|
||||||
|
>{{ recent_search }}</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<tabs-search :query="search_query"></tabs-search>
|
<tabs-search :query="search_query" />
|
||||||
|
|
||||||
<!-- Tracks -->
|
<!-- Tracks -->
|
||||||
<content-with-heading v-if="show_tracks && tracks.total">
|
<content-with-heading v-if="show_tracks && tracks.total">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Tracks</p>
|
<p class="title is-4">Tracks</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-tracks :tracks="tracks.items"></list-tracks>
|
<list-tracks :tracks="tracks.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav v-if="show_all_tracks_button" class="level">
|
<nav v-if="show_all_tracks_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_tracks">Show all {{ tracks.total.toLocaleString() }} tracks</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_tracks"
|
||||||
|
>Show all {{ tracks.total.toLocaleString() }} tracks</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show_tracks && !tracks.total" class="mt-6">
|
<content-text v-if="show_tracks && !tracks.total" class="mt-6">
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p><i>No tracks found</i></p>
|
<p><i>No tracks found</i></p>
|
||||||
</template>
|
</template>
|
||||||
</content-text>
|
</content-text>
|
||||||
|
|
||||||
<!-- Artists -->
|
<!-- Artists -->
|
||||||
<content-with-heading v-if="show_artists && artists.total">
|
<content-with-heading v-if="show_artists && artists.total">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Artists</p>
|
<p class="title is-4">Artists</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-artists :artists="artists.items"></list-artists>
|
<list-artists :artists="artists.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav v-if="show_all_artists_button" class="level">
|
<nav v-if="show_all_artists_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_artists">Show all {{ artists.total.toLocaleString() }} artists</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_artists"
|
||||||
|
>Show all {{ artists.total.toLocaleString() }} artists</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show_artists && !artists.total">
|
<content-text v-if="show_artists && !artists.total">
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p><i>No artists found</i></p>
|
<p><i>No artists found</i></p>
|
||||||
</template>
|
</template>
|
||||||
</content-text>
|
</content-text>
|
||||||
|
|
||||||
<!-- Albums -->
|
<!-- Albums -->
|
||||||
<content-with-heading v-if="show_albums && albums.total">
|
<content-with-heading v-if="show_albums && albums.total">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Albums</p>
|
<p class="title is-4">Albums</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="albums.items"></list-albums>
|
<list-albums :albums="albums.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav v-if="show_all_albums_button" class="level">
|
<nav v-if="show_all_albums_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_albums">Show all {{ albums.total.toLocaleString() }} albums</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_albums"
|
||||||
|
>Show all {{ albums.total.toLocaleString() }} albums</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show_albums && !albums.total">
|
<content-text v-if="show_albums && !albums.total">
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p><i>No albums found</i></p>
|
<p><i>No albums found</i></p>
|
||||||
</template>
|
</template>
|
||||||
</content-text>
|
</content-text>
|
||||||
@ -100,12 +131,16 @@
|
|||||||
<p class="title is-4">Composers</p>
|
<p class="title is-4">Composers</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot:content>
|
<template slot:content>
|
||||||
<list-composers :composers="composers.items"></list-composers>
|
<list-composers :composers="composers.items" />
|
||||||
</template>
|
</template>
|
||||||
<template slot:footer>
|
<template slot:footer>
|
||||||
<nav v-if="show_all_composers_button" class="level">
|
<nav v-if="show_all_composers_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_composers">Show all {{ composers.total }} composers</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_composers"
|
||||||
|
>Show all {{ composers.total }} composers</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
@ -118,66 +153,78 @@
|
|||||||
|
|
||||||
<!-- Playlists -->
|
<!-- Playlists -->
|
||||||
<content-with-heading v-if="show_playlists && playlists.total">
|
<content-with-heading v-if="show_playlists && playlists.total">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Playlists</p>
|
<p class="title is-4">Playlists</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-playlists :playlists="playlists.items"></list-playlists>
|
<list-playlists :playlists="playlists.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav v-if="show_all_playlists_button" class="level">
|
<nav v-if="show_all_playlists_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_playlists">Show all {{ playlists.total.toLocaleString() }} playlists</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_playlists"
|
||||||
|
>Show all {{ playlists.total.toLocaleString() }} playlists</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show_playlists && !playlists.total">
|
<content-text v-if="show_playlists && !playlists.total">
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p><i>No playlists found</i></p>
|
<p><i>No playlists found</i></p>
|
||||||
</template>
|
</template>
|
||||||
</content-text>
|
</content-text>
|
||||||
|
|
||||||
<!-- Podcasts -->
|
<!-- Podcasts -->
|
||||||
<content-with-heading v-if="show_podcasts && podcasts.total">
|
<content-with-heading v-if="show_podcasts && podcasts.total">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Podcasts</p>
|
<p class="title is-4">Podcasts</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="podcasts.items"></list-albums>
|
<list-albums :albums="podcasts.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav v-if="show_all_podcasts_button" class="level">
|
<nav v-if="show_all_podcasts_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_podcasts">Show all {{ podcasts.total.toLocaleString() }} podcasts</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_podcasts"
|
||||||
|
>Show all {{ podcasts.total.toLocaleString() }} podcasts</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show_podcasts && !podcasts.total">
|
<content-text v-if="show_podcasts && !podcasts.total">
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p><i>No podcasts found</i></p>
|
<p><i>No podcasts found</i></p>
|
||||||
</template>
|
</template>
|
||||||
</content-text>
|
</content-text>
|
||||||
|
|
||||||
<!-- Audiobooks -->
|
<!-- Audiobooks -->
|
||||||
<content-with-heading v-if="show_audiobooks && audiobooks.total">
|
<content-with-heading v-if="show_audiobooks && audiobooks.total">
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<p class="title is-4">Audiobooks</p>
|
<p class="title is-4">Audiobooks</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<list-albums :albums="audiobooks.items"></list-albums>
|
<list-albums :albums="audiobooks.items" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<nav v-if="show_all_audiobooks_button" class="level">
|
<nav v-if="show_all_audiobooks_button" class="level">
|
||||||
<p class="level-item">
|
<p class="level-item">
|
||||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_audiobooks">Show all {{ audiobooks.total.toLocaleString() }} audiobooks</a>
|
<a
|
||||||
|
class="button is-light is-small is-rounded"
|
||||||
|
@click="open_search_audiobooks"
|
||||||
|
>Show all {{ audiobooks.total.toLocaleString() }} audiobooks</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show_audiobooks && !audiobooks.total">
|
<content-text v-if="show_audiobooks && !audiobooks.total">
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<p><i>No audiobooks found</i></p>
|
<p><i>No audiobooks found</i></p>
|
||||||
</template>
|
</template>
|
||||||
</content-text>
|
</content-text>
|
||||||
@ -198,9 +245,18 @@ import * as types from '@/store/mutation_types'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PageSearch',
|
name: 'PageSearch',
|
||||||
components: { ContentWithHeading, ContentText, TabsSearch, ListTracks, ListArtists, ListAlbums, ListPlaylists, ListComposers },
|
components: {
|
||||||
|
ContentWithHeading,
|
||||||
|
ContentText,
|
||||||
|
TabsSearch,
|
||||||
|
ListTracks,
|
||||||
|
ListArtists,
|
||||||
|
ListAlbums,
|
||||||
|
ListPlaylists,
|
||||||
|
ListComposers
|
||||||
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
search_query: '',
|
search_query: '',
|
||||||
|
|
||||||
@ -215,64 +271,85 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
recent_searches () {
|
recent_searches() {
|
||||||
return this.$store.state.recent_searches
|
return this.$store.state.recent_searches
|
||||||
},
|
},
|
||||||
|
|
||||||
show_tracks () {
|
show_tracks() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('track')
|
return this.$route.query.type && this.$route.query.type.includes('track')
|
||||||
},
|
},
|
||||||
show_all_tracks_button () {
|
show_all_tracks_button() {
|
||||||
return this.tracks.total > this.tracks.items.length
|
return this.tracks.total > this.tracks.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
show_artists () {
|
show_artists() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('artist')
|
return this.$route.query.type && this.$route.query.type.includes('artist')
|
||||||
},
|
},
|
||||||
show_all_artists_button () {
|
show_all_artists_button() {
|
||||||
return this.artists.total > this.artists.items.length
|
return this.artists.total > this.artists.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
show_albums () {
|
show_albums() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('album')
|
return this.$route.query.type && this.$route.query.type.includes('album')
|
||||||
},
|
},
|
||||||
show_all_albums_button () {
|
show_all_albums_button() {
|
||||||
return this.albums.total > this.albums.items.length
|
return this.albums.total > this.albums.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
show_composers () {
|
show_composers() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('composer')
|
return (
|
||||||
|
this.$route.query.type && this.$route.query.type.includes('composer')
|
||||||
|
)
|
||||||
},
|
},
|
||||||
show_all_composers_button () {
|
show_all_composers_button() {
|
||||||
return this.composers.total > this.composers.items.length
|
return this.composers.total > this.composers.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
show_playlists () {
|
show_playlists() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('playlist')
|
return (
|
||||||
|
this.$route.query.type && this.$route.query.type.includes('playlist')
|
||||||
|
)
|
||||||
},
|
},
|
||||||
show_all_playlists_button () {
|
show_all_playlists_button() {
|
||||||
return this.playlists.total > this.playlists.items.length
|
return this.playlists.total > this.playlists.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
show_audiobooks () {
|
show_audiobooks() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('audiobook')
|
return (
|
||||||
|
this.$route.query.type && this.$route.query.type.includes('audiobook')
|
||||||
|
)
|
||||||
},
|
},
|
||||||
show_all_audiobooks_button () {
|
show_all_audiobooks_button() {
|
||||||
return this.audiobooks.total > this.audiobooks.items.length
|
return this.audiobooks.total > this.audiobooks.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
show_podcasts () {
|
show_podcasts() {
|
||||||
return this.$route.query.type && this.$route.query.type.includes('podcast')
|
return (
|
||||||
|
this.$route.query.type && this.$route.query.type.includes('podcast')
|
||||||
|
)
|
||||||
},
|
},
|
||||||
show_all_podcasts_button () {
|
show_all_podcasts_button() {
|
||||||
return this.podcasts.total > this.podcasts.items.length
|
return this.podcasts.total > this.podcasts.items.length
|
||||||
},
|
},
|
||||||
|
|
||||||
is_visible_artwork () {
|
is_visible_artwork() {
|
||||||
return this.$store.getters.settings_option('webinterface', 'show_cover_artwork_in_album_lists').value
|
return this.$store.getters.settings_option(
|
||||||
|
'webinterface',
|
||||||
|
'show_cover_artwork_in_album_lists'
|
||||||
|
).value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
$route(to, from) {
|
||||||
|
this.search(to)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted: function () {
|
||||||
|
this.search(this.$route)
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
search: function (route) {
|
search: function (route) {
|
||||||
if (!route.query.query || route.query.query === '') {
|
if (!route.query.query || route.query.query === '') {
|
||||||
@ -289,7 +366,12 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
searchMusic: function (query) {
|
searchMusic: function (query) {
|
||||||
if (query.type.indexOf('track') < 0 && query.type.indexOf('artist') < 0 && query.type.indexOf('album') < 0 && query.type.indexOf('playlist') < 0) {
|
if (
|
||||||
|
query.type.indexOf('track') < 0 &&
|
||||||
|
query.type.indexOf('artist') < 0 &&
|
||||||
|
query.type.indexOf('album') < 0 &&
|
||||||
|
query.type.indexOf('playlist') < 0
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,8 +395,12 @@ export default {
|
|||||||
this.tracks = data.tracks ? data.tracks : { items: [], total: 0 }
|
this.tracks = data.tracks ? data.tracks : { items: [], total: 0 }
|
||||||
this.artists = data.artists ? data.artists : { items: [], total: 0 }
|
this.artists = data.artists ? data.artists : { items: [], total: 0 }
|
||||||
this.albums = data.albums ? data.albums : { items: [], total: 0 }
|
this.albums = data.albums ? data.albums : { items: [], total: 0 }
|
||||||
this.composers = data.composers ? data.composers : { items: [], total: 0 }
|
this.composers = data.composers
|
||||||
this.playlists = data.playlists ? data.playlists : { items: [], total: 0 }
|
? data.composers
|
||||||
|
: { items: [], total: 0 }
|
||||||
|
this.playlists = data.playlists
|
||||||
|
? data.playlists
|
||||||
|
: { items: [], total: 0 }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -331,7 +417,12 @@ export default {
|
|||||||
if (query.query.startsWith('query:')) {
|
if (query.query.startsWith('query:')) {
|
||||||
searchParams.expression = query.query.replace(/^query:/, '').trim()
|
searchParams.expression = query.query.replace(/^query:/, '').trim()
|
||||||
} else {
|
} else {
|
||||||
searchParams.expression = '((album includes "' + query.query + '" or artist includes "' + query.query + '") and media_kind is audiobook)'
|
searchParams.expression =
|
||||||
|
'((album includes "' +
|
||||||
|
query.query +
|
||||||
|
'" or artist includes "' +
|
||||||
|
query.query +
|
||||||
|
'") and media_kind is audiobook)'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.limit) {
|
if (query.limit) {
|
||||||
@ -357,7 +448,12 @@ export default {
|
|||||||
if (query.query.startsWith('query:')) {
|
if (query.query.startsWith('query:')) {
|
||||||
searchParams.expression = query.query.replace(/^query:/, '').trim()
|
searchParams.expression = query.query.replace(/^query:/, '').trim()
|
||||||
} else {
|
} else {
|
||||||
searchParams.expression = '((album includes "' + query.query + '" or artist includes "' + query.query + '") and media_kind is podcast)'
|
searchParams.expression =
|
||||||
|
'((album includes "' +
|
||||||
|
query.query +
|
||||||
|
'" or artist includes "' +
|
||||||
|
query.query +
|
||||||
|
'") and media_kind is podcast)'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.limit) {
|
if (query.limit) {
|
||||||
@ -458,7 +554,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
open_composer: function (composer) {
|
open_composer: function (composer) {
|
||||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: composer.name } })
|
this.$router.push({
|
||||||
|
name: 'ComposerAlbums',
|
||||||
|
params: { composer: composer.name }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
open_playlist: function (playlist) {
|
open_playlist: function (playlist) {
|
||||||
@ -494,19 +593,8 @@ export default {
|
|||||||
this.selected_playlist = playlist
|
this.selected_playlist = playlist
|
||||||
this.show_playlist_details_modal = true
|
this.show_playlist_details_modal = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted: function () {
|
|
||||||
this.search(this.$route)
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'$route' (to, from) {
|
|
||||||
this.search(to)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,28 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fd-page-with-tabs">
|
<div class="fd-page-with-tabs">
|
||||||
<tabs-settings></tabs-settings>
|
<tabs-settings />
|
||||||
|
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
<template v-slot:heading-left>
|
<template #heading-left>
|
||||||
<div class="title is-4">Artwork</div>
|
<div class="title is-4">Artwork</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>
|
<p>
|
||||||
OwnTone supports PNG and JPEG artwork which is either placed as separate image files in the library,
|
OwnTone supports PNG and JPEG artwork which is either placed as
|
||||||
embedded in the media files or made available online by radio stations.
|
separate image files in the library, embedded in the media files or
|
||||||
|
made available online by radio stations.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In addition to that, you can enable fetching artwork from the
|
||||||
|
following artwork providers:
|
||||||
</p>
|
</p>
|
||||||
<p>In addition to that, you can enable fetching artwork from the following artwork providers:</p>
|
|
||||||
</div>
|
</div>
|
||||||
<settings-checkbox category_name="artwork" option_name="use_artwork_source_spotify" v-if="spotify.libspotify_logged_in">
|
<settings-checkbox
|
||||||
<template v-slot:label> Spotify</template>
|
v-if="spotify.libspotify_logged_in"
|
||||||
|
category_name="artwork"
|
||||||
|
option_name="use_artwork_source_spotify"
|
||||||
|
>
|
||||||
|
<template #label> Spotify </template>
|
||||||
</settings-checkbox>
|
</settings-checkbox>
|
||||||
<settings-checkbox category_name="artwork" option_name="use_artwork_source_discogs">
|
<settings-checkbox
|
||||||
<template v-slot:label> Discogs (<a href="https://www.discogs.com/">https://www.discogs.com/</a>)</template>
|
category_name="artwork"
|
||||||
|
option_name="use_artwork_source_discogs"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
Discogs (<a href="https://www.discogs.com/"
|
||||||
|
>https://www.discogs.com/</a
|
||||||
|
>)
|
||||||
|
</template>
|
||||||
</settings-checkbox>
|
</settings-checkbox>
|
||||||
<settings-checkbox category_name="artwork" option_name="use_artwork_source_coverartarchive">
|
<settings-checkbox
|
||||||
<template v-slot:label> Cover Art Archive (<a href="https://coverartarchive.org/">https://coverartarchive.org/</a>)</template>
|
category_name="artwork"
|
||||||
|
option_name="use_artwork_source_coverartarchive"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
Cover Art Archive (<a href="https://coverartarchive.org/"
|
||||||
|
>https://coverartarchive.org/</a
|
||||||
|
>)
|
||||||
|
</template>
|
||||||
</settings-checkbox>
|
</settings-checkbox>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
@ -39,12 +61,11 @@ export default {
|
|||||||
components: { ContentWithHeading, TabsSettings, SettingsCheckbox },
|
components: { ContentWithHeading, TabsSettings, SettingsCheckbox },
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
spotify () {
|
spotify() {
|
||||||
return this.$store.state.spotify
|
return this.$store.state.spotify
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user