mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-06 20:08:48 +02:00
Merge
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
<widgets-download-progress-indicator />
|
||||
|
||||
<nuxt-link class="h-7 mx-2" to="/search">
|
||||
<nuxt-link v-if="user" class="h-7 mx-2" to="/search">
|
||||
<span class="material-icons" style="font-size: 1.75rem">search</span>
|
||||
</nuxt-link>
|
||||
|
||||
|
||||
@@ -43,7 +43,10 @@
|
||||
<div id="streamContainer" class="w-full z-20 bg-primary absolute bottom-0 left-0 right-0 p-2 pointer-events-auto transition-all" @click="clickContainer">
|
||||
<div v-if="showFullscreen" class="absolute top-0 left-0 right-0 w-full py-3 mx-auto px-3" style="max-width: 380px">
|
||||
<div class="flex items-center justify-between pointer-events-auto">
|
||||
<span class="material-icons text-3xl text-white text-opacity-75 cursor-pointer" @click="$emit('showBookmarks')">{{ bookmarks.length ? 'bookmark' : 'bookmark_border' }}</span>
|
||||
<span v-if="!isPodcast" class="material-icons text-3xl text-white text-opacity-75 cursor-pointer" @click="$emit('showBookmarks')">{{ bookmarks.length ? 'bookmark' : 'bookmark_border' }}</span>
|
||||
<!-- hidden for podcasts but still using this as a placeholder -->
|
||||
<span v-else class="material-icons text-3xl text-white text-opacity-0">bookmark</span>
|
||||
|
||||
<span class="font-mono text-white text-opacity-75 cursor-pointer" style="font-size: 1.35rem" @click="$emit('selectPlaybackSpeed')">{{ currentPlaybackRate }}x</span>
|
||||
<svg v-if="!sleepTimerRunning" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-white text-opacity-75 cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor" @click.stop="$emit('showSleepTimer')">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
||||
@@ -156,6 +159,12 @@ export default {
|
||||
}
|
||||
return this.showFullscreen ? 200 : 60
|
||||
},
|
||||
mediaType() {
|
||||
return this.playbackSession ? this.playbackSession.mediaType : null
|
||||
},
|
||||
isPodcast() {
|
||||
return this.mediaType === 'podcast'
|
||||
},
|
||||
mediaMetadata() {
|
||||
return this.playbackSession ? this.playbackSession.mediaMetadata : null
|
||||
},
|
||||
@@ -552,6 +561,10 @@ export default {
|
||||
}
|
||||
this.isEnded = data.playerState == this.$constants.PlayerState.ENDED
|
||||
|
||||
console.log('received metadata update', data)
|
||||
|
||||
if (data.currentRate && data.currentRate > 0) this.playbackSpeed = data.currentRate
|
||||
|
||||
this.timeupdate()
|
||||
},
|
||||
// When a playback session is started the native android/ios will send the session
|
||||
|
||||
@@ -101,12 +101,14 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
items.push({
|
||||
icon: 'folder',
|
||||
iconOutlined: true,
|
||||
text: 'Local Media',
|
||||
to: '/localMedia/folders'
|
||||
})
|
||||
if (this.$platform !== 'ios') {
|
||||
items.push({
|
||||
icon: 'folder',
|
||||
iconOutlined: true,
|
||||
text: 'Local Media',
|
||||
to: '/localMedia/folders'
|
||||
})
|
||||
}
|
||||
return items
|
||||
},
|
||||
currentRoutePath() {
|
||||
|
||||
@@ -120,7 +120,7 @@ export default {
|
||||
return this.media.coverPath || this.placeholderUrl
|
||||
},
|
||||
hasCover() {
|
||||
return !!this.media.coverPath || this.localCover
|
||||
return !!this.media.coverPath || this.localCover || this.downloadCover
|
||||
},
|
||||
sizeMultiplier() {
|
||||
var baseSize = this.squareAspectRatio ? 192 : 120
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
<template>
|
||||
<div class="w-full px-0 py-4 overflow-hidden relative border-b border-white border-opacity-10">
|
||||
<div v-if="episode" class="flex items-center">
|
||||
<!-- <div class="w-12 min-w-12 max-w-16 h-full">
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<span class="material-icons drag-handle text-lg text-white text-opacity-50 hover:text-opacity-100">menu</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="flex-grow px-1">
|
||||
<p v-if="publishedAt" class="text-xs text-gray-400 mb-1">Published {{ $formatDate(publishedAt, 'MMM do, yyyy') }}</p>
|
||||
<div v-if="episode" class="w-full px-1">
|
||||
<!-- Help debug for testing -->
|
||||
<!-- <template>
|
||||
<p class="text-xs mb-1">{{ isLocal ? 'LOCAL' : 'NOT LOCAL' }}</p>
|
||||
<p class="text-xs mb-4">Lid:{{ libraryItemId }}<br />Eid:{{ episode.id }}<br />LLid:{{ localLibraryItemId }}<br />LEid:{{ localEpisodeId }}</p>
|
||||
<p v-if="itemProgress">Server Media Progress {{ Math.round(itemProgress.progress * 100) }}</p>
|
||||
<p v-else>No Server Media Progress</p>
|
||||
<p v-if="localMediaProgress">Local Media Progress {{ Math.round(localMediaProgress.progress * 100) }}</p>
|
||||
<p v-else>No Local Media Progress</p>
|
||||
</template> -->
|
||||
|
||||
<p class="text-sm font-semibold">
|
||||
{{ title }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-200 episode-subtitle mt-1.5 mb-0.5">
|
||||
{{ description }}
|
||||
</p>
|
||||
<div class="flex items-center pt-2">
|
||||
<div class="h-8 px-4 border border-white border-opacity-20 hover:bg-white hover:bg-opacity-10 rounded-full flex items-center justify-center cursor-pointer" :class="userIsFinished ? 'text-white text-opacity-40' : ''" @click="playClick">
|
||||
<span class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<p class="pl-2 pr-1 text-sm font-semibold">{{ timeRemaining }}</p>
|
||||
</div>
|
||||
<p v-if="publishedAt" class="text-xs text-gray-400 mb-1">Published {{ $formatDate(publishedAt, 'MMM do, yyyy') }}</p>
|
||||
|
||||
<ui-read-icon-btn :disabled="isProcessingReadUpdate" :is-read="userIsFinished" borderless class="mx-1 mt-0.5" @click="toggleFinished" />
|
||||
<span class="material-icons px-2" :class="downloadItem ? 'animate-bounce text-warning text-opacity-75 text-xl' : 'text-gray-300 text-xl'" @click="downloadClick">{{ downloadItem ? 'downloading' : 'download' }}</span>
|
||||
<p class="text-sm font-semibold">
|
||||
{{ title }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-200 episode-subtitle mt-1.5 mb-0.5">
|
||||
{{ description }}
|
||||
</p>
|
||||
<div class="flex items-center pt-2">
|
||||
<div class="h-8 px-4 border border-white border-opacity-20 hover:bg-white hover:bg-opacity-10 rounded-full flex items-center justify-center cursor-pointer" :class="userIsFinished ? 'text-white text-opacity-40' : ''" @click="playClick">
|
||||
<span class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<p class="pl-2 pr-1 text-sm font-semibold">{{ timeRemaining }}</p>
|
||||
</div>
|
||||
|
||||
<ui-read-icon-btn :disabled="isProcessingReadUpdate" :is-read="userIsFinished" borderless class="mx-1 mt-0.5" @click="toggleFinished" />
|
||||
|
||||
<span v-if="isLocal" class="material-icons-outlined px-2 text-success text-lg">audio_file</span>
|
||||
<span v-else-if="!localEpisode" class="material-icons px-2" :class="downloadItem ? 'animate-bounce text-warning text-opacity-75 text-xl' : 'text-gray-300 text-xl'" @click="downloadClick">{{ downloadItem ? 'downloading' : 'download' }}</span>
|
||||
<span v-else class="material-icons px-2 text-success text-xl">download_done</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,11 +44,16 @@ import { AbsDownloader } from '@/plugins/capacitor'
|
||||
export default {
|
||||
props: {
|
||||
libraryItemId: String,
|
||||
isLocal: Boolean,
|
||||
episode: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
localLibraryItemId: String,
|
||||
localEpisode: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
isLocal: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -68,8 +79,15 @@ export default {
|
||||
return this.$secondsToTimestamp(this.episode.duration)
|
||||
},
|
||||
isStreaming() {
|
||||
if (this.playerIsLocal && this.localLibraryItemId && this.localEpisode) {
|
||||
// Check is streaming local version of this episode
|
||||
return this.$store.getters['getIsEpisodeStreaming'](this.localLibraryItemId, this.localEpisode.id)
|
||||
}
|
||||
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItemId, this.episode.id)
|
||||
},
|
||||
playerIsLocal() {
|
||||
return !!this.$store.state.playerIsLocal
|
||||
},
|
||||
streamIsPlaying() {
|
||||
return this.$store.state.playerIsPlaying && this.isStreaming
|
||||
},
|
||||
@@ -77,6 +95,14 @@ export default {
|
||||
if (this.isLocal) return this.$store.getters['globals/getLocalMediaProgressById'](this.libraryItemId, this.episode.id)
|
||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId, this.episode.id)
|
||||
},
|
||||
localMediaProgress() {
|
||||
if (this.isLocal) return this.$store.getters['globals/getLocalMediaProgressById'](this.libraryItemId, this.episode.id)
|
||||
else if (this.localLibraryItemId && this.localEpisode) {
|
||||
return this.$store.getters['globals/getLocalMediaProgressById'](this.localLibraryItemId, this.localEpisode.id)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
itemProgressPercent() {
|
||||
return this.itemProgress ? this.itemProgress.progress : 0
|
||||
},
|
||||
@@ -95,6 +121,9 @@ export default {
|
||||
},
|
||||
downloadItem() {
|
||||
return this.$store.getters['globals/getDownloadItem'](this.libraryItemId, this.episode.id)
|
||||
},
|
||||
localEpisodeId() {
|
||||
return this.localEpisode ? this.localEpisode.id : null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -154,30 +183,69 @@ export default {
|
||||
if (this.streamIsPlaying) {
|
||||
this.$eventBus.$emit('pause-item')
|
||||
} else {
|
||||
this.$eventBus.$emit('play-item', {
|
||||
libraryItemId: this.libraryItemId,
|
||||
episodeId: this.episode.id
|
||||
})
|
||||
if (this.localEpisode && this.localLibraryItemId) {
|
||||
console.log('Play local episode', this.localEpisode.id, this.localLibraryItemId)
|
||||
|
||||
this.$eventBus.$emit('play-item', {
|
||||
libraryItemId: this.localLibraryItemId,
|
||||
episodeId: this.localEpisode.id
|
||||
})
|
||||
} else {
|
||||
this.$eventBus.$emit('play-item', {
|
||||
libraryItemId: this.libraryItemId,
|
||||
episodeId: this.episode.id
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
toggleFinished() {
|
||||
var updatePayload = {
|
||||
isFinished: !this.userIsFinished
|
||||
}
|
||||
async toggleFinished() {
|
||||
this.isProcessingReadUpdate = true
|
||||
this.$axios
|
||||
.$patch(`/api/me/progress/${this.libraryItemId}/${this.episode.id}`, updatePayload)
|
||||
.then(() => {
|
||||
this.isProcessingReadUpdate = false
|
||||
this.$toast.success(`Item marked as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
this.isProcessingReadUpdate = false
|
||||
this.$toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||
})
|
||||
if (this.isLocal || this.localEpisode) {
|
||||
var isFinished = !this.userIsFinished
|
||||
var localLibraryItemId = this.isLocal ? this.libraryItemId : this.localLibraryItemId
|
||||
var localEpisodeId = this.isLocal ? this.episode.id : this.localEpisode.id
|
||||
var localMediaProgressId = `${localLibraryItemId}-${localEpisodeId}`
|
||||
console.log('toggleFinished local media progress id', localMediaProgressId, isFinished)
|
||||
var payload = await this.$db.updateLocalMediaProgressFinished({ localMediaProgressId, isFinished })
|
||||
console.log('toggleFinished payload', JSON.stringify(payload))
|
||||
if (!payload || payload.error) {
|
||||
var errorMsg = payload ? payload.error : 'Unknown error'
|
||||
this.$toast.error(errorMsg)
|
||||
} else {
|
||||
var localMediaProgress = payload.localMediaProgress
|
||||
console.log('toggleFinished localMediaProgress', JSON.stringify(localMediaProgress))
|
||||
if (localMediaProgress) {
|
||||
this.$store.commit('globals/updateLocalMediaProgress', localMediaProgress)
|
||||
}
|
||||
|
||||
var lmp = this.$store.getters['globals/getLocalMediaProgressById'](this.libraryItemId, this.episode.id)
|
||||
console.log('toggleFinished Check LMP', this.libraryItemId, this.episode.id, JSON.stringify(lmp))
|
||||
|
||||
var serverUpdated = payload.server
|
||||
if (serverUpdated) {
|
||||
this.$toast.success(`Local & Server Item marked as ${isFinished ? 'Finished' : 'Not Finished'}`)
|
||||
} else {
|
||||
this.$toast.success(`Local Item marked as ${isFinished ? 'Finished' : 'Not Finished'}`)
|
||||
}
|
||||
}
|
||||
this.isProcessingReadUpdate = false
|
||||
} else {
|
||||
var updatePayload = {
|
||||
isFinished: !this.userIsFinished
|
||||
}
|
||||
this.$axios
|
||||
.$patch(`/api/me/progress/${this.libraryItemId}/${this.episode.id}`, updatePayload)
|
||||
.then(() => {
|
||||
this.isProcessingReadUpdate = false
|
||||
this.$toast.success(`Item marked as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
this.isProcessingReadUpdate = false
|
||||
this.$toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<p class="text-lg mb-1 font-semibold">Episodes ({{ episodes.length }})</p>
|
||||
|
||||
<template v-for="episode in episodes">
|
||||
<tables-podcast-episode-row :episode="episode" :library-item-id="libraryItemId" :key="episode.id" />
|
||||
<tables-podcast-episode-row :episode="episode" :local-episode="localEpisodeMap[episode.id]" :library-item-id="libraryItemId" :local-library-item-id="localLibraryItemId" :is-local="isLocal" :key="episode.id" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -15,12 +15,29 @@ export default {
|
||||
episodes: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
localLibraryItemId: String,
|
||||
localEpisodes: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
isLocal: Boolean // If is local then episodes and libraryItemId are local, otherwise local is passed in localLibraryItemId and localEpisodes
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
computed: {
|
||||
// Map of local episodes where server episode id is key
|
||||
localEpisodeMap() {
|
||||
var epmap = {}
|
||||
this.localEpisodes.forEach((localEp) => {
|
||||
if (localEp.serverEpisodeId) {
|
||||
epmap[localEp.serverEpisodeId] = localEp
|
||||
}
|
||||
})
|
||||
return epmap
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,9 @@ export default {
|
||||
data.itemProgress = itemProgress
|
||||
data.episodes = downloadItemParts.filter((dip) => dip.episode).map((dip) => dip.episode)
|
||||
|
||||
console.log('Saving item update download payload', JSON.stringify(update))
|
||||
console.log('[download] Saving item update download payload', JSON.stringify(update))
|
||||
console.log('[download] Download Progress indicator data', JSON.stringify(data))
|
||||
|
||||
this.$set(this.itemDownloadingMap, update.id, update)
|
||||
|
||||
this.$store.commit('globals/addUpdateItemDownload', data)
|
||||
|
||||
Reference in New Issue
Block a user