mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-20 01:17:45 +02:00
Add:Player queue for audiobooks #1077
This commit is contained in:
parent
3357ccfaf3
commit
78559520ab
@ -477,6 +477,21 @@ export default {
|
|||||||
text: this.$strings.ButtonRemoveFromContinueListening
|
text: this.$strings.ButtonRemoveFromContinueListening
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (!this.isPodcast) {
|
||||||
|
if (this.libraryItemIdStreaming && !this.isStreamingFromDifferentLibrary) {
|
||||||
|
if (!this.isQueued) {
|
||||||
|
items.push({
|
||||||
|
func: 'addToQueue',
|
||||||
|
text: this.$strings.ButtonQueueAddItem
|
||||||
|
})
|
||||||
|
} else if (!this.isStreaming) {
|
||||||
|
items.push({
|
||||||
|
func: 'removeFromQueue',
|
||||||
|
text: this.$strings.ButtonQueueRemoveItem
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return items
|
return items
|
||||||
},
|
},
|
||||||
_socket() {
|
_socket() {
|
||||||
@ -690,8 +705,9 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
addToQueue() {
|
addToQueue() {
|
||||||
|
var queueItem = {}
|
||||||
if (this.recentEpisode) {
|
if (this.recentEpisode) {
|
||||||
const queueItem = {
|
queueItem = {
|
||||||
libraryItemId: this.libraryItemId,
|
libraryItemId: this.libraryItemId,
|
||||||
libraryId: this.libraryId,
|
libraryId: this.libraryId,
|
||||||
episodeId: this.recentEpisode.id,
|
episodeId: this.recentEpisode.id,
|
||||||
@ -701,8 +717,19 @@ export default {
|
|||||||
duration: this.recentEpisode.audioFile.duration || null,
|
duration: this.recentEpisode.audioFile.duration || null,
|
||||||
coverPath: this.media.coverPath || null
|
coverPath: this.media.coverPath || null
|
||||||
}
|
}
|
||||||
this.store.commit('addItemToQueue', queueItem)
|
} else {
|
||||||
|
queueItem = {
|
||||||
|
libraryItemId: this.libraryItemId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: this.title,
|
||||||
|
subtitle: this.author,
|
||||||
|
caption: '',
|
||||||
|
duration: this.media.duration || null,
|
||||||
|
coverPath: this.media.coverPath || null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.store.commit('addItemToQueue', queueItem)
|
||||||
},
|
},
|
||||||
removeFromQueue() {
|
removeFromQueue() {
|
||||||
const episodeId = this.recentEpisode ? this.recentEpisode.id : null
|
const episodeId = this.recentEpisode ? this.recentEpisode.id : null
|
||||||
@ -815,6 +842,18 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const queueItem = {
|
||||||
|
libraryItemId: this.libraryItemId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: this.title,
|
||||||
|
subtitle: this.author,
|
||||||
|
caption: '',
|
||||||
|
duration: this.media.duration || null,
|
||||||
|
coverPath: this.media.coverPath || null
|
||||||
|
}
|
||||||
|
queueItems.push(queueItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.$emit('play-item', {
|
eventBus.$emit('play-item', {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary">
|
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary">
|
||||||
<div class="absolute cover-bg" ref="coverBg" />
|
<div class="absolute cover-bg" ref="coverBg" />
|
||||||
</div>
|
</div>
|
||||||
<img ref="cover" :src="cover" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0" :class="showCoverBg ? 'object-contain' : 'object-cover'" />
|
<img ref="cover" :src="cover" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0" :class="showCoverBg ? 'object-contain' : 'object-fill'" />
|
||||||
|
|
||||||
<a v-if="!imageFailed && showOpenNewTab && isHovering" :href="cover" @click.stop target="_blank" class="absolute bg-primary flex items-center justify-center shadow-sm rounded-full hover:scale-110 transform duration-100" :style="{ top: sizeMultiplier * 0.5 + 'rem', right: sizeMultiplier * 0.5 + 'rem', width: 2.5 * sizeMultiplier + 'rem', height: 2.5 * sizeMultiplier + 'rem' }">
|
<a v-if="!imageFailed && showOpenNewTab && isHovering" :href="cover" @click.stop target="_blank" class="absolute bg-primary flex items-center justify-center shadow-sm rounded-full hover:scale-110 transform duration-100" :style="{ top: sizeMultiplier * 0.5 + 'rem', right: sizeMultiplier * 0.5 + 'rem', width: 2.5 * sizeMultiplier + 'rem', height: 2.5 * sizeMultiplier + 'rem' }">
|
||||||
<span class="material-icons" :style="{ fontSize: sizeMultiplier * 1.75 + 'rem' }">open_in_new</span>
|
<span class="material-icons" :style="{ fontSize: sizeMultiplier * 1.75 + 'rem' }">open_in_new</span>
|
||||||
@ -63,6 +63,9 @@ export default {
|
|||||||
},
|
},
|
||||||
resolution() {
|
resolution() {
|
||||||
return `${this.naturalWidth}x${this.naturalHeight}px`
|
return `${this.naturalWidth}x${this.naturalHeight}px`
|
||||||
|
},
|
||||||
|
placeholderUrl() {
|
||||||
|
return `${this.$config.routerBasePath}/book_placeholder.jpg`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -72,7 +75,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
imageLoaded() {
|
imageLoaded() {
|
||||||
if (this.$refs.cover) {
|
if (this.$refs.cover && this.src !== this.placeholderUrl) {
|
||||||
var { naturalWidth, naturalHeight } = this.$refs.cover
|
var { naturalWidth, naturalHeight } = this.$refs.cover
|
||||||
this.naturalHeight = naturalHeight
|
this.naturalHeight = naturalHeight
|
||||||
this.naturalWidth = naturalWidth
|
this.naturalWidth = naturalWidth
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<p v-if="caption" class="text-gray-400 text-xs">{{ caption }}</p>
|
<p v-if="caption" class="text-gray-400 text-xs">{{ caption }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-28">
|
<div class="w-28">
|
||||||
<p v-if="isOpenInPlayer" class="text-sm text-right text-gray-400">Streaming</p>
|
<p v-if="isOpenInPlayer" class="text-sm text-right text-gray-400">{{ $strings.ButtonPlaying }}</p>
|
||||||
<div v-else-if="isHovering" class="flex items-center justify-end -mx-1">
|
<div v-else-if="isHovering" class="flex items-center justify-end -mx-1">
|
||||||
<button class="outline-none mx-1 flex items-center" @click.stop="playClick">
|
<button class="outline-none mx-1 flex items-center" @click.stop="playClick">
|
||||||
<span class="material-icons text-success">play_arrow</span>
|
<span class="material-icons text-success">play_arrow</span>
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
<modals-modal v-model="show" name="queue-items" :width="800" :height="'unset'">
|
<modals-modal v-model="show" name="queue-items" :width="800" :height="'unset'">
|
||||||
<template #outer>
|
<template #outer>
|
||||||
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
|
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
|
||||||
<p class="font-book text-3xl text-white truncate">Player Queue</p>
|
<p class="font-book text-3xl text-white truncate">{{ $strings.HeaderPlayerQueue }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden py-4" style="max-height: 80vh">
|
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden py-4" style="max-height: 80vh">
|
||||||
<div v-if="show" class="w-full h-full">
|
<div v-if="show" class="w-full h-full">
|
||||||
<div class="pb-4 px-4 flex items-center">
|
<div class="pb-4 px-4 flex items-center">
|
||||||
<p class="text-base text-gray-200">Player Queue</p>
|
<p class="text-base text-gray-200">{{ $strings.HeaderPlayerQueue }}</p>
|
||||||
<p class="text-base text-gray-400 px-4">{{ playerQueueItems.length }} Items</p>
|
<p class="text-base text-gray-400 px-4">{{ playerQueueItems.length }} Items</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<ui-checkbox v-model="playerQueueAutoPlay" label="Auto Play" medium checkbox-bg="primary" border-color="gray-600" label-class="pl-2 mb-px" />
|
<ui-checkbox v-model="playerQueueAutoPlay" label="Auto Play" medium checkbox-bg="primary" border-color="gray-600" label-class="pl-2 mb-px" />
|
||||||
|
@ -22,15 +22,15 @@
|
|||||||
<span class="material-icons text-2xl sm:text-3xl">format_list_bulleted</span>
|
<span class="material-icons text-2xl sm:text-3xl">format_list_bulleted</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button v-if="playerQueueItems.length" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')">
|
||||||
|
<span class="material-icons text-2xl sm:text-3xl">queue_music</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack">
|
<ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack">
|
||||||
<div class="cursor-pointer text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="setUseChapterTrack">
|
<div class="cursor-pointer text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="setUseChapterTrack">
|
||||||
<span class="material-icons text-2xl sm:text-3xl transform transition-transform" :class="useChapterTrack ? 'rotate-180' : ''">timelapse</span>
|
<span class="material-icons text-2xl sm:text-3xl transform transition-transform" :class="useChapterTrack ? 'rotate-180' : ''">timelapse</span>
|
||||||
</div>
|
</div>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<button v-if="playerQueueItems.length" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')">
|
|
||||||
<span class="material-icons text-2xl sm:text-3xl">queue_music</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<player-playback-controls :loading="loading" :seek-loading="seekLoading" :playback-rate.sync="playbackRate" :paused="paused" :has-next-chapter="hasNextChapter" @prevChapter="prevChapter" @nextChapter="nextChapter" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setPlaybackRate="setPlaybackRate" @playPause="playPause" />
|
<player-playback-controls :loading="loading" :seek-loading="seekLoading" :playback-rate.sync="playbackRate" :paused="paused" :has-next-chapter="hasNextChapter" @prevChapter="prevChapter" @nextChapter="nextChapter" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setPlaybackRate="setPlaybackRate" @playPause="playPause" />
|
||||||
|
@ -72,11 +72,23 @@ export default {
|
|||||||
this.expanded = !this.expanded
|
this.expanded = !this.expanded
|
||||||
},
|
},
|
||||||
goToTimestamp(time) {
|
goToTimestamp(time) {
|
||||||
|
const queueItem = {
|
||||||
|
libraryItemId: this.libraryItemId,
|
||||||
|
libraryId: this.libraryItem.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: this.metadata.title,
|
||||||
|
subtitle: this.metadata.authors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: this.media.duration || null,
|
||||||
|
coverPath: this.media.coverPath || null
|
||||||
|
}
|
||||||
|
|
||||||
if (this.$store.getters['getIsMediaStreaming'](this.libraryItemId)) {
|
if (this.$store.getters['getIsMediaStreaming'](this.libraryItemId)) {
|
||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
libraryItemId: this.libraryItemId,
|
libraryItemId: this.libraryItemId,
|
||||||
episodeId: null,
|
episodeId: null,
|
||||||
startTime: time
|
startTime: time,
|
||||||
|
queueItems: [queueItem]
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
const payload = {
|
const payload = {
|
||||||
@ -86,7 +98,8 @@ export default {
|
|||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
libraryItemId: this.libraryItemId,
|
libraryItemId: this.libraryItemId,
|
||||||
episodeId: null,
|
episodeId: null,
|
||||||
startTime: time
|
startTime: time,
|
||||||
|
queueItems: [queueItem]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -137,8 +137,22 @@ export default {
|
|||||||
this.isHovering = false
|
this.isHovering = false
|
||||||
},
|
},
|
||||||
playClick() {
|
playClick() {
|
||||||
|
const queueItems = [
|
||||||
|
{
|
||||||
|
libraryItemId: this.book.id,
|
||||||
|
libraryId: this.book.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: this.bookTitle,
|
||||||
|
subtitle: this.bookAuthors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: this.media.duration || null,
|
||||||
|
coverPath: this.media.coverPath || null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
libraryItemId: this.book.id
|
libraryItemId: this.book.id,
|
||||||
|
queueItems
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clickEdit() {
|
clickEdit() {
|
||||||
|
@ -122,13 +122,42 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
clickPlay() {
|
clickPlay() {
|
||||||
var nextBookNotRead = this.playableBooks.find((pb) => {
|
const queueItems = []
|
||||||
var prog = this.$store.getters['user/getUserMediaProgress'](pb.id)
|
|
||||||
return !prog || !prog.isFinished
|
// Collection queue will start at the first unfinished book
|
||||||
|
// if all books are finished then entire collection is queued
|
||||||
|
const itemsWithProgress = this.playableBooks.map((item) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
progress: this.$store.getters['user/getUserMediaProgress'](item.id)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (nextBookNotRead) {
|
|
||||||
|
const hasUnfinishedItems = itemsWithProgress.some((i) => !i.progress || !i.progress.isFinished)
|
||||||
|
if (!hasUnfinishedItems) {
|
||||||
|
console.warn('All items in collection are finished - starting at first item')
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < itemsWithProgress.length; i++) {
|
||||||
|
const libraryItem = itemsWithProgress[i]
|
||||||
|
if (!hasUnfinishedItems || !libraryItem.progress || !libraryItem.progress.isFinished) {
|
||||||
|
queueItems.push({
|
||||||
|
libraryItemId: libraryItem.id,
|
||||||
|
libraryId: libraryItem.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: libraryItem.media.metadata.title,
|
||||||
|
subtitle: libraryItem.media.metadata.authors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: libraryItem.media.duration || null,
|
||||||
|
coverPath: libraryItem.media.coverPath || null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queueItems.length >= 0) {
|
||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
libraryItemId: nextBookNotRead.id
|
libraryItemId: queueItems[0].libraryItemId,
|
||||||
|
queueItems
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,12 +127,38 @@ export default {
|
|||||||
this.processingGoToTimestamp = false
|
this.processingGoToTimestamp = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (session.episodeId && !libraryItem.media.episodes.find((ep) => ep.id === session.episodeId)) {
|
if (session.episodeId && !libraryItem.media.episodes.some((ep) => ep.id === session.episodeId)) {
|
||||||
this.$toast.error('Failed to get podcast episode')
|
this.$toast.error('Failed to get podcast episode')
|
||||||
this.processingGoToTimestamp = false
|
this.processingGoToTimestamp = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var queueItem = {}
|
||||||
|
if (session.episodeId) {
|
||||||
|
var episode = libraryItem.media.episodes.find((ep) => ep.id === session.episodeId)
|
||||||
|
queueItem = {
|
||||||
|
libraryItemId: libraryItem.id,
|
||||||
|
libraryId: libraryItem.libraryId,
|
||||||
|
episodeId: episode.id,
|
||||||
|
title: episode.title,
|
||||||
|
subtitle: libraryItem.media.metadata.title,
|
||||||
|
caption: episode.publishedAt ? `Published ${this.$formatDate(episode.publishedAt, 'MMM do, yyyy')}` : 'Unknown publish date',
|
||||||
|
duration: episode.audioFile.duration || null,
|
||||||
|
coverPath: libraryItem.media.coverPath || null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueItem = {
|
||||||
|
libraryItemId: libraryItem.id,
|
||||||
|
libraryId: libraryItem.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: libraryItem.media.metadata.title,
|
||||||
|
subtitle: libraryItem.media.metadata.authors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: libraryItem.media.duration || null,
|
||||||
|
coverPath: libraryItem.media.coverPath || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
message: this.$getString('MessageStartPlaybackAtTime', [session.displayTitle, this.$secondsToTimestamp(session.currentTime)]),
|
message: this.$getString('MessageStartPlaybackAtTime', [session.displayTitle, this.$secondsToTimestamp(session.currentTime)]),
|
||||||
callback: (confirmed) => {
|
callback: (confirmed) => {
|
||||||
@ -140,7 +166,8 @@ export default {
|
|||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
libraryItemId: libraryItem.id,
|
libraryItemId: libraryItem.id,
|
||||||
episodeId: session.episodeId || null,
|
episodeId: session.episodeId || null,
|
||||||
startTime: session.currentTime
|
startTime: session.currentTime,
|
||||||
|
queueItems: [queueItem]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.processingGoToTimestamp = false
|
this.processingGoToTimestamp = false
|
||||||
|
@ -114,12 +114,38 @@ export default {
|
|||||||
this.processingGoToTimestamp = false
|
this.processingGoToTimestamp = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (session.episodeId && !libraryItem.media.episodes.find((ep) => ep.id === session.episodeId)) {
|
if (session.episodeId && !libraryItem.media.episodes.some((ep) => ep.id === session.episodeId)) {
|
||||||
this.$toast.error('Failed to get podcast episode')
|
this.$toast.error('Failed to get podcast episode')
|
||||||
this.processingGoToTimestamp = false
|
this.processingGoToTimestamp = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var queueItem = {}
|
||||||
|
if (session.episodeId) {
|
||||||
|
var episode = libraryItem.media.episodes.find((ep) => ep.id === session.episodeId)
|
||||||
|
queueItem = {
|
||||||
|
libraryItemId: libraryItem.id,
|
||||||
|
libraryId: libraryItem.libraryId,
|
||||||
|
episodeId: episode.id,
|
||||||
|
title: episode.title,
|
||||||
|
subtitle: libraryItem.media.metadata.title,
|
||||||
|
caption: episode.publishedAt ? `Published ${this.$formatDate(episode.publishedAt, 'MMM do, yyyy')}` : 'Unknown publish date',
|
||||||
|
duration: episode.audioFile.duration || null,
|
||||||
|
coverPath: libraryItem.media.coverPath || null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueItem = {
|
||||||
|
libraryItemId: libraryItem.id,
|
||||||
|
libraryId: libraryItem.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: libraryItem.media.metadata.title,
|
||||||
|
subtitle: libraryItem.media.metadata.authors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: libraryItem.media.duration || null,
|
||||||
|
coverPath: libraryItem.media.coverPath || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
message: this.$getString('MessageStartPlaybackAtTime', [session.displayTitle, this.$secondsToTimestamp(session.currentTime)]),
|
message: this.$getString('MessageStartPlaybackAtTime', [session.displayTitle, this.$secondsToTimestamp(session.currentTime)]),
|
||||||
callback: (confirmed) => {
|
callback: (confirmed) => {
|
||||||
@ -127,7 +153,8 @@ export default {
|
|||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
libraryItemId: libraryItem.id,
|
libraryItemId: libraryItem.id,
|
||||||
episodeId: session.episodeId || null,
|
episodeId: session.episodeId || null,
|
||||||
startTime: session.currentTime
|
startTime: session.currentTime,
|
||||||
|
queueItems: [queueItem]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.processingGoToTimestamp = false
|
this.processingGoToTimestamp = false
|
||||||
|
@ -137,12 +137,16 @@
|
|||||||
{{ isMissing ? $strings.LabelMissing : $strings.LabelIncomplete }}
|
{{ isMissing ? $strings.LabelMissing : $strings.LabelIncomplete }}
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
|
|
||||||
|
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
|
||||||
|
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_add'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
|
||||||
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-btn v-if="showReadButton" color="info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
|
<ui-btn v-if="showReadButton" color="info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
|
||||||
<span class="material-icons -ml-2 pr-2 text-white">auto_stories</span>
|
<span class="material-icons -ml-2 pr-2 text-white">auto_stories</span>
|
||||||
{{ $strings.ButtonRead }}
|
{{ $strings.ButtonRead }}
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
|
|
||||||
<ui-tooltip v-if="userCanUpdate" text="Edit" direction="top">
|
<ui-tooltip v-if="userCanUpdate" :text="$strings.LabelEdit" direction="top">
|
||||||
<ui-icon-btn icon="edit" class="mx-0.5" @click="editClick" />
|
<ui-icon-btn icon="edit" class="mx-0.5" @click="editClick" />
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
@ -398,6 +402,9 @@ export default {
|
|||||||
isStreaming() {
|
isStreaming() {
|
||||||
return this.streamLibraryItem && this.streamLibraryItem.id === this.libraryItemId
|
return this.streamLibraryItem && this.streamLibraryItem.id === this.libraryItemId
|
||||||
},
|
},
|
||||||
|
isQueued() {
|
||||||
|
return this.$store.getters['getIsMediaQueued'](this.libraryItemId)
|
||||||
|
},
|
||||||
userCanUpdate() {
|
userCanUpdate() {
|
||||||
return this.$store.getters['user/getUserCanUpdate']
|
return this.$store.getters['user/getUserCanUpdate']
|
||||||
},
|
},
|
||||||
@ -412,6 +419,10 @@ export default {
|
|||||||
|
|
||||||
// If rss feed is open then show feed url to users otherwise just show to admins
|
// If rss feed is open then show feed url to users otherwise just show to admins
|
||||||
return this.userIsAdminOrUp || this.rssFeedUrl
|
return this.userIsAdminOrUp || this.rssFeedUrl
|
||||||
|
},
|
||||||
|
showQueueBtn() {
|
||||||
|
if (this.isPodcast || this.isVideo) return false
|
||||||
|
return !this.$store.getters['getIsStreamingFromDifferentLibrary'] && this.streamLibraryItem
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -536,6 +547,7 @@ export default {
|
|||||||
if (!podcastProgress || !podcastProgress.isFinished) {
|
if (!podcastProgress || !podcastProgress.isFinished) {
|
||||||
queueItems.push({
|
queueItems.push({
|
||||||
libraryItemId: this.libraryItemId,
|
libraryItemId: this.libraryItemId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
episodeId: episode.id,
|
episodeId: episode.id,
|
||||||
title: episode.title,
|
title: episode.title,
|
||||||
subtitle: this.title,
|
subtitle: this.title,
|
||||||
@ -545,6 +557,18 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const queueItem = {
|
||||||
|
libraryItemId: this.libraryItemId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: this.title,
|
||||||
|
subtitle: this.authors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: this.duration || null,
|
||||||
|
coverPath: this.media.coverPath || null
|
||||||
|
}
|
||||||
|
queueItems.push(queueItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$eventBus.$emit('play-item', {
|
this.$eventBus.$emit('play-item', {
|
||||||
@ -615,6 +639,26 @@ export default {
|
|||||||
console.log('RSS Feed Closed', data)
|
console.log('RSS Feed Closed', data)
|
||||||
this.rssFeedUrl = null
|
this.rssFeedUrl = null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
queueBtnClick() {
|
||||||
|
if (this.isQueued) {
|
||||||
|
// Remove from queue
|
||||||
|
this.$store.commit('removeItemFromQueue', { libraryItemId: this.libraryItemId })
|
||||||
|
} else {
|
||||||
|
// Add to queue
|
||||||
|
|
||||||
|
const queueItem = {
|
||||||
|
libraryItemId: this.libraryItemId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
|
episodeId: null,
|
||||||
|
title: this.title,
|
||||||
|
subtitle: this.authors.map((au) => au.name).join(', '),
|
||||||
|
caption: '',
|
||||||
|
duration: this.duration || null,
|
||||||
|
coverPath: this.media.coverPath || null
|
||||||
|
}
|
||||||
|
this.$store.commit('addItemToQueue', queueItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -107,6 +107,7 @@
|
|||||||
"HeaderOtherFiles": "Other Files",
|
"HeaderOtherFiles": "Other Files",
|
||||||
"HeaderOpenRSSFeed": "Open RSS Feed",
|
"HeaderOpenRSSFeed": "Open RSS Feed",
|
||||||
"HeaderPermissions": "Permissions",
|
"HeaderPermissions": "Permissions",
|
||||||
|
"HeaderPlayerQueue": "Player Queue",
|
||||||
"HeaderPodcastsToAdd": "Podcasts to Add",
|
"HeaderPodcastsToAdd": "Podcasts to Add",
|
||||||
"HeaderPreviewCover": "Preview Cover",
|
"HeaderPreviewCover": "Preview Cover",
|
||||||
"HeaderRemoveEpisode": "Remove Episode",
|
"HeaderRemoveEpisode": "Remove Episode",
|
||||||
|
Loading…
Reference in New Issue
Block a user