Add:Bookmarks icon btn on library item page and ability to open player at specified time #796

This commit is contained in:
advplyr 2022-07-29 19:06:52 -05:00
parent 45cd39ac0c
commit a8c7905f6d
5 changed files with 64 additions and 24 deletions

View File

@ -381,7 +381,7 @@ export default {
if (this.$refs.audioPlayer) this.$refs.audioPlayer.checkUpdateChapterTrack() if (this.$refs.audioPlayer) this.$refs.audioPlayer.checkUpdateChapterTrack()
}) })
this.playerHandler.load(libraryItem, episodeId, true, this.initialPlaybackRate) this.playerHandler.load(libraryItem, episodeId, true, this.initialPlaybackRate, payload.startTime)
}, },
pauseItem() { pauseItem() {
this.playerHandler.pause() this.playerHandler.pause()
@ -393,11 +393,13 @@ export default {
}, },
mounted() { mounted() {
this.$eventBus.$on('cast-session-active', this.castSessionActive) this.$eventBus.$on('cast-session-active', this.castSessionActive)
this.$eventBus.$on('playback-seek', this.seek)
this.$eventBus.$on('play-item', this.playLibraryItem) this.$eventBus.$on('play-item', this.playLibraryItem)
this.$eventBus.$on('pause-item', this.pauseItem) this.$eventBus.$on('pause-item', this.pauseItem)
}, },
beforeDestroy() { beforeDestroy() {
this.$eventBus.$off('cast-session-active', this.castSessionActive) this.$eventBus.$off('cast-session-active', this.castSessionActive)
this.$eventBus.$off('playback-seek', this.seek)
this.$eventBus.$off('play-item', this.playLibraryItem) this.$eventBus.$off('play-item', this.playLibraryItem)
this.$eventBus.$off('pause-item', this.pauseItem) this.$eventBus.$off('pause-item', this.pauseItem)
} }

View File

@ -1,6 +1,11 @@
<template> <template>
<modals-modal v-model="show" name="bookmarks" :width="500" :height="'unset'"> <modals-modal v-model="show" name="bookmarks" :width="500" :height="'unset'">
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh"> <template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
<p class="font-book text-3xl text-white truncate">Your Bookmarks</p>
</div>
</template>
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="show" class="w-full h-full"> <div v-if="show" class="w-full h-full">
<template v-for="bookmark in bookmarks"> <template v-for="bookmark in bookmarks">
<modals-bookmarks-bookmark-item :key="bookmark.id" :highlight="currentTime === bookmark.time" :bookmark="bookmark" @click="clickBookmark" @update="submitUpdateBookmark" @delete="deleteBookmark" /> <modals-bookmarks-bookmark-item :key="bookmark.id" :highlight="currentTime === bookmark.time" :bookmark="bookmark" @click="clickBookmark" @update="submitUpdateBookmark" @delete="deleteBookmark" />
@ -8,8 +13,8 @@
<div v-if="!bookmarks.length" class="flex h-32 items-center justify-center"> <div v-if="!bookmarks.length" class="flex h-32 items-center justify-center">
<p class="text-xl">No Bookmarks</p> <p class="text-xl">No Bookmarks</p>
</div> </div>
<div class="w-full h-px bg-white bg-opacity-10" /> <div v-if="!hideCreate" class="w-full h-px bg-white bg-opacity-10" />
<form @submit.prevent="submitCreateBookmark"> <form v-if="!hideCreate" @submit.prevent="submitCreateBookmark">
<div v-show="canCreateBookmark" class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80"> <div v-show="canCreateBookmark" class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
<div class="w-16 max-w-16 text-center"> <div class="w-16 max-w-16 text-center">
<p class="text-sm font-mono text-gray-400"> <p class="text-sm font-mono text-gray-400">
@ -39,7 +44,8 @@ export default {
type: Number, type: Number,
default: 0 default: 0
}, },
libraryItemId: String libraryItemId: String,
hideCreate: Boolean
}, },
data() { data() {
return { return {

View File

@ -1,6 +1,5 @@
<template> <template>
<div class="flex items-center px-4 py-4 justify-start relative hover:bg-bg" :class="wrapperClass" @click="click" @mouseover="mouseover" @mouseleave="mouseleave"> <div class="flex items-center px-4 py-4 justify-start relative bg-primary hover:bg-opacity-25" :class="wrapperClass" @click="click" @mouseover="mouseover" @mouseleave="mouseleave">
<!-- <span class="material-icons" :class="highlight ? 'text-success' : 'text-white text-opacity-80'">{{ highlight ? 'bookmark' : 'bookmark_border' }}</span> -->
<div class="w-16 max-w-16 text-center"> <div class="w-16 max-w-16 text-center">
<p class="text-sm font-mono text-gray-400"> <p class="text-sm font-mono text-gray-400">
{{ this.$secondsToTimestamp(bookmark.time) }} {{ this.$secondsToTimestamp(bookmark.time) }}

View File

@ -11,7 +11,7 @@
<!-- Item Cover Overlay --> <!-- Item Cover Overlay -->
<div class="absolute top-0 left-0 w-full h-full z-10 bg-black bg-opacity-30 opacity-0 hover:opacity-100 transition-opacity" @mousedown.prevent @mouseup.prevent> <div class="absolute top-0 left-0 w-full h-full z-10 bg-black bg-opacity-30 opacity-0 hover:opacity-100 transition-opacity" @mousedown.prevent @mouseup.prevent>
<div v-show="showPlayButton && !streaming" class="h-full flex items-center justify-center pointer-events-none"> <div v-show="showPlayButton && !isStreaming" class="h-full flex items-center justify-center pointer-events-none">
<div class="hover:text-white text-gray-200 hover:scale-110 transform duration-200 pointer-events-auto cursor-pointer" @click.stop.prevent="startStream"> <div class="hover:text-white text-gray-200 hover:scale-110 transform duration-200 pointer-events-auto cursor-pointer" @click.stop.prevent="startStream">
<span class="material-icons text-4xl">play_circle_filled</span> <span class="material-icons text-4xl">play_circle_filled</span>
</div> </div>
@ -129,12 +129,12 @@
<!-- Icon buttons --> <!-- Icon buttons -->
<div class="flex items-center justify-center md:justify-start pt-4"> <div class="flex items-center justify-center md:justify-start pt-4">
<ui-btn v-if="showPlayButton" :disabled="streaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="startStream"> <ui-btn v-if="showPlayButton" :disabled="isStreaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="startStream">
<span v-show="!streaming" class="material-icons -ml-2 pr-1 text-white">play_arrow</span> <span v-show="!isStreaming" class="material-icons -ml-2 pr-1 text-white">play_arrow</span>
{{ streaming ? 'Playing' : 'Play' }} {{ isStreaming ? 'Playing' : 'Play' }}
</ui-btn> </ui-btn>
<ui-btn v-else-if="isMissing || isInvalid" color="error" :padding-x="4" small class="flex items-center h-9 mr-2"> <ui-btn v-else-if="isMissing || isInvalid" color="error" :padding-x="4" small class="flex items-center h-9 mr-2">
<span v-show="!streaming" class="material-icons -ml-2 pr-1 text-white">error</span> <span v-show="!isStreaming" class="material-icons -ml-2 pr-1 text-white">error</span>
{{ isMissing ? 'Missing' : 'Incomplete' }} {{ isMissing ? 'Missing' : 'Incomplete' }}
</ui-btn> </ui-btn>
@ -160,7 +160,11 @@
<ui-icon-btn icon="search" class="mx-0.5" :loading="fetchingRSSFeed" outlined @click="findEpisodesClick" /> <ui-icon-btn icon="search" class="mx-0.5" :loading="fetchingRSSFeed" outlined @click="findEpisodesClick" />
</ui-tooltip> </ui-tooltip>
<!-- Experimental RSS feed open --> <ui-tooltip v-if="bookmarks.length" text="Your Bookmarks" direction="top">
<ui-icon-btn :icon="bookmarks.length ? 'bookmarks' : 'bookmark_border'" class="mx-0.5" @click="clickBookmarksBtn" />
</ui-tooltip>
<!-- RSS feed -->
<ui-tooltip v-if="showRssFeedBtn" text="Open RSS Feed" direction="top"> <ui-tooltip v-if="showRssFeedBtn" text="Open RSS Feed" direction="top">
<ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeedUrl ? 'success' : 'primary'" outlined @click="clickRSSFeed" /> <ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeedUrl ? 'success' : 'primary'" outlined @click="clickRSSFeed" />
</ui-tooltip> </ui-tooltip>
@ -189,6 +193,7 @@
<modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" /> <modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" />
<modals-rssfeed-view-modal v-model="showRssFeedModal" :library-item="libraryItem" :feed-url="rssFeedUrl" /> <modals-rssfeed-view-modal v-model="showRssFeedModal" :library-item="libraryItem" :feed-url="rssFeedUrl" />
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :library-item-id="libraryItemId" hide-create @select="selectBookmark" />
</div> </div>
</template> </template>
@ -222,7 +227,8 @@ export default {
podcastFeedEpisodes: [], podcastFeedEpisodes: [],
episodesDownloading: [], episodesDownloading: [],
episodeDownloadsQueued: [], episodeDownloadsQueued: [],
showRssFeedModal: false showRssFeedModal: false,
showBookmarksModal: false
} }
}, },
computed: { computed: {
@ -296,6 +302,10 @@ export default {
chapters() { chapters() {
return this.media.chapters || [] return this.media.chapters || []
}, },
bookmarks() {
if (this.isPodcast) return []
return this.$store.getters['user/getUserBookmarksForItem'](this.libraryItemId)
},
tracks() { tracks() {
return this.media.tracks || [] return this.media.tracks || []
}, },
@ -389,7 +399,7 @@ export default {
streamLibraryItem() { streamLibraryItem() {
return this.$store.state.streamLibraryItem return this.$store.state.streamLibraryItem
}, },
streaming() { isStreaming() {
return this.streamLibraryItem && this.streamLibraryItem.id === this.libraryItemId return this.streamLibraryItem && this.streamLibraryItem.id === this.libraryItemId
}, },
userCanUpdate() { userCanUpdate() {
@ -409,6 +419,23 @@ export default {
} }
}, },
methods: { methods: {
clickBookmarksBtn() {
this.showBookmarksModal = true
},
selectBookmark(bookmark) {
if (!bookmark) return
console.log('Select bookmark', bookmark)
if (this.isStreaming) {
this.$eventBus.$emit('playback-seek', bookmark.time)
} else if (this.streamLibraryItem) {
if (confirm(`Are you sure you want to play ${this.title} @ ${this.$secondsToTimestamp(bookmark.time)}?`)) {
this.startStream(bookmark.time)
}
} else {
this.startStream(bookmark.time)
}
this.showBookmarksModal = false
},
clearDownloadQueue() { clearDownloadQueue() {
if (confirm('Are you sure you want to clear episode download queue?')) { if (confirm('Are you sure you want to clear episode download queue?')) {
this.$axios this.$axios
@ -470,7 +497,7 @@ export default {
this.$toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`) this.$toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
}) })
}, },
startStream() { startStream(startTime = null) {
var episodeId = null var episodeId = null
if (this.isPodcast) { if (this.isPodcast) {
var episode = this.podcastEpisodes.find((ep) => { var episode = this.podcastEpisodes.find((ep) => {
@ -483,7 +510,8 @@ export default {
this.$eventBus.$emit('play-item', { this.$eventBus.$emit('play-item', {
libraryItemId: this.libraryItem.id, libraryItemId: this.libraryItem.id,
episodeId episodeId,
startTime
}) })
}, },
editClick() { editClick() {

View File

@ -18,6 +18,7 @@ export default class PlayerHandler {
this.isHlsTranscode = false this.isHlsTranscode = false
this.isVideo = false this.isVideo = false
this.currentSessionId = null this.currentSessionId = null
this.startTimeOverride = undefined // Used for starting playback at a specific time (i.e. clicking bookmark from library item page)
this.startTime = 0 this.startTime = 0
this.failedProgressSyncs = 0 this.failedProgressSyncs = 0
@ -51,12 +52,13 @@ export default class PlayerHandler {
return this.libraryItem.media.episodes.find(ep => ep.id === this.episodeId) return this.libraryItem.media.episodes.find(ep => ep.id === this.episodeId)
} }
load(libraryItem, episodeId, playWhenReady, playbackRate) { load(libraryItem, episodeId, playWhenReady, playbackRate, startTimeOverride = undefined) {
this.libraryItem = libraryItem this.libraryItem = libraryItem
this.episodeId = episodeId this.episodeId = episodeId
this.playWhenReady = playWhenReady this.playWhenReady = playWhenReady
this.initialPlaybackRate = playbackRate this.initialPlaybackRate = playbackRate
this.isVideo = libraryItem.mediaType === 'video' this.isVideo = libraryItem.mediaType === 'video'
this.startTimeOverride = (startTimeOverride == null || isNaN(startTimeOverride)) ? undefined : Number(startTimeOverride)
if (!this.player) this.switchPlayer(playWhenReady) if (!this.player) this.switchPlayer(playWhenReady)
else this.prepare() else this.prepare()
@ -142,12 +144,14 @@ export default class PlayerHandler {
} else { } else {
this.stopPlayInterval() this.stopPlayInterval()
} }
if (this.player) {
if (this.playerState === 'LOADED' || this.playerState === 'PLAYING') { if (this.playerState === 'LOADED' || this.playerState === 'PLAYING') {
this.ctx.setDuration(this.getDuration()) this.ctx.setDuration(this.getDuration())
} }
if (this.playerState !== 'LOADING') { if (this.playerState !== 'LOADING') {
this.ctx.setCurrentTime(this.player.getCurrentTime()) this.ctx.setCurrentTime(this.player.getCurrentTime())
} }
}
this.ctx.setPlaying(this.playerState === 'PLAYING') this.ctx.setPlaying(this.playerState === 'PLAYING')
this.ctx.playerLoading = this.playerState === 'LOADING' this.ctx.playerLoading = this.playerState === 'LOADING'
@ -183,13 +187,14 @@ export default class PlayerHandler {
this.isVideo = session.libraryItem.mediaType === 'video' this.isVideo = session.libraryItem.mediaType === 'video'
this.playWhenReady = false this.playWhenReady = false
this.initialPlaybackRate = playbackRate this.initialPlaybackRate = playbackRate
this.startTimeOverride = undefined
this.prepareSession(session) this.prepareSession(session)
} }
prepareSession(session) { prepareSession(session) {
this.failedProgressSyncs = 0 this.failedProgressSyncs = 0
this.startTime = session.currentTime this.startTime = this.startTimeOverride !== undefined ? this.startTimeOverride : session.currentTime
this.currentSessionId = session.id this.currentSessionId = session.id
this.displayTitle = session.displayTitle this.displayTitle = session.displayTitle
this.displayAuthor = session.displayAuthor this.displayAuthor = session.displayAuthor