mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-27 19:51:37 +00:00
merge upstream
This commit is contained in:
commit
61bf30e08a
89 changed files with 2963 additions and 1805 deletions
|
|
@ -15,6 +15,13 @@
|
|||
|
||||
<span v-if="showExperimentalFeatures" class="material-icons text-4xl text-warning pr-0 sm:pr-2 md:pr-4">logo_dev</span>
|
||||
|
||||
<ui-tooltip v-if="isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center">
|
||||
<span class="material-icons-outlined text-warning text-opacity-50"> cast </span>
|
||||
</ui-tooltip>
|
||||
<div v-if="isChromecastInitialized" class="w-6 h-6 mr-2 cursor-pointer">
|
||||
<google-cast-launcher></google-cast-launcher>
|
||||
</div>
|
||||
|
||||
<nuxt-link to="/config/stats" class="outline-none hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center mx-1">
|
||||
<span class="material-icons">equalizer</span>
|
||||
</nuxt-link>
|
||||
|
|
@ -39,10 +46,6 @@
|
|||
|
||||
<div v-show="numAudiobooksSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center">
|
||||
<h1 class="text-2xl px-4">{{ numAudiobooksSelected }} Selected</h1>
|
||||
<!-- <ui-btn v-show="!isHome" small class="text-sm mx-2" @click="toggleSelectAll"
|
||||
>{{ isAllSelected ? 'Select None' : 'Select All' }}<span class="pl-2">({{ entitiesLoaded }})</span></ui-btn
|
||||
> -->
|
||||
|
||||
<div class="flex-grow" />
|
||||
<ui-tooltip :text="`Mark as ${selectedIsRead ? 'Not Read' : 'Read'}`" direction="bottom">
|
||||
<ui-read-icon-btn :disabled="processingBatch" :is-read="selectedIsRead" @click="toggleBatchRead" class="mx-1.5" />
|
||||
|
|
@ -124,6 +127,15 @@ export default {
|
|||
},
|
||||
showExperimentalFeatures() {
|
||||
return this.$store.state.showExperimentalFeatures
|
||||
},
|
||||
isChromecastEnabled() {
|
||||
return this.$store.getters['getServerSetting']('chromecastEnabled')
|
||||
},
|
||||
isChromecastInitialized() {
|
||||
return this.$store.state.globals.isChromecastInitialized
|
||||
},
|
||||
isHttps() {
|
||||
return location.protocol === 'https:' || process.env.NODE_ENV === 'development'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -168,8 +168,7 @@ export default {
|
|||
})
|
||||
},
|
||||
startStream() {
|
||||
this.$store.commit('setStreamAudiobook', this.book)
|
||||
this.$root.socket.emit('open_stream', this.book.id)
|
||||
this.$eventBus.$emit('play-audiobook', this.book.id)
|
||||
},
|
||||
editClick() {
|
||||
this.$emit('edit', this.book)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@
|
|||
<div id="bookshelf" class="w-full overflow-y-auto">
|
||||
<template v-for="shelf in totalShelves">
|
||||
<div :key="shelf" :id="`shelf-${shelf - 1}`" class="w-full px-4 sm:px-8 relative" :class="{ bookshelfRow: !isAlternativeBookshelfView }" :style="{ height: shelfHeight + 'px' }">
|
||||
<!-- <div class="absolute top-0 left-0 bottom-0 p-4 z-10">
|
||||
<p class="text-white text-2xl">{{ shelf }}</p>
|
||||
</div> -->
|
||||
<div v-if="!isAlternativeBookshelfView" class="bookshelfDivider w-full absolute bottom-0 left-0 right-0 z-20" :class="`h-${shelfDividerHeightIndex}`" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -26,7 +23,9 @@
|
|||
<widgets-cover-size-widget class="fixed bottom-4 right-4 z-30" />
|
||||
<!-- Experimental Bookshelf Texture -->
|
||||
<div v-show="showExperimentalFeatures" class="fixed bottom-4 right-28 z-40">
|
||||
<div class="rounded-full py-1 bg-primary hover:bg-bg cursor-pointer px-2 border border-black-100 text-center flex items-center box-shadow-md" @mousedown.prevent @mouseup.prevent @click="showBookshelfTextureModal"><p class="text-sm py-0.5">Texture</p></div>
|
||||
<div class="rounded-full py-1 bg-primary hover:bg-bg cursor-pointer px-2 border border-black-100 text-center flex items-center box-shadow-md" @mousedown.prevent @mouseup.prevent @click="showBookshelfTextureModal">
|
||||
<p class="text-sm py-0.5">Texture</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -114,6 +113,9 @@ export default {
|
|||
bookshelfView() {
|
||||
return this.$store.getters['settings/getServerSetting']('bookshelfView')
|
||||
},
|
||||
sortingIgnorePrefix() {
|
||||
return this.$store.getters['getServerSetting']('sortingIgnorePrefix')
|
||||
},
|
||||
isCoverSquareAspectRatio() {
|
||||
return this.coverAspectRatio === this.$constants.BookCoverAspectRatio.SQUARE
|
||||
},
|
||||
|
|
@ -245,7 +247,7 @@ export default {
|
|||
console.error('failed to fetch books', error)
|
||||
return null
|
||||
})
|
||||
console.log('payload', payload)
|
||||
|
||||
this.isFetchingEntities = false
|
||||
if (this.pendingReset) {
|
||||
this.pendingReset = false
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="flex items-start pl-24 mb-6 md:mb-0">
|
||||
<div>
|
||||
<nuxt-link :to="`/audiobook/${streamAudiobook.id}`" class="hover:underline cursor-pointer text-base sm:text-lg">
|
||||
{{ title }} <span v-if="stream && $isDev" class="text-xs text-gray-400">({{ stream.id }})</span>
|
||||
{{ title }}
|
||||
</nuxt-link>
|
||||
<div class="text-gray-400 flex items-center">
|
||||
<span class="material-icons text-sm">person</span>
|
||||
|
|
@ -22,26 +22,29 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
<span v-if="stream" class="material-icons p-4 cursor-pointer" @click="cancelStream">close</span>
|
||||
<span class="material-icons p-4 cursor-pointer" @click="closePlayer">close</span>
|
||||
</div>
|
||||
|
||||
<audio-player ref="audioPlayer" :stream-id="streamId" :audiobook-id="audiobookId" :chapters="chapters" :loading="isLoading" :bookmarks="bookmarks" @close="cancelStream" @loaded="(d) => (totalDuration = d)" @showBookmarks="showBookmarks" @sync="sendStreamSync" @hook:mounted="audioPlayerMounted" />
|
||||
<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" />
|
||||
|
||||
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :audiobook-id="bookmarkAudiobookId" :current-time="bookmarkCurrentTime" @select="selectBookmark" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayerHandler from '@/players/PlayerHandler'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
audioPlayerReady: false,
|
||||
lastServerUpdateSentSeconds: 0,
|
||||
stream: null,
|
||||
playerHandler: new PlayerHandler(this),
|
||||
totalDuration: 0,
|
||||
showBookmarksModal: false,
|
||||
bookmarkCurrentTime: 0,
|
||||
bookmarkAudiobookId: null
|
||||
bookmarkAudiobookId: null,
|
||||
playerLoading: false,
|
||||
isPlaying: false,
|
||||
currentTime: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -69,18 +72,13 @@ export default {
|
|||
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 []
|
||||
return (this.userAudiobook.bookmarks || []).map((bm) => ({ ...bm })).sort((a, b) => a.time - b.time)
|
||||
},
|
||||
isLoading() {
|
||||
if (!this.streamAudiobook) return false
|
||||
if (this.stream) {
|
||||
// IF Stream exists, set loading if stream is diff from next stream
|
||||
return this.stream.audiobook.id !== this.streamAudiobook.id
|
||||
}
|
||||
return true
|
||||
},
|
||||
streamAudiobook() {
|
||||
return this.$store.state.streamAudiobook
|
||||
},
|
||||
|
|
@ -105,12 +103,6 @@ export default {
|
|||
authorsList() {
|
||||
return this.authorFL ? this.authorFL.split(', ') : []
|
||||
},
|
||||
streamId() {
|
||||
return this.stream ? this.stream.id : null
|
||||
},
|
||||
playlistUrl() {
|
||||
return this.stream ? this.stream.clientPlaylistUri : null
|
||||
},
|
||||
libraryId() {
|
||||
return this.streamAudiobook ? this.streamAudiobook.libraryId : null
|
||||
},
|
||||
|
|
@ -119,8 +111,40 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
addListeningTime(time) {
|
||||
console.log('Send listening time to server', time)
|
||||
playPause() {
|
||||
this.playerHandler.playPause()
|
||||
},
|
||||
jumpForward() {
|
||||
this.playerHandler.jumpForward()
|
||||
},
|
||||
jumpBackward() {
|
||||
this.playerHandler.jumpBackward()
|
||||
},
|
||||
setVolume(volume) {
|
||||
this.playerHandler.setVolume(volume)
|
||||
},
|
||||
setPlaybackRate(playbackRate) {
|
||||
this.playerHandler.setPlaybackRate(playbackRate)
|
||||
},
|
||||
seek(time) {
|
||||
this.playerHandler.seek(time)
|
||||
},
|
||||
setCurrentTime(time) {
|
||||
this.currentTime = time
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.setCurrentTime(time)
|
||||
}
|
||||
},
|
||||
setDuration(duration) {
|
||||
this.totalDuration = duration
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.setDuration(duration)
|
||||
}
|
||||
},
|
||||
setBufferTime(buffertime) {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.setBufferTime(buffertime)
|
||||
}
|
||||
},
|
||||
showBookmarks(currentTime) {
|
||||
this.bookmarkAudiobookId = this.audiobookId
|
||||
|
|
@ -128,47 +152,12 @@ export default {
|
|||
this.showBookmarksModal = true
|
||||
},
|
||||
selectBookmark(bookmark) {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.selectBookmark(bookmark)
|
||||
}
|
||||
this.seek(bookmark.time)
|
||||
this.showBookmarksModal = false
|
||||
},
|
||||
filterByAuthor() {
|
||||
if (this.$route.name !== 'index') {
|
||||
this.$router.push(`/library/${this.libraryId || this.$store.state.libraries.currentLibraryId}/bookshelf`)
|
||||
}
|
||||
var settingsUpdate = {
|
||||
filterBy: `authors.${this.$encode(this.author)}`
|
||||
}
|
||||
this.$store.dispatch('user/updateUserSettings', settingsUpdate)
|
||||
},
|
||||
audioPlayerMounted() {
|
||||
this.audioPlayerReady = true
|
||||
if (this.stream) {
|
||||
console.log('[STREAM-CONTAINER] audioPlayer Mounted w/ Stream', this.stream)
|
||||
this.openStream()
|
||||
}
|
||||
},
|
||||
cancelStream() {
|
||||
this.$root.socket.emit('close_stream')
|
||||
},
|
||||
terminateStream() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.terminateStream()
|
||||
}
|
||||
},
|
||||
openStream() {
|
||||
var playOnLoad = this.$store.state.playOnLoad
|
||||
console.log(`[StreamContainer] openStream PlayOnLoad`, playOnLoad)
|
||||
if (!this.$refs.audioPlayer) {
|
||||
console.error('NO Audio Player')
|
||||
return
|
||||
}
|
||||
var currentTime = this.stream.clientCurrentTime || 0
|
||||
this.$refs.audioPlayer.set(this.playlistUrl, currentTime, playOnLoad)
|
||||
if (this.stream.isTranscodeComplete) {
|
||||
this.$refs.audioPlayer.setStreamReady()
|
||||
}
|
||||
closePlayer() {
|
||||
this.playerHandler.closePlayer()
|
||||
this.$store.commit('setStreamAudiobook', null)
|
||||
},
|
||||
streamProgress(data) {
|
||||
if (!data.numSegments) return
|
||||
|
|
@ -181,21 +170,14 @@ export default {
|
|||
}
|
||||
},
|
||||
streamOpen(stream) {
|
||||
this.stream = stream
|
||||
this.$store.commit('updateStreamAudiobook', stream.audiobook)
|
||||
|
||||
if (this.$refs.audioPlayer) {
|
||||
console.log('[StreamContainer] streamOpen', stream)
|
||||
this.openStream()
|
||||
} else if (this.audioPlayerReady) {
|
||||
console.error('No Audio Ref')
|
||||
}
|
||||
this.$store.commit('setStreamAudiobook', stream.audiobook)
|
||||
this.playerHandler.prepareStream(stream)
|
||||
},
|
||||
streamClosed(streamId) {
|
||||
if (this.stream && (this.stream.id === streamId || streamId === 'n/a')) {
|
||||
this.terminateStream()
|
||||
this.$store.commit('clearStreamAudiobook', this.stream.audiobook.id)
|
||||
this.stream = null
|
||||
// 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()
|
||||
}
|
||||
},
|
||||
streamReady() {
|
||||
|
|
@ -207,41 +189,42 @@ export default {
|
|||
}
|
||||
},
|
||||
streamError(streamId) {
|
||||
if (this.stream && (this.stream.id === streamId || streamId === 'n/a')) {
|
||||
this.terminateStream()
|
||||
this.$store.commit('clearStreamAudiobook', this.stream.audiobook.id)
|
||||
this.stream = null
|
||||
// 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()
|
||||
}
|
||||
},
|
||||
sendStreamSync(syncData) {
|
||||
var diff = syncData.currentTime - this.lastServerUpdateSentSeconds
|
||||
if (Math.abs(diff) < 1 && !syncData.timeListened) {
|
||||
// No need to sync
|
||||
return
|
||||
}
|
||||
this.$root.socket.emit('stream_sync', syncData)
|
||||
},
|
||||
// updateTime(currentTime) {
|
||||
// var diff = currentTime - this.lastServerUpdateSentSeconds
|
||||
// if (diff > 4 || diff < 0) {
|
||||
// this.lastServerUpdateSentSeconds = currentTime
|
||||
// var updatePayload = {
|
||||
// currentTime,
|
||||
// streamId: this.streamId
|
||||
// }
|
||||
// this.$root.socket.emit('stream_update', updatePayload)
|
||||
// }
|
||||
// },
|
||||
streamReset({ startTime, streamId }) {
|
||||
if (streamId !== this.streamId) {
|
||||
console.error('resetStream StreamId Mismatch', streamId, this.streamId)
|
||||
return
|
||||
}
|
||||
if (this.$refs.audioPlayer) {
|
||||
console.log(`[STREAM-CONTAINER] streamReset Received for time ${startTime}`)
|
||||
this.$refs.audioPlayer.resetStream(startTime)
|
||||
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)
|
||||
}
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue