mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-01-01 08:39:39 +00:00
Update:Media item share endpoints and audio player #1768
- Add endpoints for getting tracks, getting cover image and updating progress - Implement share session cookie and caching share playback session - Audio player UI/UX
This commit is contained in:
parent
c1349e586a
commit
31146082f0
6 changed files with 229 additions and 33 deletions
|
|
@ -1,10 +1,14 @@
|
|||
<template>
|
||||
<div id="page-wrapper" class="w-full h-screen overflow-y-auto">
|
||||
<div id="page-wrapper" class="w-full h-screen max-h-screen overflow-hidden">
|
||||
<div class="w-full h-full flex items-center justify-center">
|
||||
<div class="w-full p-8">
|
||||
<p class="text-3xl font-semibold text-center mb-6">{{ mediaItemShare.playbackSession?.displayTitle || 'N/A' }}</p>
|
||||
<div class="w-full p-2 sm:p-4 md:p-8">
|
||||
<div :style="{ width: coverWidth + 'px', height: coverHeight + 'px' }" class="mx-auto overflow-hidden rounded-xl my-2">
|
||||
<img :src="coverUrl" class="object-contain w-full h-full" />
|
||||
</div>
|
||||
<p class="text-2xl md:text-3xl font-semibold text-center mb-1">{{ mediaItemShare.playbackSession.displayTitle || 'No title' }}</p>
|
||||
<p v-if="mediaItemShare.playbackSession.displayAuthor" class="text-xl text-slate-400 font-semibold text-center mb-1">{{ mediaItemShare.playbackSession.displayAuthor }}</p>
|
||||
|
||||
<div class="w-full py-8">
|
||||
<div class="w-full pt-16">
|
||||
<player-ui ref="audioPlayer" :chapters="chapters" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -36,12 +40,25 @@ export default {
|
|||
playerState: null,
|
||||
playInterval: null,
|
||||
hasLoaded: false,
|
||||
totalDuration: 0
|
||||
totalDuration: 0,
|
||||
windowWidth: 0,
|
||||
windowHeight: 0,
|
||||
listeningTimeSinceSync: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
playbackSession() {
|
||||
return this.mediaItemShare.playbackSession
|
||||
},
|
||||
coverUrl() {
|
||||
if (!this.playbackSession.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg`
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return `http://localhost:3333/public/share/${this.mediaItemShare.slug}/cover`
|
||||
}
|
||||
return `/public/share/${this.mediaItemShare.slug}/cover`
|
||||
},
|
||||
audioTracks() {
|
||||
return (this.mediaItemShare.playbackSession?.audioTracks || []).map((track) => {
|
||||
return (this.playbackSession.audioTracks || []).map((track) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
track.contentUrl = `${process.env.serverUrl}${track.contentUrl}`
|
||||
}
|
||||
|
|
@ -56,7 +73,24 @@ export default {
|
|||
return !this.isPlaying
|
||||
},
|
||||
chapters() {
|
||||
return this.mediaItemShare.playbackSession?.chapters || []
|
||||
return this.playbackSession.chapters || []
|
||||
},
|
||||
coverAspectRatio() {
|
||||
const coverAspectRatio = this.playbackSession.coverAspectRatio
|
||||
return coverAspectRatio === this.$constants.BookCoverAspectRatio.STANDARD ? 1.6 : 1
|
||||
},
|
||||
coverWidth() {
|
||||
const availableCoverWidth = Math.min(450, this.windowWidth - 32)
|
||||
const availableCoverHeight = Math.min(450, this.windowHeight - 250)
|
||||
|
||||
const mostCoverHeight = availableCoverWidth * this.coverAspectRatio
|
||||
if (mostCoverHeight > availableCoverHeight) {
|
||||
return availableCoverHeight / this.coverAspectRatio
|
||||
}
|
||||
return availableCoverWidth
|
||||
},
|
||||
coverHeight() {
|
||||
return this.coverWidth * this.coverAspectRatio
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -102,11 +136,29 @@ export default {
|
|||
this.$refs.audioPlayer.setDuration(this.totalDuration)
|
||||
}
|
||||
},
|
||||
sendProgressSync(currentTime) {
|
||||
console.log('Sending progress sync for time', currentTime)
|
||||
const progress = {
|
||||
currentTime
|
||||
}
|
||||
this.$axios.$patch(`/public/share/${this.mediaItemShare.slug}/progress`, progress, { progress: false }).catch((error) => {
|
||||
console.error('Failed to send progress sync', error)
|
||||
})
|
||||
},
|
||||
startPlayInterval() {
|
||||
let lastTick = Date.now()
|
||||
clearInterval(this.playInterval)
|
||||
this.playInterval = setInterval(() => {
|
||||
if (this.localAudioPlayer) {
|
||||
this.setCurrentTime(this.localAudioPlayer.getCurrentTime())
|
||||
if (!this.localAudioPlayer) return
|
||||
|
||||
const currentTime = this.localAudioPlayer.getCurrentTime()
|
||||
this.setCurrentTime(currentTime)
|
||||
const exactTimeElapsed = (Date.now() - lastTick) / 1000
|
||||
lastTick = Date.now()
|
||||
this.listeningTimeSinceSync += exactTimeElapsed
|
||||
if (this.listeningTimeSinceSync >= 30) {
|
||||
this.listeningTimeSinceSync = 0
|
||||
this.sendProgressSync(currentTime)
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
|
|
@ -115,7 +167,6 @@ export default {
|
|||
this.playInterval = null
|
||||
},
|
||||
playerStateChange(state) {
|
||||
console.log('Player state change', state)
|
||||
this.playerState = state
|
||||
if (state === 'LOADED' || state === 'PLAYING') {
|
||||
this.setDuration()
|
||||
|
|
@ -158,17 +209,28 @@ export default {
|
|||
this.$eventBus.$emit('player-hotkey', name)
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
resize() {
|
||||
this.windowWidth = window.innerWidth
|
||||
this.windowHeight = window.innerHeight
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.resize()
|
||||
window.addEventListener('resize', this.resize)
|
||||
window.addEventListener('keydown', this.keyDown)
|
||||
|
||||
console.log('Loaded media item share', this.mediaItemShare)
|
||||
this.localAudioPlayer.set(null, this.audioTracks, false, 0, false)
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('Loaded media item share', this.mediaItemShare)
|
||||
}
|
||||
|
||||
const startTime = this.playbackSession.currentTime || 0
|
||||
this.localAudioPlayer.set(null, this.audioTracks, false, startTime, false)
|
||||
this.localAudioPlayer.on('stateChange', this.playerStateChange.bind(this))
|
||||
this.localAudioPlayer.on('timeupdate', this.playerTimeUpdate.bind(this))
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.resize)
|
||||
window.removeEventListener('keydown', this.keyDown)
|
||||
|
||||
this.localAudioPlayer.off('stateChange', this.playerStateChange)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue