audiobookshelf/client/layouts/default.vue

292 lines
11 KiB
Vue

<template>
<div class="text-white max-h-screen h-screen overflow-hidden bg-bg">
<app-appbar />
<Nuxt />
<app-stream-container ref="streamContainer" />
<modals-edit-modal />
<widgets-scan-alert />
</div>
</template>
<script>
export default {
middleware: 'authenticated',
data() {
return {
socket: null
}
},
watch: {
$route(newVal) {
if (this.$store.state.showEditModal) {
this.$store.commit('setShowEditModal', false)
}
if (this.$store.state.selectedAudiobooks) {
this.$store.commit('setSelectedAudiobooks', [])
}
if (this.$store.state.audiobooks.keywordFilter) {
this.$store.commit('audiobooks/setKeywordFilter', '')
}
}
},
computed: {
user() {
return this.$store.state.user.user
}
},
methods: {
connect() {
console.log('[SOCKET] Connected')
var token = this.$store.getters['user/getToken']
this.socket.emit('auth', token)
},
connectError() {},
disconnect() {
console.log('[SOCKET] Disconnected')
},
reconnect() {},
reconnectError() {},
reconnectFailed() {},
init(payload) {
console.log('Init Payload', payload)
if (payload.stream) {
if (this.$refs.streamContainer) {
this.$store.commit('setStream', payload.stream)
this.$refs.streamContainer.streamOpen(payload.stream)
}
}
if (payload.user) {
this.$store.commit('user/setUser', payload.user)
this.$store.commit('user/setSettings', payload.user.settings)
}
if (payload.serverSettings) {
this.$store.commit('setServerSettings', payload.serverSettings)
}
},
streamOpen(stream) {
if (this.$refs.streamContainer) this.$refs.streamContainer.streamOpen(stream)
},
streamClosed(streamId) {
if (this.$refs.streamContainer) this.$refs.streamContainer.streamClosed(streamId)
},
streamProgress(data) {
if (this.$refs.streamContainer) this.$refs.streamContainer.streamProgress(data)
},
streamReady() {
if (this.$refs.streamContainer) this.$refs.streamContainer.streamReady()
},
streamReset(payload) {
if (this.$refs.streamContainer) this.$refs.streamContainer.streamReset(payload)
},
audiobookAdded(audiobook) {
this.$store.commit('audiobooks/addUpdate', audiobook)
},
audiobookUpdated(audiobook) {
this.$store.commit('audiobooks/addUpdate', audiobook)
},
audiobookRemoved(audiobook) {
if (this.$route.name.startsWith('audiobook')) {
if (this.$route.params.id === audiobook.id) {
this.$router.replace('/library')
}
}
this.$store.commit('audiobooks/remove', audiobook)
},
scanComplete({ scanType, results }) {
if (scanType === 'covers') {
this.$store.commit('setIsScanningCovers', false)
if (results) {
this.$toast.success(`Scan Finished\nUpdated ${results.found} covers`)
}
} else {
this.$store.commit('setIsScanning', false)
if (results) {
var scanResultMsgs = []
if (results.added) scanResultMsgs.push(`${results.added} added`)
if (results.updated) scanResultMsgs.push(`${results.updated} updated`)
if (results.removed) scanResultMsgs.push(`${results.removed} removed`)
if (results.missing) scanResultMsgs.push(`${results.missing} missing`)
if (!scanResultMsgs.length) this.$toast.success('Scan Finished\nEverything was up to date')
else this.$toast.success('Scan Finished\n' + scanResultMsgs.join('\n'))
}
}
},
scanStart(scanType) {
if (scanType === 'covers') {
this.$store.commit('setIsScanningCovers', true)
} else {
this.$store.commit('setIsScanning', true)
}
},
scanProgress({ scanType, progress }) {
if (scanType === 'covers') {
this.$store.commit('setCoverScanProgress', progress)
} else {
this.$store.commit('setScanProgress', progress)
}
},
userUpdated(user) {
if (this.$store.state.user.user.id === user.id) {
this.$store.commit('user/setUser', user)
this.$store.commit('user/setSettings', user.settings)
}
},
downloadToastClick(download) {
if (!download || !download.audiobookId) {
return console.error('Invalid download object', download)
}
var audiobook = this.$store.getters['audiobooks/getAudiobook'](download.audiobookId)
if (!audiobook) {
return console.error('Audiobook not found for download', download)
}
this.$store.commit('showEditModalOnTab', { audiobook, tab: 'download' })
},
downloadStarted(download) {
download.status = this.$constants.DownloadStatus.PENDING
download.toastId = this.$toast(`Preparing download "${download.filename}"`, { timeout: false, draggable: false, closeOnClick: false, onClick: () => this.downloadToastClick(download) })
this.$store.commit('downloads/addUpdateDownload', download)
},
downloadReady(download) {
download.status = this.$constants.DownloadStatus.READY
var existingDownload = this.$store.getters['downloads/getDownload'](download.id)
if (existingDownload && existingDownload.toastId !== undefined) {
download.toastId = existingDownload.toastId
this.$toast.update(existingDownload.toastId, { content: `Download "${download.filename}" is ready!`, options: { timeout: 5000, type: 'success', onClick: () => this.downloadToastClick(download) } }, true)
} else {
this.$toast.success(`Download "${download.filename}" is ready!`)
}
this.$store.commit('downloads/addUpdateDownload', download)
},
downloadFailed(download) {
download.status = this.$constants.DownloadStatus.FAILED
var existingDownload = this.$store.getters['downloads/getDownload'](download.id)
var failedMsg = download.isTimedOut ? 'timed out' : 'failed'
if (existingDownload && existingDownload.toastId !== undefined) {
download.toastId = existingDownload.toastId
this.$toast.update(existingDownload.toastId, { content: `Download "${download.filename}" ${failedMsg}`, options: { timeout: 5000, type: 'error', onClick: () => this.downloadToastClick(download) } }, true)
} else {
console.warn('Download failed no existing download', existingDownload)
this.$toast.error(`Download "${download.filename}" ${failedMsg}`)
}
this.$store.commit('downloads/addUpdateDownload', download)
},
downloadKilled(download) {
var existingDownload = this.$store.getters['downloads/getDownload'](download.id)
if (existingDownload && existingDownload.toastId !== undefined) {
download.toastId = existingDownload.toastId
this.$toast.update(existingDownload.toastId, { content: `Download "${download.filename}" was terminated`, options: { timeout: 5000, type: 'error', onClick: () => this.downloadToastClick(download) } }, true)
} else {
console.warn('Download killed no existing download found', existingDownload)
this.$toast.error(`Download "${download.filename}" was terminated`)
}
this.$store.commit('downloads/removeDownload', download)
},
downloadExpired(download) {
download.status = this.$constants.DownloadStatus.EXPIRED
this.$store.commit('downloads/addUpdateDownload', download)
},
logEvtReceived(payload) {
this.$store.commit('logs/logEvt', payload)
},
initializeSocket() {
this.socket = this.$nuxtSocket({
name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
persist: 'main',
teardown: true,
transports: ['websocket'],
upgrade: false
})
this.$root.socket = this.socket
// Connection Listeners
this.socket.on('connect', this.connect)
this.socket.on('connect_error', this.connectError)
this.socket.on('disconnect', this.disconnect)
this.socket.on('reconnecting', this.reconnecting)
this.socket.on('reconnect', this.reconnect)
this.socket.on('reconnect_error', this.reconnectError)
this.socket.on('reconnect_failed', this.reconnectFailed)
this.socket.on('init', this.init)
// Stream Listeners
this.socket.on('stream_open', this.streamOpen)
this.socket.on('stream_closed', this.streamClosed)
this.socket.on('stream_progress', this.streamProgress)
this.socket.on('stream_ready', this.streamReady)
this.socket.on('stream_reset', this.streamReset)
// Audiobook Listeners
this.socket.on('audiobook_updated', this.audiobookUpdated)
this.socket.on('audiobook_added', this.audiobookAdded)
this.socket.on('audiobook_removed', this.audiobookRemoved)
// User Listeners
this.socket.on('user_updated', this.userUpdated)
// Scan Listeners
this.socket.on('scan_start', this.scanStart)
this.socket.on('scan_complete', this.scanComplete)
this.socket.on('scan_progress', this.scanProgress)
// Download Listeners
this.socket.on('download_started', this.downloadStarted)
this.socket.on('download_ready', this.downloadReady)
this.socket.on('download_failed', this.downloadFailed)
this.socket.on('download_killed', this.downloadKilled)
this.socket.on('download_expired', this.downloadExpired)
this.socket.on('log', this.logEvtReceived)
},
showUpdateToast(versionData) {
var ignoreVersion = localStorage.getItem('ignoreVersion')
var latestVersion = versionData.latestVersion
if (!ignoreVersion || ignoreVersion !== latestVersion) {
this.$toast.info(`Update is available!\nCheck release notes for v${versionData.latestVersion}`, {
position: 'top-center',
toastClassName: 'cursor-pointer',
bodyClassName: 'custom-class-1',
timeout: 20000,
closeOnClick: false,
draggable: false,
hideProgressBar: false,
onClick: () => {
window.open(versionData.githubTagUrl, '_blank')
},
onClose: () => {
localStorage.setItem('ignoreVersion', versionData.latestVersion)
}
})
} else {
console.warn(`Update is available but user chose to dismiss it! v${versionData.latestVersion}`)
}
}
},
mounted() {
this.initializeSocket()
this.$store
.dispatch('checkForUpdate')
.then((res) => {
if (res && res.hasUpdate) this.showUpdateToast(res)
})
.catch((err) => console.error(err))
if (this.$route.query.error) {
this.$toast.error(this.$route.query.error)
this.$router.replace(this.$route.path)
}
}
}
</script>
<style>
.Vue-Toastification__toast-body.custom-class-1 {
font-size: 14px;
}
</style>