mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-02 00:54:30 +02:00
Offline support, downloading, syncing progress
This commit is contained in:
parent
e97218f2e8
commit
a412c9d359
37 changed files with 2836 additions and 201 deletions
|
@ -1,21 +1,21 @@
|
|||
<template>
|
||||
<div class="w-full p-4 pointer-events-none fixed bottom-0 left-0 right-0 z-20">
|
||||
<div v-if="streamAudiobook" class="w-full bg-primary absolute bottom-0 left-0 right-0 z-50 p-2 pointer-events-auto" @click.stop @mousedown.stop @mouseup.stop>
|
||||
<div v-if="audiobook" class="w-full bg-primary absolute bottom-0 left-0 right-0 z-50 p-2 pointer-events-auto" @click.stop @mousedown.stop @mouseup.stop>
|
||||
<div class="pl-16 pr-2 flex items-center pb-2">
|
||||
<div>
|
||||
<p class="px-2">{{ title }}</p>
|
||||
<p class="px-2 text-xs text-gray-400">by {{ author }}</p>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
<div class="cursor-pointer flex items-center justify-center mr-6 w-6 text-center" :class="chapters.length ? 'text-gray-300' : 'text-gray-400'" @mousedown.prevent @mouseup.prevent @click="clickChapterBtn">
|
||||
<div v-if="chapters.length" class="cursor-pointer flex items-center justify-center mr-6 w-6 text-center" :class="chapters.length ? 'text-gray-300' : 'text-gray-400'" @mousedown.prevent @mouseup.prevent @click="clickChapterBtn">
|
||||
<span class="material-icons text-2xl">format_list_bulleted</span>
|
||||
</div>
|
||||
<span class="material-icons" @click="cancelStream">close</span>
|
||||
</div>
|
||||
<div class="absolute left-2 -top-10">
|
||||
<cards-book-cover :audiobook="streamAudiobook" :width="64" />
|
||||
<cards-book-cover :audiobook="audiobook" :width="64" />
|
||||
</div>
|
||||
<audio-player-mini ref="audioPlayerMini" :loading="!stream || currStreamAudiobookId !== streamAudiobookId" @updateTime="updateTime" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @hook:mounted="audioPlayerMounted" />
|
||||
<audio-player-mini ref="audioPlayerMini" :loading="isLoading" @updateTime="updateTime" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @hook:mounted="audioPlayerMounted" />
|
||||
</div>
|
||||
<modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-speed.sync="playbackSpeed" @change="changePlaybackSpeed" />
|
||||
<modals-chapters-modal v-model="showChapterModal" :chapters="chapters" @select="selectChapter" />
|
||||
|
@ -30,24 +30,62 @@ export default {
|
|||
return {
|
||||
audioPlayerReady: false,
|
||||
stream: null,
|
||||
lastServerUpdateSentSeconds: 0,
|
||||
download: null,
|
||||
lastProgressTimeUpdate: 0,
|
||||
showPlaybackSpeedModal: false,
|
||||
playbackSpeed: 1,
|
||||
showChapterModal: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// playingDownload: {
|
||||
// handler(newVal, oldVal) {
|
||||
// console.log('[StreamContainer] Download AUDIOBOOK Changed ' + newVal + '|' + oldVal)
|
||||
// if (newVal) {
|
||||
// if (this.audioPlayerReady) {
|
||||
// this.playDownload()
|
||||
// }
|
||||
// } else if (this.download) {
|
||||
// this.download = null
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// streamAudiobook(newVal, oldval) {
|
||||
// console.log('[StreamContainer] Stream AUDIOBOOK Changed ' + newVal + '|' + oldVal)
|
||||
// }
|
||||
},
|
||||
watch: {
|
||||
socketConnected(newVal) {
|
||||
if (newVal) {
|
||||
console.log('Socket Connected set listeners')
|
||||
this.setListeners()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
socketConnected() {
|
||||
return this.$store.state.socketConnected
|
||||
},
|
||||
isLoading() {
|
||||
if (this.playingDownload) return false
|
||||
if (!this.streamAudiobook) return false
|
||||
return !this.stream || this.streamAudiobook.id !== this.stream.audiobook.id
|
||||
},
|
||||
playingDownload() {
|
||||
return this.$store.state.playingDownload
|
||||
},
|
||||
audiobook() {
|
||||
if (this.playingDownload) return this.playingDownload.audiobook
|
||||
return this.streamAudiobook
|
||||
},
|
||||
audiobookId() {
|
||||
return this.audiobook ? this.audiobook.id : null
|
||||
},
|
||||
streamAudiobook() {
|
||||
return this.$store.state.streamAudiobook
|
||||
},
|
||||
streamAudiobookId() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.id : null
|
||||
},
|
||||
currStreamAudiobookId() {
|
||||
return this.stream ? this.stream.audiobook.id : null
|
||||
},
|
||||
book() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.book || {} : {}
|
||||
return this.audiobook ? this.audiobook.book || {} : {}
|
||||
},
|
||||
title() {
|
||||
return this.book ? this.book.title : ''
|
||||
|
@ -62,7 +100,7 @@ export default {
|
|||
return this.book ? this.book.series : ''
|
||||
},
|
||||
chapters() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.chapters || [] : []
|
||||
return this.audiobook ? this.audiobook.chapters || [] : []
|
||||
},
|
||||
volumeNumber() {
|
||||
return this.book ? this.book.volumeNumber : ''
|
||||
|
@ -73,7 +111,7 @@ export default {
|
|||
return `${this.series} #${this.volumeNumber}`
|
||||
},
|
||||
duration() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.duration || 0 : 0
|
||||
return this.audiobook ? this.audiobook.duration || 0 : 0
|
||||
},
|
||||
coverForNative() {
|
||||
if (!this.cover) {
|
||||
|
@ -100,27 +138,61 @@ export default {
|
|||
this.showChapterModal = false
|
||||
},
|
||||
async cancelStream() {
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: 'Cancel this stream?'
|
||||
})
|
||||
if (value) {
|
||||
this.$server.socket.emit('close_stream')
|
||||
this.$store.commit('setStreamAudiobook', null)
|
||||
if (this.download) {
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
this.$refs.audioPlayerMini.terminateStream()
|
||||
}
|
||||
this.$store.commit('setPlayingDownload', null)
|
||||
|
||||
this.$localStore.setCurrent(null)
|
||||
} else {
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: 'Cancel this stream?'
|
||||
})
|
||||
if (value) {
|
||||
this.$server.socket.emit('close_stream')
|
||||
this.$store.commit('setStreamAudiobook', null)
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
this.$refs.audioPlayerMini.terminateStream()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateTime(currentTime) {
|
||||
var diff = currentTime - this.lastServerUpdateSentSeconds
|
||||
var diff = currentTime - this.lastProgressTimeUpdate
|
||||
|
||||
if (diff > 4 || diff < 0) {
|
||||
this.lastServerUpdateSentSeconds = currentTime
|
||||
var updatePayload = {
|
||||
currentTime,
|
||||
streamId: this.stream.id
|
||||
this.lastProgressTimeUpdate = currentTime
|
||||
if (this.stream) {
|
||||
var updatePayload = {
|
||||
currentTime,
|
||||
streamId: this.stream.id
|
||||
}
|
||||
this.$server.socket.emit('stream_update', updatePayload)
|
||||
} else if (this.download) {
|
||||
var progressUpdate = {
|
||||
audiobookId: this.download.id,
|
||||
currentTime: currentTime,
|
||||
totalDuration: this.download.audiobook.duration,
|
||||
progress: Number((currentTime / this.download.audiobook.duration).toFixed(3)),
|
||||
lastUpdate: Date.now(),
|
||||
isRead: false
|
||||
}
|
||||
|
||||
if (this.$server.connected) {
|
||||
// var updateObj = {
|
||||
// audiobookId: this.download.id,
|
||||
// totalDuration: this.download.audiobook.duration,
|
||||
// clientCurrentTime: currentTime,
|
||||
// clientProgress: Number((currentTime / this.download.audiobook.duration).toFixed(3))
|
||||
// }
|
||||
this.$server.socket.emit('progress_update', progressUpdate)
|
||||
}
|
||||
this.$localStore.updateUserAudiobookProgress(progressUpdate).then(() => {
|
||||
console.log('Updated user audiobook progress', currentTime)
|
||||
})
|
||||
}
|
||||
this.$server.socket.emit('stream_update', updatePayload)
|
||||
}
|
||||
},
|
||||
closeStream() {},
|
||||
|
@ -151,8 +223,67 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
async getDownloadStartTime() {
|
||||
var userAudiobook = await this.$localStore.getMostRecentUserAudiobook(this.audiobookId)
|
||||
if (!userAudiobook) {
|
||||
console.log('[StreamContainer] getDownloadStartTime no user audiobook record found')
|
||||
return 0
|
||||
}
|
||||
return userAudiobook.currentTime
|
||||
},
|
||||
async playDownload() {
|
||||
if (this.stream) {
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
this.$refs.audioPlayerMini.terminateStream()
|
||||
}
|
||||
this.stream = null
|
||||
}
|
||||
|
||||
this.lastProgressTimeUpdate = 0
|
||||
console.log('[StreamContainer] Playing local', this.playingDownload)
|
||||
if (!this.$refs.audioPlayerMini) {
|
||||
console.error('No Audio Player Mini')
|
||||
return
|
||||
}
|
||||
|
||||
var playOnLoad = this.$store.state.playOnLoad
|
||||
if (playOnLoad) this.$store.commit('setPlayOnLoad', false)
|
||||
|
||||
var currentTime = await this.getDownloadStartTime()
|
||||
if (isNaN(currentTime) || currentTime === null) currentTime = 0
|
||||
|
||||
// Update local current time
|
||||
this.$localStore.setCurrent({
|
||||
audiobookId: this.download.id,
|
||||
lastUpdate: Date.now()
|
||||
})
|
||||
|
||||
var audiobookStreamData = {
|
||||
title: this.title,
|
||||
author: this.author,
|
||||
playWhenReady: !!playOnLoad,
|
||||
startTime: String(Math.floor(currentTime * 1000)),
|
||||
playbackSpeed: this.playbackSpeed || 1,
|
||||
cover: this.download.coverUrl || null,
|
||||
duration: String(Math.floor(this.duration * 1000)),
|
||||
series: this.seriesTxt,
|
||||
token: this.$store.getters['user/getToken'],
|
||||
contentUrl: this.playingDownload.contentUrl,
|
||||
isLocal: true
|
||||
}
|
||||
|
||||
this.$refs.audioPlayerMini.set(audiobookStreamData)
|
||||
},
|
||||
streamOpen(stream) {
|
||||
console.log('[StreamContainer] Stream Open', stream)
|
||||
if (this.download) {
|
||||
if (this.$refs.audioPlayerMini) {
|
||||
this.$refs.audioPlayerMini.terminateStream()
|
||||
}
|
||||
}
|
||||
|
||||
this.lastProgressTimeUpdate = 0
|
||||
console.log('[StreamContainer] Stream Open: ' + this.title)
|
||||
|
||||
if (!this.$refs.audioPlayerMini) {
|
||||
console.error('No Audio Player Mini')
|
||||
return
|
||||
|
@ -160,6 +291,9 @@ export default {
|
|||
|
||||
this.stream = stream
|
||||
|
||||
// Update local remove current
|
||||
this.$localStore.setCurrent(null)
|
||||
|
||||
var playlistUrl = stream.clientPlaylistUri
|
||||
var currentTime = stream.clientCurrentTime || 0
|
||||
var playOnLoad = this.$store.state.playOnLoad
|
||||
|
@ -177,13 +311,17 @@ export default {
|
|||
playlistUrl: this.$server.url + playlistUrl,
|
||||
token: this.$store.getters['user/getToken']
|
||||
}
|
||||
|
||||
this.$refs.audioPlayerMini.set(audiobookStreamData)
|
||||
},
|
||||
audioPlayerMounted() {
|
||||
console.log('Audio Player Mounted', this.$server.stream)
|
||||
this.audioPlayerReady = true
|
||||
if (this.$server.stream) {
|
||||
|
||||
if (this.playingDownload) {
|
||||
console.log('[StreamContainer] Play download on audio mount')
|
||||
this.playDownload()
|
||||
} else if (this.$server.stream) {
|
||||
console.log('[StreamContainer] Open stream on audio mount')
|
||||
this.streamOpen(this.$server.stream)
|
||||
}
|
||||
},
|
||||
|
@ -206,6 +344,26 @@ export default {
|
|||
this.playbackSpeed = settings.playbackRate
|
||||
this.$refs.audioPlayerMini.updatePlaybackRate()
|
||||
}
|
||||
},
|
||||
streamUpdated(type, data) {
|
||||
if (type === 'download') {
|
||||
if (data) {
|
||||
console.log('START DOWNLOAD PLAY')
|
||||
this.download = { ...data }
|
||||
if (this.audioPlayerReady) {
|
||||
this.playDownload()
|
||||
}
|
||||
} else if (this.download) {
|
||||
console.log('STOP DOWNLOAD PLAY')
|
||||
this.cancelStream()
|
||||
}
|
||||
} else {
|
||||
if (data) {
|
||||
console.log('STARTING STREAM')
|
||||
} else {
|
||||
console.log('STOPPING STREAM')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -213,15 +371,19 @@ export default {
|
|||
|
||||
this.setListeners()
|
||||
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
|
||||
this.$store.commit('setStreamListener', this.streamUpdated)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$server.socket.off('stream_open', this.streamOpen)
|
||||
this.$server.socket.off('stream_closed', this.streamClosed)
|
||||
this.$server.socket.off('stream_progress', this.streamProgress)
|
||||
this.$server.socket.off('stream_ready', this.streamReady)
|
||||
this.$server.socket.off('stream_reset', this.streamReset)
|
||||
if (this.$server.socket) {
|
||||
this.$server.socket.off('stream_open', this.streamOpen)
|
||||
this.$server.socket.off('stream_closed', this.streamClosed)
|
||||
this.$server.socket.off('stream_progress', this.streamProgress)
|
||||
this.$server.socket.off('stream_ready', this.streamReady)
|
||||
this.$server.socket.off('stream_reset', this.streamReset)
|
||||
}
|
||||
|
||||
this.$store.commit('user/removeSettingsListener', 'streamContainer')
|
||||
this.$store.commit('removeStreamListener')
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue