audiobookshelf/client/components/app/StreamContainer.vue

236 lines
8.1 KiB
Vue
Raw Normal View History

2021-08-18 00:01:11 +02:00
<template>
<div v-if="streamAudiobook" id="streamContainer" class="w-full fixed bottom-0 left-0 right-0 h-48 sm:h-44 md:h-40 z-40 bg-primary px-4 pb-4 pt-2">
<nuxt-link :to="`/audiobook/${streamAudiobook.id}`" class="absolute left-4 cursor-pointer" :style="{ top: bookCoverPosTop + 'px' }">
<covers-book-cover :audiobook="streamAudiobook" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</nuxt-link>
<div class="flex items-start pl-24 mb-6 md:mb-0">
2021-08-18 00:01:11 +02:00
<div>
<nuxt-link :to="`/audiobook/${streamAudiobook.id}`" class="hover:underline cursor-pointer text-base sm:text-lg">
2022-02-13 00:19:14 +01:00
{{ title }}
</nuxt-link>
<div class="text-gray-400 flex items-center">
<span class="material-icons text-sm">person</span>
<p v-if="authorFL" class="pl-1.5 text-sm sm:text-base">
<nuxt-link v-for="(author, index) in authorsList" :key="index" :to="`/library/${libraryId}/bookshelf?filter=authors.${$encode(author)}`" class="hover:underline">{{ author }}<span v-if="index < authorsList.length - 1">,&nbsp;</span></nuxt-link>
</p>
<p v-else class="text-sm sm:text-base cursor-pointer pl-2">Unknown</p>
</div>
<div class="text-gray-400 flex items-center">
<span class="material-icons text-xs">schedule</span>
<p class="font-mono text-sm pl-2 pb-px">{{ totalDurationPretty }}</p>
</div>
2021-08-18 00:01:11 +02:00
</div>
<div class="flex-grow" />
<span class="material-icons p-4 cursor-pointer" @click="closePlayer">close</span>
2021-08-18 00:01:11 +02:00
</div>
<audio-player ref="audioPlayer" :chapters="chapters" :paused="!isPlaying" :loading="playerLoading" :bookmarks="bookmarks" @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" @close="closePlayer" @showBookmarks="showBookmarks" />
2021-11-03 23:45:47 +01:00
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :audiobook-id="bookmarkAudiobookId" :current-time="bookmarkCurrentTime" @select="selectBookmark" />
2021-08-18 00:01:11 +02:00
</div>
</template>
<script>
import PlayerHandler from '@/players/PlayerHandler'
2021-08-18 00:01:11 +02:00
export default {
data() {
return {
playerHandler: new PlayerHandler(this),
totalDuration: 0,
showBookmarksModal: false,
bookmarkCurrentTime: 0,
bookmarkAudiobookId: null,
playerLoading: false,
isPlaying: false,
currentTime: 0
2021-08-18 00:01:11 +02:00
}
},
computed: {
coverAspectRatio() {
return this.$store.getters['getServerSetting']('coverAspectRatio')
},
bookCoverAspectRatio() {
return this.coverAspectRatio === this.$constants.BookCoverAspectRatio.SQUARE ? 1 : 1.6
},
bookCoverWidth() {
return 88
},
bookCoverPosTop() {
if (this.bookCoverAspectRatio === 1) return -10
return -64
},
2021-08-18 00:01:11 +02:00
cover() {
if (this.streamAudiobook && this.streamAudiobook.cover) return this.streamAudiobook.cover
return 'Logo.png'
},
user() {
return this.$store.state.user.user
2021-08-18 00:01:11 +02:00
},
userAudiobook() {
if (!this.audiobookId) return
return this.$store.getters['user/getUserAudiobook'](this.audiobookId)
},
userAudiobookCurrentTime() {
return this.userAudiobook ? this.userAudiobook.currentTime || 0 : 0
},
bookmarks() {
if (!this.userAudiobook) return []
2021-11-03 23:45:47 +01:00
return (this.userAudiobook.bookmarks || []).map((bm) => ({ ...bm })).sort((a, b) => a.time - b.time)
},
2021-08-18 00:01:11 +02:00
streamAudiobook() {
return this.$store.state.streamAudiobook
},
audiobookId() {
return this.streamAudiobook ? this.streamAudiobook.id : null
},
2021-08-18 00:01:11 +02:00
book() {
return this.streamAudiobook ? this.streamAudiobook.book || {} : {}
},
chapters() {
return this.streamAudiobook ? this.streamAudiobook.chapters || [] : []
},
2021-08-18 00:01:11 +02:00
title() {
return this.book.title || 'No Title'
},
author() {
return this.book.author || 'Unknown'
},
authorFL() {
return this.book.authorFL
},
authorsList() {
return this.authorFL ? this.authorFL.split(', ') : []
},
2021-10-06 04:10:49 +02:00
libraryId() {
return this.streamAudiobook ? this.streamAudiobook.libraryId : null
},
totalDurationPretty() {
return this.$secondsToTimestamp(this.totalDuration)
2021-08-18 00:01:11 +02:00
}
},
methods: {
playPause() {
this.playerHandler.playPause()
},
jumpForward() {
this.playerHandler.jumpForward()
},
jumpBackward() {
this.playerHandler.jumpBackward()
},
setVolume(volume) {
this.playerHandler.setVolume(volume)
},
setPlaybackRate(playbackRate) {
this.playerHandler.setPlaybackRate(playbackRate)
2021-08-18 00:01:11 +02:00
},
seek(time) {
this.playerHandler.seek(time)
2021-08-18 00:01:11 +02:00
},
setCurrentTime(time) {
this.currentTime = time
2021-08-18 00:01:11 +02:00
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setCurrentTime(time)
2021-08-18 00:01:11 +02:00
}
},
setDuration(duration) {
this.totalDuration = duration
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setDuration(duration)
2021-08-18 00:01:11 +02:00
}
},
setBufferTime(buffertime) {
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setBufferTime(buffertime)
}
2021-08-18 00:01:11 +02:00
},
showBookmarks(currentTime) {
this.bookmarkAudiobookId = this.audiobookId
this.bookmarkCurrentTime = currentTime
this.showBookmarksModal = true
},
selectBookmark(bookmark) {
this.seek(bookmark.time)
this.showBookmarksModal = false
},
closePlayer() {
this.playerHandler.closePlayer()
this.$store.commit('setStreamAudiobook', null)
},
2021-08-18 00:01:11 +02:00
streamProgress(data) {
if (!data.numSegments) return
var chunks = data.chunks
console.log(`[StreamContainer] Stream Progress ${data.percent}`)
2021-08-18 00:01:11 +02:00
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setChunksReady(chunks, data.numSegments)
} else {
console.error('No Audio Ref')
2021-08-18 00:01:11 +02:00
}
},
streamOpen(stream) {
this.$store.commit('setStreamAudiobook', stream.audiobook)
this.playerHandler.prepareStream(stream)
2021-08-18 00:01:11 +02:00
},
streamClosed(streamId) {
// Stream was closed from the server
if (this.playerHandler.isPlayingLocalAudiobook && this.playerHandler.currentStreamId === streamId) {
console.warn('[StreamContainer] Closing stream due to request from server')
this.playerHandler.closePlayer()
2021-08-18 00:01:11 +02:00
}
},
streamReady() {
console.log(`[STREAM-CONTAINER] Stream Ready`)
2021-08-18 00:01:11 +02:00
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setStreamReady()
} else {
console.error('No Audio Ref')
2021-08-18 00:01:11 +02:00
}
},
streamError(streamId) {
// Stream had critical error from the server
if (this.playerHandler.isPlayingLocalAudiobook && this.playerHandler.currentStreamId === streamId) {
console.warn('[StreamContainer] Closing stream due to stream error from server')
this.playerHandler.closePlayer()
}
},
2021-08-18 00:01:11 +02:00
streamReset({ startTime, streamId }) {
this.playerHandler.resetStream(startTime, streamId)
},
castSessionActive(isActive) {
if (isActive && this.playerHandler.isPlayingLocalAudiobook) {
// Cast session started switch to cast player
this.playerHandler.switchPlayer()
} else if (!isActive && this.playerHandler.isPlayingCastedAudiobook) {
// Cast session ended switch to local player
this.playerHandler.switchPlayer()
}
},
async playAudiobook(audiobookId) {
var audiobook = await this.$axios.$get(`/api/books/${audiobookId}`).catch((error) => {
console.error('Failed to fetch full audiobook', error)
return null
})
if (!audiobook) return
this.$store.commit('setStreamAudiobook', audiobook)
this.playerHandler.load(audiobook, true, this.userAudiobookCurrentTime)
2021-08-18 00:01:11 +02:00
}
},
mounted() {
this.$eventBus.$on('cast-session-active', this.castSessionActive)
this.$eventBus.$on('play-audiobook', this.playAudiobook)
},
beforeDestroy() {
this.$eventBus.$off('cast-session-active', this.castSessionActive)
this.$eventBus.$off('play-audiobook', this.playAudiobook)
2021-08-18 00:01:11 +02:00
}
}
</script>
<style>
#streamContainer {
box-shadow: 0px -6px 8px #1111113f;
}
</style>