mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-15 16:48:22 -04:00
[web] Switch to camel case
This commit is contained in:
parent
6c09457e5d
commit
7e8672917e
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
15
web-src/package-lock.json
generated
15
web-src/package-lock.json
generated
@ -518,9 +518,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
|
||||||
"integrity": "sha512-RoV8Xs9eNwiDvhv7M+xcL4PWyRyIXRY/FLp3buU4h1EYfdF7unWUy3dOjPqb3C7rMUewIcqwW850PgS8h1o1yg==",
|
"integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1960,12 +1960,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.8.2",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz",
|
||||||
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
|
"integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==",
|
||||||
"version": "1.8.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
|
|
||||||
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</router-view>
|
</router-view>
|
||||||
<modal-dialog-remote-pairing
|
<modal-dialog-remote-pairing
|
||||||
:show="pairing_active"
|
:show="pairingActive"
|
||||||
@close="pairing_active = false"
|
@close="pairingActive = false"
|
||||||
/>
|
/>
|
||||||
<modal-dialog-update
|
<modal-dialog-update
|
||||||
:show="showUpdateDialog"
|
:show="showUpdateDialog"
|
||||||
@ -67,9 +67,9 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
pairing_active: false,
|
pairingActive: false,
|
||||||
reconnect_attempts: 0,
|
reconnect_attempts: 0,
|
||||||
token_timer_id: 0
|
timerId: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -100,10 +100,10 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
showBurgerMenu() {
|
showBurgerMenu() {
|
||||||
this.update_is_clipped()
|
this.updateClipping()
|
||||||
},
|
},
|
||||||
showPlayerMenu() {
|
showPlayerMenu() {
|
||||||
this.update_is_clipped()
|
this.updateClipping()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -145,7 +145,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
openWebsocket() {
|
openWebsocket() {
|
||||||
const socket = this.create_websocket()
|
const socket = this.createWebsocket()
|
||||||
const vm = this
|
const vm = this
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
vm.reconnect_attempts = 0
|
vm.reconnect_attempts = 0
|
||||||
@ -165,14 +165,14 @@ export default {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
vm.update_outputs()
|
vm.updateOutputs()
|
||||||
vm.update_player_status()
|
vm.updatePlayerStatus()
|
||||||
vm.update_library_stats()
|
vm.updateLibraryStats()
|
||||||
vm.update_settings()
|
vm.updateSettings()
|
||||||
vm.update_queue()
|
vm.updateQueue()
|
||||||
vm.update_spotify()
|
vm.updateSpotify()
|
||||||
vm.update_lastfm()
|
vm.updateLastfm()
|
||||||
vm.update_pairing()
|
vm.updatePairing()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -181,23 +181,23 @@ export default {
|
|||||||
* There are two relevant events - focus and visibilitychange, so we
|
* There are two relevant events - focus and visibilitychange, so we
|
||||||
* throttle the updates to avoid multiple redundant updates.
|
* throttle the updates to avoid multiple redundant updates.
|
||||||
*/
|
*/
|
||||||
let update_throttled = false
|
let updateThrottled = false
|
||||||
|
|
||||||
const update_info = () => {
|
const updateInfo = () => {
|
||||||
if (update_throttled) {
|
if (updateThrottled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vm.update_outputs()
|
vm.updateOutputs()
|
||||||
vm.update_player_status()
|
vm.updatePlayerStatus()
|
||||||
vm.update_library_stats()
|
vm.updateLibraryStats()
|
||||||
vm.update_settings()
|
vm.updateSettings()
|
||||||
vm.update_queue()
|
vm.updateQueue()
|
||||||
vm.update_spotify()
|
vm.updateSpotify()
|
||||||
vm.update_lastfm()
|
vm.updateLastfm()
|
||||||
vm.update_pairing()
|
vm.updatePairing()
|
||||||
update_throttled = true
|
updateThrottled = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
update_throttled = false
|
updateThrottled = false
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,10 +205,10 @@ export default {
|
|||||||
* These events are fired when the window becomes active in different
|
* These events are fired when the window becomes active in different
|
||||||
* ways. When this happens, we should update 'now playing' info, etc.
|
* ways. When this happens, we should update 'now playing' info, etc.
|
||||||
*/
|
*/
|
||||||
window.addEventListener('focus', update_info)
|
window.addEventListener('focus', updateInfo)
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
if (document.visibilityState === 'visible') {
|
if (document.visibilityState === 'visible') {
|
||||||
update_info()
|
updateInfo()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -218,33 +218,33 @@ export default {
|
|||||||
data.notify.includes('update') ||
|
data.notify.includes('update') ||
|
||||||
data.notify.includes('database')
|
data.notify.includes('database')
|
||||||
) {
|
) {
|
||||||
vm.update_library_stats()
|
vm.updateLibraryStats()
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
data.notify.includes('player') ||
|
data.notify.includes('player') ||
|
||||||
data.notify.includes('options') ||
|
data.notify.includes('options') ||
|
||||||
data.notify.includes('volume')
|
data.notify.includes('volume')
|
||||||
) {
|
) {
|
||||||
vm.update_player_status()
|
vm.updatePlayerStatus()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('outputs') || data.notify.includes('volume')) {
|
if (data.notify.includes('outputs') || data.notify.includes('volume')) {
|
||||||
vm.update_outputs()
|
vm.updateOutputs()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('queue')) {
|
if (data.notify.includes('queue')) {
|
||||||
vm.update_queue()
|
vm.updateQueue()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('spotify')) {
|
if (data.notify.includes('spotify')) {
|
||||||
vm.update_spotify()
|
vm.updateSpotify()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('lastfm')) {
|
if (data.notify.includes('lastfm')) {
|
||||||
vm.update_lastfm()
|
vm.updateLastfm()
|
||||||
}
|
}
|
||||||
if (data.notify.includes('pairing')) {
|
if (data.notify.includes('pairing')) {
|
||||||
vm.update_pairing()
|
vm.updatePairing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
create_websocket() {
|
createWebsocket() {
|
||||||
const protocol = window.location.protocol.replace('http', 'ws')
|
const protocol = window.location.protocol.replace('http', 'ws')
|
||||||
const hostname =
|
const hostname =
|
||||||
(import.meta.env.DEV &&
|
(import.meta.env.DEV &&
|
||||||
@ -258,19 +258,19 @@ export default {
|
|||||||
reconnectInterval: 1000
|
reconnectInterval: 1000
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_is_clipped() {
|
updateClipping() {
|
||||||
if (this.showBurgerMenu || this.showPlayerMenu) {
|
if (this.showBurgerMenu || this.showPlayerMenu) {
|
||||||
document.querySelector('html').classList.add('is-clipped')
|
document.querySelector('html').classList.add('is-clipped')
|
||||||
} else {
|
} else {
|
||||||
document.querySelector('html').classList.remove('is-clipped')
|
document.querySelector('html').classList.remove('is-clipped')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update_lastfm() {
|
updateLastfm() {
|
||||||
webapi.lastfm().then(({ data }) => {
|
webapi.lastfm().then(({ data }) => {
|
||||||
this.servicesStore.lastfm = data
|
this.servicesStore.lastfm = data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_library_stats() {
|
updateLibraryStats() {
|
||||||
webapi.library_stats().then(({ data }) => {
|
webapi.library_stats().then(({ data }) => {
|
||||||
this.libraryStore.$state = data
|
this.libraryStore.$state = data
|
||||||
})
|
})
|
||||||
@ -278,7 +278,7 @@ export default {
|
|||||||
this.libraryStore.rss = data
|
this.libraryStore.rss = data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_lyrics() {
|
updateLyrics() {
|
||||||
const track = this.queueStore.current
|
const track = this.queueStore.current
|
||||||
if (track?.track_id) {
|
if (track?.track_id) {
|
||||||
webapi.library_track(track.track_id).then(({ data }) => {
|
webapi.library_track(track.track_id).then(({ data }) => {
|
||||||
@ -288,44 +288,44 @@ export default {
|
|||||||
this.lyricsStore.$reset()
|
this.lyricsStore.$reset()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update_outputs() {
|
updateOutputs() {
|
||||||
webapi.outputs().then(({ data }) => {
|
webapi.outputs().then(({ data }) => {
|
||||||
this.outputsStore.outputs = data.outputs
|
this.outputsStore.outputs = data.outputs
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_pairing() {
|
updatePairing() {
|
||||||
webapi.pairing().then(({ data }) => {
|
webapi.pairing().then(({ data }) => {
|
||||||
this.remotesStore.$state = data
|
this.remotesStore.$state = data
|
||||||
this.pairing_active = data.active
|
this.pairingActive = data.active
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_player_status() {
|
updatePlayerStatus() {
|
||||||
webapi.player_status().then(({ data }) => {
|
webapi.player_status().then(({ data }) => {
|
||||||
this.playerStore.$state = data
|
this.playerStore.$state = data
|
||||||
this.update_lyrics()
|
this.updateLyrics()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_queue() {
|
updateQueue() {
|
||||||
webapi.queue().then(({ data }) => {
|
webapi.queue().then(({ data }) => {
|
||||||
this.queueStore.$state = data
|
this.queueStore.$state = data
|
||||||
this.update_lyrics()
|
this.updateLyrics()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_settings() {
|
updateSettings() {
|
||||||
webapi.settings().then(({ data }) => {
|
webapi.settings().then(({ data }) => {
|
||||||
this.settingsStore.$state = data
|
this.settingsStore.$state = data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_spotify() {
|
updateSpotify() {
|
||||||
webapi.spotify().then(({ data }) => {
|
webapi.spotify().then(({ data }) => {
|
||||||
this.servicesStore.spotify = data
|
this.servicesStore.spotify = data
|
||||||
if (this.token_timer_id > 0) {
|
if (this.timerId > 0) {
|
||||||
window.clearTimeout(this.token_timer_id)
|
window.clearTimeout(this.timerId)
|
||||||
this.token_timer_id = 0
|
this.timerId = 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.timerId = window.setTimeout(
|
||||||
this.update_spotify,
|
this.updateSpotify,
|
||||||
1000 * data.webapi_token_expires_in
|
1000 * data.webapi_token_expires_in
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.itemId">
|
<list-item
|
||||||
<div v-if="!item.isItem" class="py-5">
|
v-for="item in items"
|
||||||
<span
|
:key="item.itemId"
|
||||||
:id="`index_${item.index}`"
|
:is-item="item.isItem"
|
||||||
class="tag is-small has-text-weight-bold"
|
:image="url(item)"
|
||||||
v-text="item.index"
|
:index="item.index"
|
||||||
|
:lines="[
|
||||||
|
item.item.name,
|
||||||
|
item.item.artist,
|
||||||
|
$filters.toDate(item.item.date_released)
|
||||||
|
]"
|
||||||
|
@open="open(item.item)"
|
||||||
|
@open-details="openDetails(item.item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
|
||||||
@click="open(item.item)"
|
|
||||||
>
|
|
||||||
<control-image
|
|
||||||
v-if="settingsStore.show_cover_artwork_in_album_lists"
|
|
||||||
:url="item.item.artwork_url"
|
|
||||||
:artist="item.item.artist"
|
|
||||||
:album="item.item.name"
|
|
||||||
class="media-left is-small"
|
|
||||||
/>
|
|
||||||
<div class="media-content">
|
|
||||||
<div class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
|
||||||
<div
|
|
||||||
class="is-size-7 has-text-grey has-text-weight-bold"
|
|
||||||
v-text="item.item.artist"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="item.item.date_released && item.item.media_kind === 'music'"
|
|
||||||
class="is-size-7 has-text-grey"
|
|
||||||
v-text="$filters.toDate(item.item.date_released)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item.item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-album
|
<modal-dialog-album
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:media_kind="media_kind"
|
:media_kind="media_kind"
|
||||||
@ -60,16 +34,15 @@
|
|||||||
<br />
|
<br />
|
||||||
</template>
|
</template>
|
||||||
<template #name>
|
<template #name>
|
||||||
<b v-text="rss_playlist_to_remove.name" />
|
<b v-text="playlistToRemove.name" />
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</template>
|
</template>
|
||||||
</modal-dialog>
|
</modal-dialog>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ControlImage from '@/components/ControlImage.vue'
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialog from '@/components/ModalDialog.vue'
|
import ModalDialog from '@/components/ModalDialog.vue'
|
||||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
|
||||||
import { useSettingsStore } from '@/stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
@ -77,7 +50,7 @@ import webapi from '@/webapi'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListAlbums',
|
name: 'ListAlbums',
|
||||||
components: { ControlImage, ModalDialog, ModalDialogAlbum },
|
components: { ListItem, ModalDialog, ModalDialogAlbum },
|
||||||
props: {
|
props: {
|
||||||
items: { required: true, type: Object },
|
items: { required: true, type: Object },
|
||||||
media_kind: { default: '', type: String }
|
media_kind: { default: '', type: String }
|
||||||
@ -88,7 +61,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
rss_playlist_to_remove: {},
|
playlistToRemove: {},
|
||||||
selectedItem: {},
|
selectedItem: {},
|
||||||
showDetailsModal: false,
|
showDetailsModal: false,
|
||||||
showRemovePodcastModal: false
|
showRemovePodcastModal: false
|
||||||
@ -119,7 +92,7 @@ export default {
|
|||||||
this.$router.push({ name: 'music-album', params: { id: item.id } })
|
this.$router.push({ name: 'music-album', params: { id: item.id } })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
},
|
},
|
||||||
@ -128,7 +101,7 @@ export default {
|
|||||||
.library_album_tracks(this.selectedItem.id, { limit: 1 })
|
.library_album_tracks(this.selectedItem.id, { limit: 1 })
|
||||||
.then(({ data: album }) => {
|
.then(({ data: album }) => {
|
||||||
webapi.library_track_playlists(album.items[0].id).then(({ data }) => {
|
webapi.library_track_playlists(album.items[0].id).then(({ data }) => {
|
||||||
;[this.rss_playlist_to_remove] = data.items.filter(
|
;[this.playlistToRemove] = data.items.filter(
|
||||||
(playlist) => playlist.type === 'rss'
|
(playlist) => playlist.type === 'rss'
|
||||||
)
|
)
|
||||||
this.showRemovePodcastModal = true
|
this.showRemovePodcastModal = true
|
||||||
@ -141,11 +114,15 @@ export default {
|
|||||||
},
|
},
|
||||||
removePodcast() {
|
removePodcast() {
|
||||||
this.showRemovePodcastModal = false
|
this.showRemovePodcastModal = false
|
||||||
webapi
|
webapi.library_playlist_delete(this.playlistToRemove.id).then(() => {
|
||||||
.library_playlist_delete(this.rss_playlist_to_remove.id)
|
|
||||||
.then(() => {
|
|
||||||
this.$emit('podcast-deleted')
|
this.$emit('podcast-deleted')
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
url(item) {
|
||||||
|
if (this.settingsStore.show_cover_artwork_in_album_lists) {
|
||||||
|
return item.item.artwork_url
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.id">
|
<list-item
|
||||||
<div
|
v-for="item in items"
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
:key="item.id"
|
||||||
@click="open(item)"
|
:is-item="item.isItem"
|
||||||
>
|
:image="url(item)"
|
||||||
<control-image
|
:index="item.index"
|
||||||
v-if="settingsStore.show_cover_artwork_in_album_lists"
|
:lines="[
|
||||||
:url="item.images?.[0]?.url ?? ''"
|
item.name,
|
||||||
:artist="item.artist"
|
item.artists[0]?.name,
|
||||||
:album="item.name"
|
$filters.toDate(item.release_date)
|
||||||
class="media-left is-small"
|
]"
|
||||||
|
@open="open(item)"
|
||||||
|
@open-details="openDetails(item)"
|
||||||
/>
|
/>
|
||||||
<div class="media-content">
|
|
||||||
<div class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
|
||||||
<div
|
|
||||||
class="is-size-7 has-text-weight-bold has-text-grey"
|
|
||||||
v-text="item.artists[0]?.name"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="is-size-7 has-text-grey"
|
|
||||||
v-text="$filters.toDate(item.release_date)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-album-spotify
|
<modal-dialog-album-spotify
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ControlImage from '@/components/ControlImage.vue'
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogAlbumSpotify from '@/components/ModalDialogAlbumSpotify.vue'
|
import ModalDialogAlbumSpotify from '@/components/ModalDialogAlbumSpotify.vue'
|
||||||
import { useSettingsStore } from '@/stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListAlbumsSpotify',
|
name: 'ListAlbumsSpotify',
|
||||||
components: { ControlImage, ModalDialogAlbumSpotify },
|
components: { ListItem, ModalDialogAlbumSpotify },
|
||||||
props: { items: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
setup() {
|
setup() {
|
||||||
return { settingsStore: useSettingsStore() }
|
return { settingsStore: useSettingsStore() }
|
||||||
@ -60,9 +42,15 @@ export default {
|
|||||||
params: { id: item.id }
|
params: { id: item.id }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
|
url(item) {
|
||||||
|
if (this.settingsStore.show_cover_artwork_in_album_lists) {
|
||||||
|
return item.images?.[0]?.url ?? ''
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.itemId">
|
<list-item
|
||||||
<div v-if="!item.isItem" class="py-5">
|
v-for="item in items"
|
||||||
<span
|
:key="item.itemId"
|
||||||
:id="`index_${item.index}`"
|
:is-item="item.isItem"
|
||||||
class="tag is-small has-text-weight-bold"
|
:index="item.index"
|
||||||
v-text="item.index"
|
:lines="[item.item.name]"
|
||||||
|
@open="open(item.item)"
|
||||||
|
@open-details="openDetails(item.item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
|
||||||
@click="open(item.item)"
|
|
||||||
>
|
|
||||||
<div class="media-content">
|
|
||||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item.item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-artist
|
<modal-dialog-artist
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListArtists',
|
name: 'ListArtists',
|
||||||
components: { ModalDialogArtist },
|
components: { ListItem, ModalDialogArtist },
|
||||||
props: { items: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { selectedItem: {}, showDetailsModal: false }
|
return { selectedItem: {}, showDetailsModal: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open(item) {
|
open(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
@ -50,7 +33,7 @@ export default {
|
|||||||
item.media_kind === 'audiobook' ? 'audiobooks-artist' : 'music-artist'
|
item.media_kind === 'audiobook' ? 'audiobooks-artist' : 'music-artist'
|
||||||
this.$router.push({ name: route, params: { id: item.id } })
|
this.$router.push({ name: route, params: { id: item.id } })
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.id">
|
<list-item
|
||||||
<div class="media is-align-items-center is-clickable mb-0">
|
v-for="item in items"
|
||||||
<div class="media-content" @click="open(item)">
|
:key="item.id"
|
||||||
<p class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
:is-item="true"
|
||||||
</div>
|
:index="item.index"
|
||||||
<div class="media-right">
|
:lines="[item.name]"
|
||||||
<a @click.prevent.stop="openDialog(item)">
|
@open="open(item)"
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
@open-details="openDetails(item)"
|
||||||
</a>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-artist-spotify
|
<modal-dialog-artist-spotify
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
|
import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListArtistsSpotify',
|
name: 'ListArtistsSpotify',
|
||||||
components: { ModalDialogArtistSpotify },
|
components: { ListItem, ModalDialogArtistSpotify },
|
||||||
props: { items: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@ -38,7 +34,7 @@ export default {
|
|||||||
params: { id: item.id }
|
params: { id: item.id }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.itemId">
|
<list-item
|
||||||
<div v-if="!item.isItem" class="py-5">
|
v-for="item in items"
|
||||||
<div class="media-content">
|
:key="item.itemId"
|
||||||
<span
|
:is-item="item.isItem"
|
||||||
:id="`index_${item.index}`"
|
:index="item.index"
|
||||||
class="tag is-small has-text-weight-bold"
|
:lines="[item.item.name]"
|
||||||
v-text="item.index"
|
@open="open(item.item)"
|
||||||
|
@open-details="openDetails(item.item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
|
||||||
@click="open(item.item)"
|
|
||||||
>
|
|
||||||
<div class="media-content">
|
|
||||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item.item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-composer
|
<modal-dialog-composer
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListComposers',
|
name: 'ListComposers',
|
||||||
components: { ModalDialogComposer },
|
components: { ListItem, ModalDialogComposer },
|
||||||
props: { items: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { selectedItem: {}, showDetailsModal: false }
|
return { selectedItem: {}, showDetailsModal: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open(item) {
|
open(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
@ -53,8 +34,7 @@ export default {
|
|||||||
params: { name: item.name }
|
params: { name: item.name }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails(item) {
|
||||||
openDialog(item) {
|
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
|
@ -27,22 +27,20 @@
|
|||||||
>
|
>
|
||||||
<mdicon class="media-left icon" name="folder" />
|
<mdicon class="media-left icon" name="folder" />
|
||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<p class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
<div class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click.prevent.stop="openDialog(item)">
|
<a @click.prevent.stop="openDetails(item)">
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-directory
|
<modal-dialog-directory
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -80,7 +78,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$router.push(route)
|
this.$router.push(route)
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item.path
|
this.selectedItem = item.path
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
},
|
},
|
||||||
|
@ -1,45 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.itemId">
|
<list-item
|
||||||
<div v-if="!item.isItem" class="py-5">
|
v-for="item in items"
|
||||||
<div class="media-content">
|
:key="item.itemId"
|
||||||
<span
|
:is-item="item.isItem"
|
||||||
:id="`index_${item.index}`"
|
:index="item.index"
|
||||||
class="tag is-small has-text-weight-bold"
|
:lines="[item.item.name]"
|
||||||
v-text="item.index"
|
@open="open(item.item)"
|
||||||
|
@open-details="openDetails(item.item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
|
||||||
@click="open(item.item)"
|
|
||||||
>
|
|
||||||
<div class="media-content">
|
|
||||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item.item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-genre
|
<modal-dialog-genre
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:media_kind="media_kind"
|
:media_kind="media_kind"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListGenres',
|
name: 'ListGenres',
|
||||||
components: { ModalDialogGenre },
|
components: { ListItem, ModalDialogGenre },
|
||||||
props: {
|
props: {
|
||||||
items: { required: true, type: Object },
|
items: { required: true, type: Object },
|
||||||
media_kind: { required: true, type: String }
|
media_kind: { required: true, type: String }
|
||||||
@ -55,7 +38,7 @@ export default {
|
|||||||
query: { media_kind: this.media_kind }
|
query: { media_kind: this.media_kind }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
|
80
web-src/src/components/ListItem.vue
Normal file
80
web-src/src/components/ListItem.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="!isItem" class="py-5">
|
||||||
|
<div class="media-content">
|
||||||
|
<span
|
||||||
|
:id="`index_${index}`"
|
||||||
|
class="tag is-small has-text-weight-bold"
|
||||||
|
v-text="index"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="media is-align-items-center is-clickable mb-0"
|
||||||
|
:class="{ 'with-progress': progress }"
|
||||||
|
@click="open"
|
||||||
|
>
|
||||||
|
<mdicon v-if="icon" class="media-left icon" :name="icon" />
|
||||||
|
<control-image v-if="image" :url="image" class="media-left is-small" />
|
||||||
|
<div class="media-content">
|
||||||
|
<div
|
||||||
|
v-for="(line, position) in lines"
|
||||||
|
:key="position"
|
||||||
|
:class="{
|
||||||
|
'is-size-6': position === 0,
|
||||||
|
'is-size-7': position !== 0,
|
||||||
|
'has-text-weight-bold': position !== 2,
|
||||||
|
'has-text-grey': position !== 0
|
||||||
|
}"
|
||||||
|
v-text="line"
|
||||||
|
/>
|
||||||
|
<progress
|
||||||
|
v-if="progress"
|
||||||
|
class="progress is-dark"
|
||||||
|
max="1"
|
||||||
|
:value="progress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="media-right">
|
||||||
|
<a @click.prevent.stop="openDetails">
|
||||||
|
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ControlImage from '@/components/ControlImage.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ListItem',
|
||||||
|
components: { ControlImage },
|
||||||
|
props: {
|
||||||
|
icon: { default: null, type: String },
|
||||||
|
image: { default: null, type: String },
|
||||||
|
index: { default: null, type: [String, Number] },
|
||||||
|
isItem: { default: true, type: Boolean },
|
||||||
|
lines: { default: null, type: Array },
|
||||||
|
progress: { default: null, type: Number }
|
||||||
|
},
|
||||||
|
emits: ['open', 'openDetails'],
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.$emit('open')
|
||||||
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.$emit('openDetails')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.progress {
|
||||||
|
height: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media.with-progress {
|
||||||
|
margin-top: 0.375rem;
|
||||||
|
}
|
||||||
|
</style>
|
@ -52,7 +52,7 @@ import webapi from '@/webapi'
|
|||||||
export default {
|
export default {
|
||||||
name: 'ListItemQueueItem',
|
name: 'ListItemQueueItem',
|
||||||
props: {
|
props: {
|
||||||
current_position: { required: true, type: Number },
|
currentPosition: { required: true, type: Number },
|
||||||
editing: Boolean,
|
editing: Boolean,
|
||||||
item: { required: true, type: Object },
|
item: { required: true, type: Object },
|
||||||
position: { required: true, type: Number },
|
position: { required: true, type: Number },
|
||||||
@ -66,7 +66,7 @@ export default {
|
|||||||
return this.item.id === this.playerStore.item_id
|
return this.item.id === this.playerStore.item_id
|
||||||
},
|
},
|
||||||
isNext() {
|
isNext() {
|
||||||
return this.current_position < 0 || this.position >= this.current_position
|
return this.currentPosition < 0 || this.position >= this.currentPosition
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1,41 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.itemId">
|
<list-item
|
||||||
<div
|
v-for="item in items"
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
:key="item.itemId"
|
||||||
@click="open(item.item)"
|
:icon="icon(item.item)"
|
||||||
>
|
:is-item="item.isItem"
|
||||||
<mdicon class="media-left icon" :name="icon(item.item)" />
|
:index="item.index"
|
||||||
<div class="media-content">
|
:lines="[item.item.name]"
|
||||||
<p class="is-size-6 has-text-weight-bold" v-text="item.item.name" />
|
@open="open(item.item)"
|
||||||
</div>
|
@open-details="openDetails(item.item)"
|
||||||
<div class="media-right">
|
/>
|
||||||
<a @click.prevent.stop="openDialog(item.item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-playlist
|
<modal-dialog-playlist
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
|
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListPlaylists',
|
name: 'ListPlaylists',
|
||||||
components: { ModalDialogPlaylist },
|
components: { ListItem, ModalDialogPlaylist },
|
||||||
props: { items: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { selectedItem: {}, showDetailsModal: false }
|
return { selectedItem: {}, showDetailsModal: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
icon(item) {
|
icon(item) {
|
||||||
if (item.type === 'folder') {
|
if (item.type === 'folder') {
|
||||||
@ -52,7 +43,7 @@ export default {
|
|||||||
this.$router.push({ name: 'playlist', params: { id: item.id } })
|
this.$router.push({ name: 'playlist', params: { id: item.id } })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.id">
|
<list-item
|
||||||
<div
|
v-for="item in items"
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
:key="item.id"
|
||||||
@click="open(item)"
|
:is-item="item.isItem"
|
||||||
>
|
:index="item.index"
|
||||||
<div class="media-content">
|
:lines="[item.name, item.owner.display_name]"
|
||||||
<div class="is-size-6 has-text-weight-bold" v-text="item.name" />
|
@open="open(item)"
|
||||||
<div
|
@open-details="openDetails(item)"
|
||||||
class="is-size-7 has-text-weight-bold has-text-grey"
|
|
||||||
v-text="item.owner.display_name"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-playlist-spotify
|
<modal-dialog-playlist-spotify
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogPlaylistSpotify from '@/components/ModalDialogPlaylistSpotify.vue'
|
import ModalDialogPlaylistSpotify from '@/components/ModalDialogPlaylistSpotify.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListPlaylistsSpotify',
|
name: 'ListPlaylistsSpotify',
|
||||||
components: {
|
components: { ListItem, ModalDialogPlaylistSpotify },
|
||||||
ModalDialogPlaylistSpotify
|
|
||||||
},
|
|
||||||
props: { items: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@ -45,7 +32,7 @@ export default {
|
|||||||
open(item) {
|
open(item) {
|
||||||
this.$router.push({ name: 'playlist-spotify', params: { id: item.id } })
|
this.$router.push({ name: 'playlist-spotify', params: { id: item.id } })
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="item in items" :key="item.itemId">
|
<list-item
|
||||||
<div v-if="!item.isItem" class="py-5">
|
v-for="item in items"
|
||||||
<span
|
:key="item.itemId"
|
||||||
:id="`index_${item.index}`"
|
:icon="icon"
|
||||||
class="tag is-small has-text-weight-bold"
|
:is-item="item.isItem"
|
||||||
v-text="item.index"
|
:index="item.index"
|
||||||
|
:lines="[item.item.title, item.item.artist, item.item.album]"
|
||||||
|
:progress="progress(item.item)"
|
||||||
|
@open="open(item.item)"
|
||||||
|
@open-details="openDetails(item.item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="media is-align-items-center is-clickable mb-0"
|
|
||||||
:class="{ 'with-progress': showProgress }"
|
|
||||||
@click="play(item.item)"
|
|
||||||
>
|
|
||||||
<mdicon
|
|
||||||
v-if="showIcon"
|
|
||||||
class="media-left icon"
|
|
||||||
name="file-music-outline"
|
|
||||||
/>
|
|
||||||
<div class="media-content">
|
|
||||||
<div
|
|
||||||
class="is-size-6 has-text-weight-bold"
|
|
||||||
:class="{
|
|
||||||
'has-text-grey':
|
|
||||||
item.item.media_kind === 'podcast' && item.item.play_count > 0
|
|
||||||
}"
|
|
||||||
v-text="item.item.title"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="is-size-7 has-text-weight-bold has-text-grey"
|
|
||||||
v-text="item.item.artist"
|
|
||||||
/>
|
|
||||||
<div class="is-size-7 has-text-grey" v-text="item.item.album" />
|
|
||||||
<progress
|
|
||||||
v-if="showProgress && item.item.seek_ms > 0"
|
|
||||||
class="progress is-dark"
|
|
||||||
:max="item.item.length_ms"
|
|
||||||
:value="item.item.seek_ms"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="openDialog(item.item)">
|
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-track
|
<modal-dialog-track
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
@play-count-changed="$emit('play-count-changed')"
|
@play-count-changed="$emit('play-count-changed')"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ListItem from '@/components/ListItem.vue'
|
||||||
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListTracks',
|
name: 'ListTracks',
|
||||||
components: { ModalDialogTrack },
|
components: { ListItem, ModalDialogTrack },
|
||||||
props: {
|
props: {
|
||||||
expression: { default: '', type: String },
|
expression: { default: '', type: String },
|
||||||
items: { required: true, type: Object },
|
items: { default: null, type: Object },
|
||||||
showIcon: Boolean,
|
icon: { default: null, type: String },
|
||||||
showProgress: Boolean,
|
showProgress: { default: false, type: Boolean },
|
||||||
uris: { default: '', type: String }
|
uris: { default: '', type: String }
|
||||||
},
|
},
|
||||||
emits: ['play-count-changed'],
|
emits: ['play-count-changed'],
|
||||||
@ -75,11 +38,7 @@ export default {
|
|||||||
return { selectedItem: {}, showDetailsModal: false }
|
return { selectedItem: {}, showDetailsModal: false }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openDialog(item) {
|
open(item) {
|
||||||
this.selectedItem = item
|
|
||||||
this.showDetailsModal = true
|
|
||||||
},
|
|
||||||
play(item) {
|
|
||||||
if (this.uris) {
|
if (this.uris) {
|
||||||
webapi.player_play_uri(this.uris, false, this.items.items.indexOf(item))
|
webapi.player_play_uri(this.uris, false, this.items.items.indexOf(item))
|
||||||
} else if (this.expression) {
|
} else if (this.expression) {
|
||||||
@ -91,17 +50,20 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
webapi.player_play_uri(item.uri, false)
|
webapi.player_play_uri(item.uri, false)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
openDetails(item) {
|
||||||
|
this.selectedItem = item
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
|
// 'has-text-grey': item.item.media_kind === 'podcast' && item.item.play_count > 0
|
||||||
|
progress(item) {
|
||||||
|
if (item.item) {
|
||||||
|
if (this.showProgress && item.item.seek_ms > 0) {
|
||||||
|
return item.item.seek_ms / item.item.length_ms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.progress {
|
|
||||||
height: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media.with-progress {
|
|
||||||
margin-top: 0.375rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -36,19 +36,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<a @click.prevent.stop="openDialog(item)">
|
<a @click.prevent.stop="openDetails(item)">
|
||||||
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
<mdicon class="icon has-text-grey" name="dots-vertical" size="16" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<teleport to="#app">
|
|
||||||
<modal-dialog-track-spotify
|
<modal-dialog-track-spotify
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@close="showDetailsModal = false"
|
@close="showDetailsModal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -66,7 +64,7 @@ export default {
|
|||||||
return { selectedItem: {}, showDetailsModal: false }
|
return { selectedItem: {}, showDetailsModal: false }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
},
|
},
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
class="lyrics"
|
class="lyrics"
|
||||||
@touchstart="autoScrolling = false"
|
@touchstart="autoScrolling = false"
|
||||||
@touchend="autoScrolling = true"
|
@touchend="autoScrolling = true"
|
||||||
@scroll.passive="start_scrolling"
|
@scroll.passive="startScrolling"
|
||||||
@wheel.passive="start_scrolling"
|
@wheel.passive="startScrolling"
|
||||||
>
|
>
|
||||||
<template v-for="(verse, index) in lyrics" :key="index">
|
<template v-for="(verse, index) in lyrics" :key="index">
|
||||||
<div
|
<div
|
||||||
v-if="index === verse_index"
|
v-if="index === verseIndex"
|
||||||
:class="{ 'is-highlighted': playerStore.isPlaying }"
|
:class="{ 'is-highlighted': playerStore.isPlaying }"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -85,7 +85,7 @@ export default {
|
|||||||
}
|
}
|
||||||
return parsed
|
return parsed
|
||||||
},
|
},
|
||||||
verse_index() {
|
verseIndex() {
|
||||||
if (this.lyrics.length && this.lyrics[0].time) {
|
if (this.lyrics.length && this.lyrics[0].time) {
|
||||||
const currentTime = this.playerStore.item_progress_ms / 1000,
|
const currentTime = this.playerStore.item_progress_ms / 1000,
|
||||||
la = this.lyrics,
|
la = this.lyrics,
|
||||||
@ -96,7 +96,7 @@ export default {
|
|||||||
la[this.lastIndex].time > currentTime
|
la[this.lastIndex].time > currentTime
|
||||||
// Reset the cache when the track has changed or has been seeked
|
// Reset the cache when the track has changed or has been seeked
|
||||||
if (trackChanged || trackSeeked) {
|
if (trackChanged || trackSeeked) {
|
||||||
this.reset_scrolling()
|
this.resetScrolling()
|
||||||
}
|
}
|
||||||
// Check the next two items and the last one before searching
|
// Check the next two items and the last one before searching
|
||||||
if (
|
if (
|
||||||
@ -134,20 +134,20 @@ export default {
|
|||||||
}
|
}
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
this.reset_scrolling()
|
this.resetScrolling()
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
verse_index() {
|
verseIndex() {
|
||||||
if (this.autoScrolling) {
|
if (this.autoScrolling) {
|
||||||
this.scroll_to_verse()
|
this.scrollToVerse()
|
||||||
}
|
}
|
||||||
this.lastIndex = this.verse_index
|
this.lastIndex = this.verseIndex
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reset_scrolling() {
|
resetScrolling() {
|
||||||
// Scroll to the start of the lyrics in all cases
|
// Scroll to the start of the lyrics in all cases
|
||||||
if (this.playerStore.item_id !== this.lastItemId && this.$refs.lyrics) {
|
if (this.playerStore.item_id !== this.lastItemId && this.$refs.lyrics) {
|
||||||
this.$refs.lyrics.scrollTo(0, 0)
|
this.$refs.lyrics.scrollTo(0, 0)
|
||||||
@ -155,13 +155,13 @@ export default {
|
|||||||
this.lastItemId = this.playerStore.item_id
|
this.lastItemId = this.playerStore.item_id
|
||||||
this.lastIndex = -1
|
this.lastIndex = -1
|
||||||
},
|
},
|
||||||
scroll_to_verse() {
|
scrollToVerse() {
|
||||||
const pane = this.$refs.lyrics
|
const pane = this.$refs.lyrics
|
||||||
if (this.verse_index === -1) {
|
if (this.verseIndex === -1) {
|
||||||
pane.scrollTo(0, 0)
|
pane.scrollTo(0, 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const currentVerse = pane.children[this.verse_index]
|
const currentVerse = pane.children[this.verseIndex]
|
||||||
pane.scrollBy({
|
pane.scrollBy({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
left: 0,
|
left: 0,
|
||||||
@ -172,7 +172,7 @@ export default {
|
|||||||
pane.scrollTop
|
pane.scrollTop
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
start_scrolling(event) {
|
startScrolling(event) {
|
||||||
// Consider only user events
|
// Consider only user events
|
||||||
if (event.screenX ?? event.screenY) {
|
if (event.screenX ?? event.screenY) {
|
||||||
this.autoScrolling = false
|
this.autoScrolling = false
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
<p class="control has-icons-left">
|
<p class="control has-icons-left">
|
||||||
<input
|
<input
|
||||||
ref="playlist_name_field"
|
ref="playlist_name_field"
|
||||||
v-model="playlist_name"
|
v-model="playlistName"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
pattern=".+"
|
pattern=".+"
|
||||||
required
|
required
|
||||||
:placeholder="$t('dialog.playlist.save.playlist-name')"
|
:placeholder="$t('dialog.playlist.save.playlist-name')"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
@input="check_name"
|
@input="check"
|
||||||
/>
|
/>
|
||||||
<mdicon class="icon is-left" name="playlist-music" size="16" />
|
<mdicon class="icon is-left" name="playlist-music" size="16" />
|
||||||
</p>
|
</p>
|
||||||
@ -41,7 +41,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
playlist_name: ''
|
playlistName: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -75,17 +75,17 @@ export default {
|
|||||||
cancel() {
|
cancel() {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
},
|
},
|
||||||
check_name(event) {
|
check(event) {
|
||||||
const { validity } = event.target
|
const { validity } = event.target
|
||||||
this.disabled = validity.patternMismatch || validity.valueMissing
|
this.disabled = validity.patternMismatch || validity.valueMissing
|
||||||
},
|
},
|
||||||
save() {
|
save() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
webapi
|
webapi
|
||||||
.queue_save_playlist(this.playlist_name)
|
.queue_save_playlist(this.playlistName)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
this.playlist_name = ''
|
this.playlistName = ''
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
@ -23,7 +23,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
spotify_track: {}
|
spotifyTrack: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -89,10 +89,10 @@ export default {
|
|||||||
spotifyApi
|
spotifyApi
|
||||||
.getTrack(this.item.path.slice(this.item.path.lastIndexOf(':') + 1))
|
.getTrack(this.item.path.slice(this.item.path.lastIndexOf(':') + 1))
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.spotify_track = response
|
this.spotifyTrack = response
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.spotify_track = {}
|
this.spotifyTrack = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -102,7 +102,7 @@ export default {
|
|||||||
if (this.item.data_kind === 'spotify') {
|
if (this.item.data_kind === 'spotify') {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'music-spotify-album',
|
name: 'music-spotify-album',
|
||||||
params: { id: this.spotify_track.album.id }
|
params: { id: this.spotifyTrack.album.id }
|
||||||
})
|
})
|
||||||
} else if (this.item.media_kind === 'podcast') {
|
} else if (this.item.media_kind === 'podcast') {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
@ -126,7 +126,7 @@ export default {
|
|||||||
if (this.item.data_kind === 'spotify') {
|
if (this.item.data_kind === 'spotify') {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'music-spotify-artist',
|
name: 'music-spotify-artist',
|
||||||
params: { id: this.spotify_track.artists[0].id }
|
params: { id: this.spotifyTrack.artists[0].id }
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
this.item.media_kind === 'music' ||
|
this.item.media_kind === 'music' ||
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<control-switch v-model="rescan_metadata">
|
<control-switch v-model="rescanMetadata">
|
||||||
<template #label>
|
<template #label>
|
||||||
<span v-text="$t('dialog.update.rescan-metadata')" />
|
<span v-text="$t('dialog.update.rescan-metadata')" />
|
||||||
</template>
|
</template>
|
||||||
@ -64,7 +64,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
rescan_metadata: false
|
rescanMetadata: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -87,7 +87,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
analyse() {
|
analyse() {
|
||||||
if (this.rescan_metadata) {
|
if (this.rescanMetadata) {
|
||||||
webapi.library_rescan(this.libraryStore.update_dialog_scan_kind)
|
webapi.library_rescan(this.libraryStore.update_dialog_scan_kind)
|
||||||
} else {
|
} else {
|
||||||
webapi.library_update(this.libraryStore.update_dialog_scan_kind)
|
webapi.library_update(this.libraryStore.update_dialog_scan_kind)
|
||||||
|
@ -107,6 +107,12 @@ export default {
|
|||||||
show: true,
|
show: true,
|
||||||
sub: true
|
sub: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'navigation.composers',
|
||||||
|
name: 'music-composers',
|
||||||
|
show: true,
|
||||||
|
sub: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'navigation.spotify',
|
key: 'navigation.spotify',
|
||||||
name: 'music-spotify',
|
name: 'music-spotify',
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<section v-if="notifications.length > 0" class="notifications">
|
<section v-if="!notificationsStore.isEmpty" class="notifications">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-half">
|
<div class="column is-half">
|
||||||
<div
|
<div
|
||||||
v-for="notification in notifications"
|
v-for="notification in notificationsStore.list"
|
||||||
:key="notification.id"
|
:key="notification.id"
|
||||||
class="notification"
|
class="notification"
|
||||||
:class="notification.type ? `is-${notification.type}` : ''"
|
:class="notification.type ? `is-${notification.type}` : ''"
|
||||||
>
|
>
|
||||||
<button class="delete" @click="remove(notification)" />
|
<button
|
||||||
|
class="delete"
|
||||||
|
@click="notificationsStore.remove(notification)"
|
||||||
|
/>
|
||||||
<div class="text" v-text="notification.text" />
|
<div class="text" v-text="notification.text" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -21,21 +24,8 @@ import { useNotificationsStore } from '@/stores/notifications'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NotificationList',
|
name: 'NotificationList',
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
return { notificationsStore: useNotificationsStore() }
|
return { notificationsStore: useNotificationsStore() }
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
notifications() {
|
|
||||||
return this.notificationsStore.list
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
remove(notification) {
|
|
||||||
this.notificationsStore.remove(notification)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"send": "Senden",
|
"send": "Senden",
|
||||||
"show-more": "Zeige mehr",
|
"show-more": "Zeige mehr",
|
||||||
"shuffle": "Zufallswiedergabe"
|
"shuffle": "Zufallswiedergabe",
|
||||||
|
"update": "Neu einlesen"
|
||||||
},
|
},
|
||||||
"count": {
|
"count": {
|
||||||
"albums": "{count} Album|{count} Album|{count} Alben",
|
"albums": "{count} Album|{count} Album|{count} Alben",
|
||||||
@ -116,6 +117,7 @@
|
|||||||
"albums": "Alben",
|
"albums": "Alben",
|
||||||
"artists": "Künstler",
|
"artists": "Künstler",
|
||||||
"audiobooks": "Hörbücher",
|
"audiobooks": "Hörbücher",
|
||||||
|
"composers": "Komponisten",
|
||||||
"now-playing": " - {album}",
|
"now-playing": " - {album}",
|
||||||
"stream-error": "HTTP-stream-Fehler: Stream kann nicht geladen werden oder wurde wg. Netzwerkfehler gestopt",
|
"stream-error": "HTTP-stream-Fehler: Stream kann nicht geladen werden oder wurde wg. Netzwerkfehler gestopt",
|
||||||
"stream": "HTTP-stream",
|
"stream": "HTTP-stream",
|
||||||
@ -216,8 +218,7 @@
|
|||||||
},
|
},
|
||||||
"podcasts": {
|
"podcasts": {
|
||||||
"new-episodes": "Neue Episoden",
|
"new-episodes": "Neue Episoden",
|
||||||
"title": "Podcasts",
|
"title": "Podcasts"
|
||||||
"update": "Neu einlesen"
|
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"title": "Warteschlange"
|
"title": "Warteschlange"
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"save": "Save",
|
"save": "Save",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"show-more": "Show more",
|
"show-more": "Show more",
|
||||||
"shuffle": "Shuffle"
|
"shuffle": "Shuffle",
|
||||||
|
"update": "Update"
|
||||||
},
|
},
|
||||||
"count": {
|
"count": {
|
||||||
"albums": "{count} album|{count} album|{count} albums",
|
"albums": "{count} album|{count} album|{count} albums",
|
||||||
@ -116,6 +117,7 @@
|
|||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"artists": "Artists",
|
"artists": "Artists",
|
||||||
"audiobooks": "Audiobooks",
|
"audiobooks": "Audiobooks",
|
||||||
|
"composers": "Composers",
|
||||||
"now-playing": " - {album}",
|
"now-playing": " - {album}",
|
||||||
"stream-error": "HTTP stream error: failed to load stream or stopped loading due to network problem",
|
"stream-error": "HTTP stream error: failed to load stream or stopped loading due to network problem",
|
||||||
"stream": "HTTP stream",
|
"stream": "HTTP stream",
|
||||||
@ -216,8 +218,7 @@
|
|||||||
},
|
},
|
||||||
"podcasts": {
|
"podcasts": {
|
||||||
"new-episodes": "New Episodes",
|
"new-episodes": "New Episodes",
|
||||||
"title": "Podcasts",
|
"title": "Podcasts"
|
||||||
"update": "Update"
|
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"title": "Queue"
|
"title": "Queue"
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"save": "Enregistrer",
|
"save": "Enregistrer",
|
||||||
"send": "Envoyer",
|
"send": "Envoyer",
|
||||||
"show-more": "Afficher plus",
|
"show-more": "Afficher plus",
|
||||||
"shuffle": "Lecture aléatoire"
|
"shuffle": "Lecture aléatoire",
|
||||||
|
"update": "Actualiser"
|
||||||
},
|
},
|
||||||
"count": {
|
"count": {
|
||||||
"albums": "{count} album|{count} album|{count} albums",
|
"albums": "{count} album|{count} album|{count} albums",
|
||||||
@ -116,6 +117,7 @@
|
|||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"artists": "Artistes",
|
"artists": "Artistes",
|
||||||
"audiobooks": "Livres audio",
|
"audiobooks": "Livres audio",
|
||||||
|
"composers": "Compositeurs",
|
||||||
"now-playing": " - {album}",
|
"now-playing": " - {album}",
|
||||||
"stream-error": "Erreur du flux HTTP : échec du chargement du flux ou arrêt du chargement en raison d’un problème réseau",
|
"stream-error": "Erreur du flux HTTP : échec du chargement du flux ou arrêt du chargement en raison d’un problème réseau",
|
||||||
"stream": "Flux HTTP",
|
"stream": "Flux HTTP",
|
||||||
@ -216,8 +218,7 @@
|
|||||||
},
|
},
|
||||||
"podcasts": {
|
"podcasts": {
|
||||||
"new-episodes": "Nouveaux épisodes",
|
"new-episodes": "Nouveaux épisodes",
|
||||||
"title": "Podcasts",
|
"title": "Podcasts"
|
||||||
"update": "Actualiser"
|
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"title": "File d’attente"
|
"title": "File d’attente"
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"save": "保存",
|
"save": "保存",
|
||||||
"send": "发送",
|
"send": "发送",
|
||||||
"show-more": "显示更多",
|
"show-more": "显示更多",
|
||||||
"shuffle": "随机播放"
|
"shuffle": "随机播放",
|
||||||
|
"update": "更新"
|
||||||
},
|
},
|
||||||
"count": {
|
"count": {
|
||||||
"albums": "{count} 张专辑|{count} 张专辑",
|
"albums": "{count} 张专辑|{count} 张专辑",
|
||||||
@ -116,6 +117,7 @@
|
|||||||
"albums": "专辑",
|
"albums": "专辑",
|
||||||
"artists": "艺人",
|
"artists": "艺人",
|
||||||
"audiobooks": "有声读物",
|
"audiobooks": "有声读物",
|
||||||
|
"composers": "作曲家",
|
||||||
"now-playing": " - {album}",
|
"now-playing": " - {album}",
|
||||||
"stream-error": "HTTP流错误:流载入失败或者由于网络原因无法载入",
|
"stream-error": "HTTP流错误:流载入失败或者由于网络原因无法载入",
|
||||||
"stream": "HTTP流",
|
"stream": "HTTP流",
|
||||||
@ -216,8 +218,7 @@
|
|||||||
},
|
},
|
||||||
"podcasts": {
|
"podcasts": {
|
||||||
"new-episodes": "最新单集",
|
"new-episodes": "最新单集",
|
||||||
"title": "播客",
|
"title": "播客"
|
||||||
"update": "更新"
|
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"title": "清单"
|
"title": "清单"
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
"save": "儲存",
|
"save": "儲存",
|
||||||
"send": "發送",
|
"send": "發送",
|
||||||
"show-more": "顯示更多",
|
"show-more": "顯示更多",
|
||||||
"shuffle": "隨機播放"
|
"shuffle": "隨機播放",
|
||||||
|
"update": "更新"
|
||||||
},
|
},
|
||||||
"count": {
|
"count": {
|
||||||
"albums": "{count} 張專輯|{count} 張專輯",
|
"albums": "{count} 張專輯|{count} 張專輯",
|
||||||
@ -116,6 +117,7 @@
|
|||||||
"albums": "專輯",
|
"albums": "專輯",
|
||||||
"artists": "藝人",
|
"artists": "藝人",
|
||||||
"audiobooks": "有聲書",
|
"audiobooks": "有聲書",
|
||||||
|
"composers": "作曲家",
|
||||||
"now-playing": " - {album}",
|
"now-playing": " - {album}",
|
||||||
"stream-error": "HTTP串流錯誤:串流載入失敗或者由於網絡原因無法載入",
|
"stream-error": "HTTP串流錯誤:串流載入失敗或者由於網絡原因無法載入",
|
||||||
"stream": "HTTP串流",
|
"stream": "HTTP串流",
|
||||||
@ -216,8 +218,7 @@
|
|||||||
},
|
},
|
||||||
"podcasts": {
|
"podcasts": {
|
||||||
"new-episodes": "最新單集",
|
"new-episodes": "最新單集",
|
||||||
"title": "Podcast",
|
"title": "Podcast"
|
||||||
"update": "更新"
|
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"title": "清單"
|
"title": "清單"
|
||||||
|
@ -34,8 +34,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<div
|
<div
|
||||||
class="is-size-7 mt-6"
|
class="is-size-7 mt-6"
|
||||||
v-text="
|
v-text="
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
class="is-clickable is-medium"
|
class="is-clickable is-medium"
|
||||||
@click="showDetails"
|
@click="openDetails"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
@ -98,11 +98,11 @@ export default {
|
|||||||
params: { id: this.album.artist_id }
|
params: { id: this.album.artist_id }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_uri(this.album.uri, true)
|
webapi.player_play_uri(this.album.uri, true)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
:artist="album.artists[0].name"
|
:artist="album.artists[0].name"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
class="is-clickable is-medium"
|
class="is-clickable is-medium"
|
||||||
@click="showDetails"
|
@click="openDetails"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
@ -102,12 +102,12 @@ export default {
|
|||||||
params: { id: this.album.artists[0].id }
|
params: { id: this.album.artists[0].id }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
this.showDetailsModal = false
|
this.showDetailsModal = false
|
||||||
webapi.player_play_uri(this.album.uri, true)
|
webapi.player_play_uri(this.album.uri, true)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
v-text="$t('options.sort.title')"
|
v-text="$t('options.sort.title')"
|
||||||
/>
|
/>
|
||||||
<control-dropdown
|
<control-dropdown
|
||||||
v-model:value="uiStore.albums_sort"
|
v-model:value="uiStore.albumsSort"
|
||||||
:options="groupings"
|
:options="groupings"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -101,7 +101,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
albums() {
|
albums() {
|
||||||
const { options } = this.groupings.find(
|
const { options } = this.groupings.find(
|
||||||
(grouping) => grouping.id === this.uiStore.albums_sort
|
(grouping) => grouping.id === this.uiStore.albumsSort
|
||||||
)
|
)
|
||||||
options.filters = [
|
options.filters = [
|
||||||
(album) => !this.uiStore.hideSingles || album.track_count > 2,
|
(album) => !this.uiStore.hideSingles || album.track_count > 2,
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
v-text="$t('page.artist.sort.title')"
|
v-text="$t('page.artist.sort.title')"
|
||||||
/>
|
/>
|
||||||
<control-dropdown
|
<control-dropdown
|
||||||
v-model:value="uiStore.artist_albums_sort"
|
v-model:value="uiStore.artistAlbumsSort"
|
||||||
:options="groupings"
|
:options="groupings"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
@ -110,7 +110,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
albums() {
|
albums() {
|
||||||
const { options } = this.groupings.find(
|
const { options } = this.groupings.find(
|
||||||
(grouping) => grouping.id === this.uiStore.artist_albums_sort
|
(grouping) => grouping.id === this.uiStore.artistAlbumsSort
|
||||||
)
|
)
|
||||||
options.filters = [
|
options.filters = [
|
||||||
(album) => !this.uiStore.hideSpotify || album.data_kind !== 'spotify'
|
(album) => !this.uiStore.hideSpotify || album.data_kind !== 'spotify'
|
||||||
@ -153,6 +153,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
openTracks() {
|
openTracks() {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'music-artist-tracks',
|
name: 'music-artist-tracks',
|
||||||
@ -164,9 +167,6 @@ export default {
|
|||||||
this.albums.items.map((item) => item.uri).join(),
|
this.albums.items.map((item) => item.uri).join(),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
@ -106,7 +106,7 @@ export default {
|
|||||||
heading() {
|
heading() {
|
||||||
return {
|
return {
|
||||||
subtitle: [{ count: this.total, key: 'count.albums' }],
|
subtitle: [{ count: this.total, key: 'count.albums' }],
|
||||||
title: this.$t('artist.name')
|
title: this.artist.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -130,12 +130,12 @@ export default {
|
|||||||
loaded(data.items.length, PAGE_SIZE)
|
loaded(data.items.length, PAGE_SIZE)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
this.showDetailsModal = false
|
this.showDetailsModal = false
|
||||||
webapi.player_play_uri(this.artist.uri, true)
|
webapi.player_play_uri(this.artist.uri, true)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
v-text="$t('options.sort.title')"
|
v-text="$t('options.sort.title')"
|
||||||
/>
|
/>
|
||||||
<control-dropdown
|
<control-dropdown
|
||||||
v-model:value="uiStore.artist_tracks_sort"
|
v-model:value="uiStore.artistTracksSort"
|
||||||
:options="groupings"
|
:options="groupings"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -38,14 +38,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<list-tracks :items="tracks" :uris="track_uris" />
|
<list-tracks :items="tracks" :uris="trackUris" />
|
||||||
<modal-dialog-artist
|
<modal-dialog-artist
|
||||||
:item="artist"
|
:item="artist"
|
||||||
:show="showDetailsModal"
|
:show="showDetailsModal"
|
||||||
@ -111,7 +111,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
album_count() {
|
albumCount() {
|
||||||
return new Set(
|
return new Set(
|
||||||
[...this.tracks]
|
[...this.tracks]
|
||||||
.filter((track) => track.isItem)
|
.filter((track) => track.isItem)
|
||||||
@ -139,7 +139,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
subtitle: [
|
subtitle: [
|
||||||
{
|
{
|
||||||
count: this.album_count,
|
count: this.albumCount,
|
||||||
handler: this.openArtist,
|
handler: this.openArtist,
|
||||||
key: 'count.albums'
|
key: 'count.albums'
|
||||||
},
|
},
|
||||||
@ -148,12 +148,12 @@ export default {
|
|||||||
title: this.artist.name
|
title: this.artist.name
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
track_uris() {
|
trackUris() {
|
||||||
return this.trackList.items.map((item) => item.uri).join()
|
return this.trackList.items.map((item) => item.uri).join()
|
||||||
},
|
},
|
||||||
tracks() {
|
tracks() {
|
||||||
const { options } = this.groupings.find(
|
const { options } = this.groupings.find(
|
||||||
(grouping) => grouping.id === this.uiStore.artist_tracks_sort
|
(grouping) => grouping.id === this.uiStore.artistTracksSort
|
||||||
)
|
)
|
||||||
options.filters = [
|
options.filters = [
|
||||||
(track) => !this.uiStore.hideSpotify || track.data_kind !== 'spotify'
|
(track) => !this.uiStore.hideSpotify || track.data_kind !== 'spotify'
|
||||||
@ -169,14 +169,14 @@ export default {
|
|||||||
params: { id: this.artist.id }
|
params: { id: this.artist.id }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_uri(
|
webapi.player_play_uri(
|
||||||
this.trackList.items.map((item) => item.uri).join(),
|
this.trackList.items.map((item) => item.uri).join(),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
v-text="$t('options.sort.title')"
|
v-text="$t('options.sort.title')"
|
||||||
/>
|
/>
|
||||||
<control-dropdown
|
<control-dropdown
|
||||||
v-model:value="uiStore.artists_sort"
|
v-model:value="uiStore.artistsSort"
|
||||||
:options="groupings"
|
:options="groupings"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -100,7 +100,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
artists() {
|
artists() {
|
||||||
const { options } = this.groupings.find(
|
const { options } = this.groupings.find(
|
||||||
(grouping) => grouping.id === this.uiStore.artists_sort
|
(grouping) => grouping.id === this.uiStore.artistsSort
|
||||||
)
|
)
|
||||||
options.filters = [
|
options.filters = [
|
||||||
(artist) =>
|
(artist) =>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
class="is-clickable is-medium"
|
class="is-clickable is-medium"
|
||||||
@click="showDetails"
|
@click="openDetails"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
@ -96,11 +96,11 @@ export default {
|
|||||||
params: { id: this.album.artist_id }
|
params: { id: this.album.artist_id }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_uri(this.album.uri, false)
|
webapi.player_play_uri(this.album.uri, false)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{
|
:button="{ handler: play, icon: 'play', key: 'actions.play' }"
|
||||||
handler: play,
|
|
||||||
icon: 'play',
|
|
||||||
key: 'page.audiobooks.artist.play'
|
|
||||||
}"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
@ -85,14 +81,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_uri(
|
webapi.player_play_uri(
|
||||||
this.albums.items.map((item) => item.uri).join(),
|
this.albums.items.map((item) => item.uri).join(),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
@ -89,6 +89,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
openTracks() {
|
openTracks() {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'music-composer-tracks',
|
name: 'music-composer-tracks',
|
||||||
@ -97,9 +100,6 @@ export default {
|
|||||||
},
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_expression(this.expression, true)
|
webapi.player_play_expression(this.expression, true)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
v-text="$t('options.sort.title')"
|
v-text="$t('options.sort.title')"
|
||||||
/>
|
/>
|
||||||
<control-dropdown
|
<control-dropdown
|
||||||
v-model:value="uiStore.composer_tracks_sort"
|
v-model:value="uiStore.composerTracksSort"
|
||||||
:options="groupings"
|
:options="groupings"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
@ -129,9 +129,9 @@ export default {
|
|||||||
},
|
},
|
||||||
tracks() {
|
tracks() {
|
||||||
const { options } = this.groupings.find(
|
const { options } = this.groupings.find(
|
||||||
(grouping) => grouping.id === this.uiStore.composer_tracks_sort
|
(grouping) => grouping.id === this.uiStore.composerTracksSort
|
||||||
)
|
)
|
||||||
return this.tracks_list.group(options)
|
return this.trackList.group(options)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -141,11 +141,11 @@ export default {
|
|||||||
params: { name: this.composer.name }
|
params: { name: this.composer.name }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_expression(this.expression, true)
|
webapi.player_play_expression(this.expression, true)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
return webapi.library_composers('music')
|
return webapi.library_composers('music')
|
||||||
},
|
},
|
||||||
set(vm, response) {
|
set(vm, response) {
|
||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
TabsMusic
|
TabsMusic
|
||||||
},
|
},
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'play', key: 'actions.play' }"
|
:button="{ handler: play, icon: 'play', key: 'actions.play' }"
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<list-tracks
|
<list-tracks
|
||||||
:expression="expression"
|
:expression="expression"
|
||||||
:items="tracks"
|
:items="tracks"
|
||||||
:show-icon="true"
|
icon="file-music-outline"
|
||||||
/>
|
/>
|
||||||
<modal-dialog-directory
|
<modal-dialog-directory
|
||||||
:item="current"
|
:item="current"
|
||||||
@ -120,12 +120,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
play() {
|
play() {
|
||||||
webapi.player_play_expression(this.expression, false)
|
webapi.player_play_expression(this.expression, false)
|
||||||
},
|
},
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
},
|
|
||||||
transform(path) {
|
transform(path) {
|
||||||
return { name: path.slice(path.lastIndexOf('/') + 1), path }
|
return { name: path.slice(path.lastIndexOf('/') + 1), path }
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
@ -95,6 +95,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openDetails() {
|
||||||
|
this.showDetailsModal = true
|
||||||
|
},
|
||||||
openTracks() {
|
openTracks() {
|
||||||
this.showDetailsModal = false
|
this.showDetailsModal = false
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
@ -108,9 +111,6 @@ export default {
|
|||||||
`genre is "${this.genre.name}" and media_kind is ${this.media_kind}`,
|
`genre is "${this.genre.name}" and media_kind is ${this.media_kind}`,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
},
|
|
||||||
showDetails() {
|
|
||||||
this.showDetailsModal = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
v-text="$t('options.sort.title')"
|
v-text="$t('options.sort.title')"
|
||||||
/>
|
/>
|
||||||
<control-dropdown
|
<control-dropdown
|
||||||
v-model:value="uiStore.genre_tracks_sort"
|
v-model:value="uiStore.genreTracksSort"
|
||||||
:options="groupings"
|
:options="groupings"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
:button="{ handler: play, icon: 'shuffle', key: 'actions.shuffle' }"
|
||||||
@ -131,7 +131,7 @@ export default {
|
|||||||
},
|
},
|
||||||
tracks() {
|
tracks() {
|
||||||
const { options } = this.groupings.find(
|
const { options } = this.groupings.find(
|
||||||
(grouping) => grouping.id === this.uiStore.genre_tracks_sort
|
(grouping) => grouping.id === this.uiStore.genreTracksSort
|
||||||
)
|
)
|
||||||
return this.trackList.group(options)
|
return this.trackList.group(options)
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ export default {
|
|||||||
play() {
|
play() {
|
||||||
webapi.player_play_expression(this.expression, true)
|
webapi.player_play_expression(this.expression, true)
|
||||||
},
|
},
|
||||||
showDetails() {
|
openDetails() {
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
return webapi.library_genres('music')
|
return webapi.library_genres('music')
|
||||||
},
|
},
|
||||||
set(vm, response) {
|
set(vm, response) {
|
||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
TabsMusic
|
TabsMusic
|
||||||
},
|
},
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -50,7 +50,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
webapi.search({
|
webapi.search({
|
||||||
expression:
|
expression:
|
||||||
@ -82,14 +82,14 @@ export default {
|
|||||||
TabsMusic
|
TabsMusic
|
||||||
},
|
},
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
albums: [],
|
albums: [],
|
||||||
tracks: { items: [] }
|
tracks: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import { useSettingsStore } from '@/stores/settings'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
const limit = useSettingsStore().recently_added_limit
|
const limit = useSettingsStore().recently_added_limit
|
||||||
return webapi.search({
|
return webapi.search({
|
||||||
expression:
|
expression:
|
||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
name: 'PageMusicRecentlyAdded',
|
name: 'PageMusicRecentlyAdded',
|
||||||
components: { ContentWithHeading, HeadingTitle, ListAlbums, TabsMusic },
|
components: { ContentWithHeading, HeadingTitle, ListAlbums, TabsMusic },
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -23,7 +23,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
return webapi.search({
|
return webapi.search({
|
||||||
expression:
|
expression:
|
||||||
'time_played after 8 weeks ago and media_kind is music order by time_played desc',
|
'time_played after 8 weeks ago and media_kind is music order by time_played desc',
|
||||||
@ -40,7 +40,7 @@ export default {
|
|||||||
name: 'PageMusicRecentlyPlayed',
|
name: 'PageMusicRecentlyPlayed',
|
||||||
components: { ContentWithHeading, HeadingTitle, ListTracks, TabsMusic },
|
components: { ContentWithHeading, HeadingTitle, ListTracks, TabsMusic },
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -50,7 +50,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
return webapi.spotify().then(({ data }) => {
|
return webapi.spotify().then(({ data }) => {
|
||||||
const spotifyApi = new SpotifyWebApi()
|
const spotifyApi = new SpotifyWebApi()
|
||||||
spotifyApi.setAccessToken(data.webapi_token)
|
spotifyApi.setAccessToken(data.webapi_token)
|
||||||
@ -82,7 +82,7 @@ export default {
|
|||||||
TabsMusic
|
TabsMusic
|
||||||
},
|
},
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
|
|||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const dataObject = {
|
const dataObject = {
|
||||||
load(to) {
|
load() {
|
||||||
return webapi.spotify().then(({ data }) => {
|
return webapi.spotify().then(({ data }) => {
|
||||||
const spotifyApi = new SpotifyWebApi()
|
const spotifyApi = new SpotifyWebApi()
|
||||||
spotifyApi.setAccessToken(data.webapi_token)
|
spotifyApi.setAccessToken(data.webapi_token)
|
||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
TabsMusic
|
TabsMusic
|
||||||
},
|
},
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
dataObject.load(to).then((response) => {
|
dataObject.load().then((response) => {
|
||||||
next((vm) => dataObject.set(vm, response))
|
next((vm) => dataObject.set(vm, response))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -9,21 +9,21 @@
|
|||||||
:album="track.album"
|
:album="track.album"
|
||||||
class="is-clickable is-big"
|
class="is-clickable is-big"
|
||||||
:class="{ 'is-masked': lyricsStore.active }"
|
:class="{ 'is-masked': lyricsStore.active }"
|
||||||
@click="openDialog(track)"
|
@click="openDetails(track)"
|
||||||
/>
|
/>
|
||||||
<lyrics-pane v-if="lyricsStore.active" />
|
<lyrics-pane v-if="lyricsStore.active" />
|
||||||
<control-slider
|
<control-slider
|
||||||
v-model:value="track_progress"
|
v-model:value="trackProgress"
|
||||||
class="mt-5"
|
class="mt-5"
|
||||||
:disabled="is_live"
|
:disabled="isLive"
|
||||||
:max="track_progress_max"
|
:max="trackProgressMax"
|
||||||
@change="seek"
|
@change="seek"
|
||||||
@mousedown="start_dragging"
|
@mousedown="startDragging"
|
||||||
@mouseup="end_dragging"
|
@mouseup="endDragging"
|
||||||
/>
|
/>
|
||||||
<div class="is-flex is-justify-content-space-between">
|
<div class="is-flex is-justify-content-space-between">
|
||||||
<p class="subtitle is-7" v-text="track_elapsed_time" />
|
<p class="subtitle is-7" v-text="trackElapsedTime" />
|
||||||
<p class="subtitle is-7" v-text="track_total_time" />
|
<p class="subtitle is-7" v-text="trackTotalTime" />
|
||||||
</div>
|
</div>
|
||||||
<p class="title is-5" v-text="track.title" />
|
<p class="title is-5" v-text="track.title" />
|
||||||
<p class="title is-6" v-text="track.artist" />
|
<p class="title is-6" v-text="track.artist" />
|
||||||
@ -83,8 +83,8 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
INTERVAL,
|
INTERVAL,
|
||||||
interval_id: 0,
|
intervalId: 0,
|
||||||
is_dragged: false,
|
isDragged: false,
|
||||||
selectedItem: {},
|
selectedItem: {},
|
||||||
showDetailsModal: false
|
showDetailsModal: false
|
||||||
}
|
}
|
||||||
@ -109,16 +109,16 @@ export default {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
is_live() {
|
isLive() {
|
||||||
return this.track.length_ms === 0
|
return this.track.length_ms === 0
|
||||||
},
|
},
|
||||||
track() {
|
track() {
|
||||||
return this.queueStore.current
|
return this.queueStore.current
|
||||||
},
|
},
|
||||||
track_elapsed_time() {
|
trackElapsedTime() {
|
||||||
return this.$filters.toTimecode(this.track_progress * INTERVAL)
|
return this.$filters.toTimecode(this.trackProgress * INTERVAL)
|
||||||
},
|
},
|
||||||
track_progress: {
|
trackProgress: {
|
||||||
get() {
|
get() {
|
||||||
return Math.floor(this.playerStore.item_progress_ms / INTERVAL)
|
return Math.floor(this.playerStore.item_progress_ms / INTERVAL)
|
||||||
},
|
},
|
||||||
@ -126,23 +126,23 @@ export default {
|
|||||||
this.playerStore.item_progress_ms = value * INTERVAL
|
this.playerStore.item_progress_ms = value * INTERVAL
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
track_progress_max() {
|
trackProgressMax() {
|
||||||
return this.is_live ? 1 : Math.floor(this.track.length_ms / INTERVAL)
|
return this.isLive ? 1 : Math.floor(this.track.length_ms / INTERVAL)
|
||||||
},
|
},
|
||||||
track_total_time() {
|
trackTotalTime() {
|
||||||
return this.is_live
|
return this.isLive
|
||||||
? this.$t('page.now-playing.live')
|
? this.$t('page.now-playing.live')
|
||||||
: this.$filters.toTimecode(this.track.length_ms)
|
: this.$filters.toTimecode(this.track.length_ms)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'playerStore.state'(newState) {
|
'playerStore.state'(newState) {
|
||||||
if (this.interval_id > 0) {
|
if (this.intervalId > 0) {
|
||||||
window.clearTimeout(this.interval_id)
|
window.clearTimeout(this.intervalId)
|
||||||
this.interval_id = 0
|
this.intervalId = 0
|
||||||
}
|
}
|
||||||
if (newState === 'play') {
|
if (newState === 'play') {
|
||||||
this.interval_id = window.setInterval(this.tick, INTERVAL)
|
this.intervalId = window.setInterval(this.tick, INTERVAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -150,35 +150,35 @@ export default {
|
|||||||
webapi.player_status().then(({ data }) => {
|
webapi.player_status().then(({ data }) => {
|
||||||
this.playerStore.$state = data
|
this.playerStore.$state = data
|
||||||
if (this.playerStore.state === 'play') {
|
if (this.playerStore.state === 'play') {
|
||||||
this.interval_id = window.setInterval(this.tick, INTERVAL)
|
this.intervalId = window.setInterval(this.tick, INTERVAL)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
if (this.interval_id > 0) {
|
if (this.intervalId > 0) {
|
||||||
window.clearTimeout(this.interval_id)
|
window.clearTimeout(this.intervalId)
|
||||||
this.interval_id = 0
|
this.intervalId = 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
end_dragging() {
|
endDragging() {
|
||||||
this.is_dragged = false
|
this.isDragged = false
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
},
|
},
|
||||||
seek() {
|
seek() {
|
||||||
if (!this.is_live) {
|
if (!this.isLive) {
|
||||||
webapi.player_seek_to_pos(this.track_progress * INTERVAL)
|
webapi.player_seek_to_pos(this.trackProgress * INTERVAL)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
start_dragging() {
|
startDragging() {
|
||||||
this.is_dragged = true
|
this.isDragged = true
|
||||||
},
|
},
|
||||||
tick() {
|
tick() {
|
||||||
if (!this.is_dragged) {
|
if (!this.isDragged) {
|
||||||
this.track_progress += 1
|
this.trackProgress += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ const dataObject = {
|
|||||||
},
|
},
|
||||||
set(vm, response) {
|
set(vm, response) {
|
||||||
vm.playlist = response[0].data
|
vm.playlist = response[0].data
|
||||||
vm.playlists_list = new GroupedList(response[1].data)
|
vm.playlistList = new GroupedList(response[1].data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
playlist: {},
|
playlist: {},
|
||||||
playlists_list: new GroupedList()
|
playlistList: new GroupedList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -68,7 +68,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
playlists() {
|
playlists() {
|
||||||
return this.playlists_list.group({
|
return this.playlistList.group({
|
||||||
filters: [
|
filters: [
|
||||||
(playlist) =>
|
(playlist) =>
|
||||||
playlist.folder ||
|
playlist.folder ||
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{
|
:button="{
|
||||||
@ -91,7 +91,7 @@ export default {
|
|||||||
play() {
|
play() {
|
||||||
webapi.player_play_uri(this.uris, true)
|
webapi.player_play_uri(this.uris, true)
|
||||||
},
|
},
|
||||||
showDetails() {
|
openDetails() {
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #heading-right>
|
<template #heading-right>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{
|
:button="{
|
||||||
@ -153,7 +153,7 @@ export default {
|
|||||||
this.showDetailsModal = false
|
this.showDetailsModal = false
|
||||||
webapi.player_play_uri(this.playlist.uri, true)
|
webapi.player_play_uri(this.playlist.uri, true)
|
||||||
},
|
},
|
||||||
showDetails() {
|
openDetails() {
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
:button="{ handler: play, icon: 'play', key: 'actions.play' }"
|
:button="{ handler: play, icon: 'play', key: 'actions.play' }"
|
||||||
/>
|
/>
|
||||||
<control-button
|
<control-button
|
||||||
:button="{ handler: showDetails, icon: 'dots-horizontal' }"
|
:button="{ handler: openDetails, icon: 'dots-horizontal' }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
:artist="album.artist"
|
:artist="album.artist"
|
||||||
:album="album.name"
|
:album="album.name"
|
||||||
class="is-clickable is-medium"
|
class="is-clickable is-medium"
|
||||||
@click="showDetails"
|
@click="openDetails"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
<br />
|
<br />
|
||||||
</template>
|
</template>
|
||||||
<template #name>
|
<template #name>
|
||||||
<b v-text="rss_playlist_to_remove.name" />
|
<b v-text="playlistToRemove.name" />
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</template>
|
</template>
|
||||||
@ -106,7 +106,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
album: {},
|
album: {},
|
||||||
rss_playlist_to_remove: {},
|
playlistToRemove: {},
|
||||||
showDetailsModal: false,
|
showDetailsModal: false,
|
||||||
showRemovePodcastModal: false,
|
showRemovePodcastModal: false,
|
||||||
tracks: new GroupedList()
|
tracks: new GroupedList()
|
||||||
@ -133,7 +133,7 @@ export default {
|
|||||||
webapi
|
webapi
|
||||||
.library_track_playlists(this.tracks.items[0].id)
|
.library_track_playlists(this.tracks.items[0].id)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
;[this.rss_playlist_to_remove] = data.items.filter(
|
;[this.playlistToRemove] = data.items.filter(
|
||||||
(pl) => pl.type === 'rss'
|
(pl) => pl.type === 'rss'
|
||||||
)
|
)
|
||||||
this.showRemovePodcastModal = true
|
this.showRemovePodcastModal = true
|
||||||
@ -150,13 +150,11 @@ export default {
|
|||||||
},
|
},
|
||||||
removePodcast() {
|
removePodcast() {
|
||||||
this.showRemovePodcastModal = false
|
this.showRemovePodcastModal = false
|
||||||
webapi
|
webapi.library_playlist_delete(this.playlistToRemove.id).then(() => {
|
||||||
.library_playlist_delete(this.rss_playlist_to_remove.id)
|
|
||||||
.then(() => {
|
|
||||||
this.$router.replace({ name: 'podcasts' })
|
this.$router.replace({ name: 'podcasts' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
showDetails() {
|
openDetails() {
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,12 @@
|
|||||||
<list-item-queue-item
|
<list-item-queue-item
|
||||||
:item="element"
|
:item="element"
|
||||||
:position="index"
|
:position="index"
|
||||||
:current_position="current_position"
|
:current-position="currentPosition"
|
||||||
:hide-read-items="uiStore.hideReadItems"
|
:hide-read-items="uiStore.hideReadItems"
|
||||||
:editing="editing"
|
:editing="editing"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a v-if="!editing" @click.prevent.stop="openDialog(element)">
|
<a v-if="!editing" @click.prevent.stop="openDetails(element)">
|
||||||
<mdicon
|
<mdicon
|
||||||
class="icon has-text-grey"
|
class="icon has-text-grey"
|
||||||
name="dots-vertical"
|
name="dots-vertical"
|
||||||
@ -139,7 +139,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
current_position() {
|
currentPosition() {
|
||||||
return this.queueStore.current?.position ?? -1
|
return this.queueStore.current?.position ?? -1
|
||||||
},
|
},
|
||||||
heading() {
|
heading() {
|
||||||
@ -166,7 +166,7 @@ export default {
|
|||||||
},
|
},
|
||||||
moveItem(event) {
|
moveItem(event) {
|
||||||
const oldPosition =
|
const oldPosition =
|
||||||
event.oldIndex + (this.uiStore.hideReadItems && this.current_position)
|
event.oldIndex + (this.uiStore.hideReadItems && this.currentPosition)
|
||||||
const item = this.items[oldPosition]
|
const item = this.items[oldPosition]
|
||||||
const newPosition = item.position + (event.newIndex - event.oldIndex)
|
const newPosition = item.position + (event.newIndex - event.oldIndex)
|
||||||
if (newPosition !== oldPosition) {
|
if (newPosition !== oldPosition) {
|
||||||
@ -176,7 +176,7 @@ export default {
|
|||||||
openAddStreamDialog() {
|
openAddStreamDialog() {
|
||||||
this.showAddStreamDialog = true
|
this.showAddStreamDialog = true
|
||||||
},
|
},
|
||||||
openDialog(item) {
|
openDetails(item) {
|
||||||
this.selectedItem = item
|
this.selectedItem = item
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
},
|
},
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
/>
|
/>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<control-setting-switch
|
<control-setting-switch
|
||||||
v-if="spotify.spotify_logged_in"
|
v-if="servicesStore.spotify_logged_in"
|
||||||
category="artwork"
|
category="artwork"
|
||||||
name="use_artwork_source_spotify"
|
name="use_artwork_source_spotify"
|
||||||
>
|
>
|
||||||
@ -95,11 +95,6 @@ export default {
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return { servicesStore: useServicesStore() }
|
return { servicesStore: useServicesStore() }
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
spotify() {
|
|
||||||
return this.servicesStore.spotify
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -47,8 +47,8 @@
|
|||||||
<div class="control">
|
<div class="control">
|
||||||
<a
|
<a
|
||||||
class="button is-danger"
|
class="button is-danger"
|
||||||
@click="logout_spotify"
|
@click="logoutSpotify"
|
||||||
v-text="$t('page.settings.services.logout')"
|
v-text="$t('actions.logout')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -75,11 +75,11 @@
|
|||||||
<a
|
<a
|
||||||
class="button is-danger"
|
class="button is-danger"
|
||||||
@click="logoutLastfm"
|
@click="logoutLastfm"
|
||||||
v-text="$t('page.settings.services.logout')"
|
v-text="$t('actions.logout')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!lastfm.scrobbling_enabled">
|
<div v-if="!lastfm.scrobbling_enabled">
|
||||||
<form @submit.prevent="login_lastfm">
|
<form @submit.prevent="loginLastfm">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
@ -106,7 +106,7 @@
|
|||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
type="submit"
|
type="submit"
|
||||||
v-text="$t('page.settings.services.login')"
|
v-text="$t('actions.login')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -172,7 +172,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
login_lastfm() {
|
loginLastfm() {
|
||||||
webapi.lastfm_login(this.lastfm_login).then((response) => {
|
webapi.lastfm_login(this.lastfm_login).then((response) => {
|
||||||
this.lastfm_login.user = ''
|
this.lastfm_login.user = ''
|
||||||
this.lastfm_login.password = ''
|
this.lastfm_login.password = ''
|
||||||
@ -190,7 +190,7 @@ export default {
|
|||||||
logoutLastfm() {
|
logoutLastfm() {
|
||||||
webapi.lastfm_logout()
|
webapi.lastfm_logout()
|
||||||
},
|
},
|
||||||
logout_spotify() {
|
logoutSpotify() {
|
||||||
webapi.spotify_logout()
|
webapi.spotify_logout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div v-if="pairing.active">
|
<div v-if="pairing.active">
|
||||||
<form @submit.prevent="kickoff_pairing">
|
<form @submit.prevent="kickoffPairing">
|
||||||
<label class="label has-text-weight-normal content">
|
<label class="label has-text-weight-normal content">
|
||||||
<span v-text="$t('page.settings.devices.pairing-request')" />
|
<span v-text="$t('page.settings.devices.pairing-request')" />
|
||||||
<b v-text="pairing.remote" />
|
<b v-text="pairing.remote" />
|
||||||
@ -17,7 +17,7 @@
|
|||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
v-model="pairing_req.pin"
|
v-model="pairingRequest.pin"
|
||||||
class="input"
|
class="input"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
pattern="[\d]{4}"
|
pattern="[\d]{4}"
|
||||||
@ -62,12 +62,12 @@
|
|||||||
<form
|
<form
|
||||||
v-if="output.needs_auth_key"
|
v-if="output.needs_auth_key"
|
||||||
class="mb-5"
|
class="mb-5"
|
||||||
@submit.prevent="kickoff_verification(output.id)"
|
@submit.prevent="kickoffVerification(output.id)"
|
||||||
>
|
>
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
v-model="verification_req.pin"
|
v-model="verificationRequest.pin"
|
||||||
class="input"
|
class="input"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
pattern="[\d]{4}"
|
pattern="[\d]{4}"
|
||||||
@ -106,8 +106,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
pairing_req: { pin: '' },
|
pairingRequest: { pin: '' },
|
||||||
verification_req: { pin: '' }
|
verificationRequest: { pin: '' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -119,11 +119,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
kickoff_pairing() {
|
kickoffPairing() {
|
||||||
webapi.pairing_kickoff(this.pairing_req)
|
webapi.pairing_kickoff(this.pairingRequest)
|
||||||
},
|
},
|
||||||
kickoff_verification(identifier) {
|
kickoffVerification(identifier) {
|
||||||
webapi.output_update(identifier, this.verification_req)
|
webapi.output_update(identifier, this.verificationRequest)
|
||||||
},
|
},
|
||||||
toggleOutput(identifier) {
|
toggleOutput(identifier) {
|
||||||
webapi.output_toggle(identifier)
|
webapi.output_toggle(identifier)
|
||||||
|
@ -8,7 +8,10 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<control-dropdown v-model:value="locale" :options="locales" />
|
<control-dropdown
|
||||||
|
v-model:value="locale"
|
||||||
|
:options="settingsStore.locales"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-with-heading>
|
<content-with-heading>
|
||||||
@ -198,14 +201,6 @@ export default {
|
|||||||
set(locale) {
|
set(locale) {
|
||||||
this.$i18n.locale = locale
|
this.$i18n.locale = locale
|
||||||
}
|
}
|
||||||
},
|
|
||||||
locales: {
|
|
||||||
get() {
|
|
||||||
return this.$i18n.availableLocales.map((item) => ({
|
|
||||||
id: item,
|
|
||||||
name: this.$t(`language.${item}`)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,5 +33,8 @@ export const useNotificationsStore = defineStore('NotificationsStore', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getters: {
|
||||||
|
isEmpty: (state) => state.list.length <= 0
|
||||||
|
},
|
||||||
state: () => ({ list: [], nextId: 1 })
|
state: () => ({ list: [], nextId: 1 })
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import i18n from '@/i18n'
|
||||||
|
|
||||||
|
const { t, availableLocales } = i18n.global
|
||||||
|
|
||||||
export const useSettingsStore = defineStore('SettingsStore', {
|
export const useSettingsStore = defineStore('SettingsStore', {
|
||||||
actions: {
|
actions: {
|
||||||
@ -25,6 +28,12 @@ export const useSettingsStore = defineStore('SettingsStore', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
locales(state) {
|
||||||
|
return availableLocales.map((item) => ({
|
||||||
|
id: item,
|
||||||
|
name: t(`language.${item}`)
|
||||||
|
}))
|
||||||
|
},
|
||||||
recently_added_limit: (state) =>
|
recently_added_limit: (state) =>
|
||||||
state.setting('webinterface', 'recently_added_limit')?.value ?? 100,
|
state.setting('webinterface', 'recently_added_limit')?.value ?? 100,
|
||||||
show_composer_for_genre: (state) =>
|
show_composer_for_genre: (state) =>
|
||||||
|
@ -19,12 +19,12 @@ export const useUIStore = defineStore('UIStore', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
state: () => ({
|
state: () => ({
|
||||||
albums_sort: 1,
|
albumsSort: 1,
|
||||||
artist_albums_sort: 1,
|
artistAlbumsSort: 1,
|
||||||
artist_tracks_sort: 1,
|
artistTracksSort: 1,
|
||||||
artists_sort: 1,
|
artistsSort: 1,
|
||||||
composer_tracks_sort: 1,
|
composerTracksSort: 1,
|
||||||
genre_tracks_sort: 1,
|
genreTracksSort: 1,
|
||||||
hideReadItems: false,
|
hideReadItems: false,
|
||||||
hideSingles: false,
|
hideSingles: false,
|
||||||
hideSpotify: false,
|
hideSpotify: false,
|
||||||
|
@ -34,11 +34,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<slot name="content" />
|
<slot name="content" />
|
||||||
<nav v-if="$slots.footer" class="level mt-4">
|
<div
|
||||||
<div class="level-item">
|
v-if="$slots.footer"
|
||||||
|
class="is-flex is-justify-content-center mt-4"
|
||||||
|
>
|
||||||
<slot name="footer" />
|
<slot name="footer" />
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user