mirror of
https://github.com/owntone/owntone-server.git
synced 2025-07-20 14:01:17 -04:00
293 lines
8.0 KiB
Vue
293 lines
8.0 KiB
Vue
<template>
|
|
<vue-progress-bar class="has-background-primary" />
|
|
<navbar-top />
|
|
<router-view v-slot="{ Component }">
|
|
<component :is="Component" />
|
|
</router-view>
|
|
<modal-dialog-remote-pairing
|
|
:show="remotesStore.active"
|
|
@close="remotesStore.active = false"
|
|
/>
|
|
<modal-dialog-update
|
|
:show="uiStore.showUpdateDialog"
|
|
@close="uiStore.showUpdateDialog = false"
|
|
/>
|
|
<list-notifications v-show="!uiStore.showBurgerMenu" />
|
|
<navbar-bottom />
|
|
<div
|
|
v-show="uiStore.showBurgerMenu || uiStore.showPlayerMenu"
|
|
class="overlay-fullscreen"
|
|
@click="uiStore.hideMenus"
|
|
/>
|
|
</template>
|
|
|
|
<script>
|
|
import ListNotifications from '@/components/ListNotifications.vue'
|
|
import ModalDialogRemotePairing from '@/components/ModalDialogRemotePairing.vue'
|
|
import ModalDialogUpdate from '@/components/ModalDialogUpdate.vue'
|
|
import NavbarBottom from '@/components/NavbarBottom.vue'
|
|
import NavbarTop from '@/components/NavbarTop.vue'
|
|
import ReconnectingWebSocket from 'reconnectingwebsocket'
|
|
import { useConfigurationStore } from '@/stores/configuration'
|
|
import { useLibraryStore } from '@/stores/library'
|
|
import { useLyricsStore } from '@/stores/lyrics'
|
|
import { useNotificationsStore } from '@/stores/notifications'
|
|
import { useOutputsStore } from './stores/outputs'
|
|
import { usePlayerStore } from '@/stores/player'
|
|
import { useQueueStore } from '@/stores/queue'
|
|
import { useRemotesStore } from './stores/remotes'
|
|
import { useServicesStore } from '@/stores/services'
|
|
import { useSettingsStore } from '@/stores/settings'
|
|
import { useUIStore } from './stores/ui'
|
|
import webapi from '@/webapi'
|
|
|
|
export default {
|
|
name: 'App',
|
|
components: {
|
|
ListNotifications,
|
|
ModalDialogRemotePairing,
|
|
ModalDialogUpdate,
|
|
NavbarBottom,
|
|
NavbarTop
|
|
},
|
|
setup() {
|
|
return {
|
|
configurationStore: useConfigurationStore(),
|
|
libraryStore: useLibraryStore(),
|
|
lyricsStore: useLyricsStore(),
|
|
notificationsStore: useNotificationsStore(),
|
|
outputsStore: useOutputsStore(),
|
|
playerStore: usePlayerStore(),
|
|
queueStore: useQueueStore(),
|
|
remotesStore: useRemotesStore(),
|
|
servicesStore: useServicesStore(),
|
|
settingsStore: useSettingsStore(),
|
|
uiStore: useUIStore()
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
timerId: 0
|
|
}
|
|
},
|
|
watch: {
|
|
'uiStore.showBurgerMenu'() {
|
|
this.updateClipping()
|
|
},
|
|
'uiStore.showPlayerMenu'() {
|
|
this.updateClipping()
|
|
}
|
|
},
|
|
created() {
|
|
this.connect()
|
|
this.$router.beforeEach((to, from, next) => {
|
|
this.updateClipping()
|
|
if (!(to.path === from.path && to.hash)) {
|
|
if (to.meta.progress) {
|
|
this.$Progress.parseMeta(to.meta.progress)
|
|
}
|
|
this.$Progress.start()
|
|
}
|
|
next()
|
|
})
|
|
this.$router.afterEach(() => {
|
|
this.$Progress.finish()
|
|
})
|
|
},
|
|
methods: {
|
|
connect() {
|
|
webapi
|
|
.config()
|
|
.then((data) => {
|
|
this.configurationStore.$state = data
|
|
this.uiStore.hideSingles = data.hide_singles
|
|
document.title = data.library_name
|
|
this.openWebsocket()
|
|
this.$Progress.finish()
|
|
})
|
|
.catch(() => {
|
|
this.notificationsStore.add({
|
|
text: this.$t('server.connection-failed'),
|
|
topic: 'connection',
|
|
type: 'danger'
|
|
})
|
|
})
|
|
},
|
|
openWebsocket() {
|
|
const socket = this.createWebsocket()
|
|
socket.onopen = () => {
|
|
socket.send(
|
|
JSON.stringify({
|
|
notify: [
|
|
'update',
|
|
'database',
|
|
'player',
|
|
'options',
|
|
'outputs',
|
|
'volume',
|
|
'queue',
|
|
'spotify',
|
|
'lastfm',
|
|
'pairing'
|
|
]
|
|
})
|
|
)
|
|
this.updateOutputs()
|
|
this.updatePlayerStatus()
|
|
this.updateLibraryStats()
|
|
this.updateSettings()
|
|
this.updateQueue()
|
|
this.updateSpotify()
|
|
this.updateLastfm()
|
|
this.updatePairing()
|
|
}
|
|
|
|
let updateThrottled = false
|
|
|
|
const updateInfo = () => {
|
|
if (updateThrottled) {
|
|
return
|
|
}
|
|
this.updateOutputs()
|
|
this.updatePlayerStatus()
|
|
this.updateLibraryStats()
|
|
this.updateSettings()
|
|
this.updateQueue()
|
|
this.updateSpotify()
|
|
this.updateLastfm()
|
|
this.updatePairing()
|
|
updateThrottled = true
|
|
setTimeout(() => {
|
|
updateThrottled = false
|
|
}, 500)
|
|
}
|
|
|
|
window.addEventListener('focus', updateInfo)
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (document.visibilityState === 'visible') {
|
|
updateInfo()
|
|
}
|
|
})
|
|
|
|
socket.onmessage = (response) => {
|
|
const data = JSON.parse(response.data)
|
|
if (
|
|
data.notify.includes('update') ||
|
|
data.notify.includes('database')
|
|
) {
|
|
this.updateLibraryStats()
|
|
}
|
|
if (
|
|
data.notify.includes('player') ||
|
|
data.notify.includes('options') ||
|
|
data.notify.includes('volume')
|
|
) {
|
|
this.updatePlayerStatus()
|
|
}
|
|
if (data.notify.includes('outputs') || data.notify.includes('volume')) {
|
|
this.updateOutputs()
|
|
}
|
|
if (data.notify.includes('queue')) {
|
|
this.updateQueue()
|
|
}
|
|
if (data.notify.includes('spotify')) {
|
|
this.updateSpotify()
|
|
}
|
|
if (data.notify.includes('lastfm')) {
|
|
this.updateLastfm()
|
|
}
|
|
if (data.notify.includes('pairing')) {
|
|
this.updatePairing()
|
|
}
|
|
}
|
|
},
|
|
createWebsocket() {
|
|
const protocol = window.location.protocol.replace('http', 'ws')
|
|
const hostname =
|
|
(import.meta.env.DEV &&
|
|
URL.parse(import.meta.env.VITE_OWNTONE_URL)?.hostname) ||
|
|
window.location.hostname
|
|
const suffix =
|
|
this.configurationStore.websocket_port || `${window.location.port}/ws`
|
|
const url = `${protocol}${hostname}:${suffix}`
|
|
return new ReconnectingWebSocket(url, 'notify', {
|
|
maxReconnectInterval: 2000,
|
|
reconnectInterval: 1000
|
|
})
|
|
},
|
|
updateClipping() {
|
|
if (this.uiStore.showBurgerMenu || this.uiStore.showPlayerMenu) {
|
|
document.querySelector('html').classList.add('is-clipped')
|
|
} else {
|
|
document.querySelector('html').classList.remove('is-clipped')
|
|
}
|
|
},
|
|
updateLastfm() {
|
|
webapi.lastfm().then((data) => {
|
|
this.servicesStore.lastfm = data
|
|
})
|
|
},
|
|
updateLibraryStats() {
|
|
webapi.library_stats().then((data) => {
|
|
this.libraryStore.$state = data
|
|
})
|
|
webapi.library_count('scan_kind is rss').then((data) => {
|
|
this.libraryStore.rss = data
|
|
})
|
|
},
|
|
updateLyrics() {
|
|
const track = this.queueStore.current
|
|
if (track?.track_id) {
|
|
webapi.library_track(track.track_id).then((data) => {
|
|
this.lyricsStore.lyrics = data.lyrics
|
|
})
|
|
} else {
|
|
this.lyricsStore.$reset()
|
|
}
|
|
},
|
|
updateOutputs() {
|
|
webapi.outputs().then((data) => {
|
|
this.outputsStore.outputs = data.outputs
|
|
})
|
|
},
|
|
updatePairing() {
|
|
webapi.pairing().then((data) => {
|
|
this.remotesStore.$state = data
|
|
})
|
|
},
|
|
updatePlayerStatus() {
|
|
webapi.player_status().then((data) => {
|
|
this.playerStore.$state = data
|
|
this.updateLyrics()
|
|
})
|
|
},
|
|
updateQueue() {
|
|
webapi.queue().then((data) => {
|
|
this.queueStore.$state = data
|
|
this.updateLyrics()
|
|
})
|
|
},
|
|
updateSettings() {
|
|
webapi.settings().then((data) => {
|
|
this.settingsStore.$state = data
|
|
})
|
|
},
|
|
updateSpotify() {
|
|
webapi.spotify().then((data) => {
|
|
this.servicesStore.spotify = data
|
|
if (this.timerId > 0) {
|
|
window.clearTimeout(this.timerId)
|
|
this.timerId = 0
|
|
}
|
|
if (data.webapi_token_expires_in > 0 && data.webapi_token) {
|
|
this.timerId = window.setTimeout(
|
|
this.updateSpotify,
|
|
1000 * data.webapi_token_expires_in
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|