diff --git a/components/app/AudioPlayerContainer.vue b/components/app/AudioPlayerContainer.vue index c2f36aa1..0e92314e 100644 --- a/components/app/AudioPlayerContainer.vue +++ b/components/app/AudioPlayerContainer.vue @@ -204,6 +204,7 @@ export default { message: `Cannot cast downloaded media items. Confirm to close cast and play on your device.` }) if (!value) { + this.$store.commit('setPlayerDoneStartingPlayback') return } } @@ -217,6 +218,7 @@ export default { } else if (this.$refs.audioPlayer) { this.$refs.audioPlayer.play() } + this.$store.commit('setPlayerDoneStartingPlayback') return } @@ -254,6 +256,9 @@ export default { console.error('Failed', error) this.$toast.error('Failed to play') }) + .finally(() => { + this.$store.commit('setPlayerDoneStartingPlayback') + }) }, pauseItem() { if (this.$refs.audioPlayer && this.$refs.audioPlayer.isPlaying) { diff --git a/components/cards/LazyBookCard.vue b/components/cards/LazyBookCard.vue index 8a743f68..21bc72d9 100644 --- a/components/cards/LazyBookCard.vue +++ b/components/cards/LazyBookCard.vue @@ -46,8 +46,13 @@ -
- {{ streamIsPlaying ? 'pause_circle' : 'play_circle_filled' }} +
+ {{ streamIsPlaying ? 'pause_circle' : 'play_circle_filled' }} +
+ + + +
@@ -322,6 +327,14 @@ export default { streamIsPlaying() { return this.store.state.playerIsPlaying && this.isStreaming }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + const mediaId = this.store.state.playerStartingPlaybackMediaId + return mediaId === this.recentEpisode?.id + }, isMissing() { return this._libraryItem.isMissing }, @@ -447,6 +460,8 @@ export default { }, async play() {}, async playEpisode() { + if (this.playerIsStartingPlayback) return + await this.$hapticsImpact() const eventBus = this.$eventBus || this.$nuxt.$eventBus if (this.streamIsPlaying) { @@ -454,6 +469,7 @@ export default { return } + this.store.commit('setPlayerIsStartingPlayback', this.recentEpisode.id) if (this.localEpisode) { // Play episode locally eventBus.$emit('play-item', { diff --git a/components/cards/LazyListBookCard.vue b/components/cards/LazyListBookCard.vue index 1e4a9433..3b46b9a4 100644 --- a/components/cards/LazyListBookCard.vue +++ b/components/cards/LazyListBookCard.vue @@ -176,7 +176,7 @@ export default { return this.mediaMetadata.series }, seriesSequence() { - return this.series ? this.series.sequence : null + return this.series?.sequence || null }, collapsedSeries() { // Only added to item object when collapseSeries is enabled @@ -209,10 +209,10 @@ export default { return this.store.getters['user/getUserMediaProgress'](this.libraryItemId) }, userProgressPercent() { - return this.userProgress ? this.userProgress.progress || 0 : 0 + return this.userProgress?.progress || 0 }, itemIsFinished() { - return this.userProgress ? !!this.userProgress.isFinished : false + return !!this.userProgress?.isFinished }, showError() { return this.numMissingParts || this.isMissing || this.isInvalid diff --git a/components/tables/playlist/ItemTableRow.vue b/components/tables/playlist/ItemTableRow.vue index 40fda325..6631dc84 100644 --- a/components/tables/playlist/ItemTableRow.vue +++ b/components/tables/playlist/ItemTableRow.vue @@ -13,8 +13,11 @@
-
@@ -117,6 +120,15 @@ export default { streamIsPlaying() { return this.$store.state.playerIsPlaying && this.isStreaming }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + const mediaId = this.$store.state.playerStartingPlaybackMediaId + let thisMediaId = this.episodeId || this.libraryItem.id + return mediaId === thisMediaId + }, userItemProgress() { return this.$store.getters['user/getUserMediaProgress'](this.libraryItem.id, this.episodeId) }, @@ -142,10 +154,14 @@ export default { this.$emit('showMore', playlistItem) }, async playClick() { + if (this.playerIsStartingPlayback) return + await this.$hapticsImpact() + let mediaId = this.episodeId || this.libraryItem.id if (this.streamIsPlaying) { this.$eventBus.$emit('pause-item') } else if (this.localLibraryItem) { + this.$store.commit('setPlayerIsStartingPlayback', mediaId) this.$eventBus.$emit('play-item', { libraryItemId: this.localLibraryItem.id, episodeId: this.localEpisode?.id, @@ -153,6 +169,7 @@ export default { serverEpisodeId: this.episodeId }) } else { + this.$store.commit('setPlayerIsStartingPlayback', mediaId) this.$eventBus.$emit('play-item', { libraryItemId: this.libraryItem.id, episodeId: this.episodeId diff --git a/components/tables/podcast/EpisodeRow.vue b/components/tables/podcast/EpisodeRow.vue index 2916eb60..8380f060 100644 --- a/components/tables/podcast/EpisodeRow.vue +++ b/components/tables/podcast/EpisodeRow.vue @@ -29,7 +29,10 @@
- {{ streamIsPlaying ? 'pause' : 'play_arrow' }} + {{ streamIsPlaying ? 'pause' : 'play_arrow' }} + + +

{{ timeRemaining }}

@@ -118,6 +121,14 @@ export default { streamIsPlaying() { return this.$store.state.playerIsPlaying && this.isStreaming }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + const mediaId = this.$store.state.playerStartingPlaybackMediaId + return mediaId === this.episode?.id + }, itemProgress() { 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) @@ -227,10 +238,14 @@ export default { } }, async playClick() { + if (this.playerIsStartingPlayback) return + await this.$hapticsImpact() if (this.streamIsPlaying) { this.$eventBus.$emit('pause-item') } else { + this.$store.commit('setPlayerIsStartingPlayback', this.episode.id) + if (this.localEpisode && this.localLibraryItemId) { console.log('Play local episode', this.localEpisode.id, this.localLibraryItemId) diff --git a/components/tables/podcast/LatestEpisodeRow.vue b/components/tables/podcast/LatestEpisodeRow.vue index 0194618d..2c99fd93 100644 --- a/components/tables/podcast/LatestEpisodeRow.vue +++ b/components/tables/podcast/LatestEpisodeRow.vue @@ -29,7 +29,10 @@
- {{ streamIsPlaying ? 'pause' : 'play_arrow' }} + {{ streamIsPlaying ? 'pause' : 'play_arrow' }} + + +

{{ timeRemaining }}

@@ -122,6 +125,15 @@ export default { streamIsPlaying() { return this.$store.state.playerIsPlaying && this.isStreaming }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + if (!this.episode?.id) return false + const mediaId = this.$store.state.playerStartingPlaybackMediaId + return mediaId === this.episode.id + }, itemProgress() { 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) @@ -135,10 +147,10 @@ export default { } }, itemProgressPercent() { - return this.itemProgress ? this.itemProgress.progress : 0 + return this.itemProgress?.progress || 0 }, userIsFinished() { - return this.itemProgress ? !!this.itemProgress.isFinished : false + return !!this.itemProgress?.isFinished }, timeRemaining() { if (this.streamIsPlaying) return 'Playing' @@ -154,7 +166,7 @@ export default { return this.$store.getters['globals/getDownloadItem'](this.libraryItemId, this.episode.id) }, localEpisodeId() { - return this.localEpisode ? this.localEpisode.id : null + return this.localEpisode?.id || null }, podcast() { return this.episode.podcast || {} @@ -238,10 +250,14 @@ export default { } }, async playClick() { + if (this.playerIsStartingPlayback) return + await this.$hapticsImpact() if (this.streamIsPlaying) { this.$eventBus.$emit('pause-item') } else { + this.$store.commit('setPlayerIsStartingPlayback', this.episode.id) + if (this.localEpisode && this.localLibraryItemId) { console.log('Play local episode', this.localEpisode.id, this.localLibraryItemId) diff --git a/pages/collection/_id.vue b/pages/collection/_id.vue index 0babb957..fe9b0c2b 100644 --- a/pages/collection/_id.vue +++ b/pages/collection/_id.vue @@ -12,7 +12,7 @@ {{ collectionName }}
- + play_arrow {{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }} @@ -53,6 +53,7 @@ export default { }, data() { return { + mediaIdStartingPlayback: null, processingRemove: false } }, @@ -77,17 +78,30 @@ export default { streaming() { return !!this.playableBooks.find((b) => this.$store.getters['getIsMediaStreaming'](b.id)) }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + if (!this.mediaIdStartingPlayback) return false + const mediaId = this.$store.state.playerStartingPlaybackMediaId + return mediaId === this.mediaIdStartingPlayback + }, showPlayButton() { return this.playableBooks.length } }, methods: { clickPlay() { + if (this.playerIsStartingPlayback) return + var nextBookNotRead = this.playableBooks.find((pb) => { var prog = this.$store.getters['user/getUserMediaProgress'](pb.id) - return !prog || !prog.isFinished + return !prog?.isFinished }) if (nextBookNotRead) { + this.mediaIdStartingPlayback = nextBookNotRead.id + this.$store.commit('setPlayerIsStartingPlayback', nextBookNotRead.id) this.$eventBus.$emit('play-item', { libraryItemId: nextBookNotRead.id }) } } diff --git a/pages/item/_id/_episode/index.vue b/pages/item/_id/_episode/index.vue index 90d5227f..43200a4a 100644 --- a/pages/item/_id/_episode/index.vue +++ b/pages/item/_id/_episode/index.vue @@ -29,7 +29,7 @@
- + {{ playerIsPlaying ? 'pause' : 'play_arrow' }} {{ playerIsPlaying ? $strings.ButtonPause : localEpisodeId ? $strings.ButtonPlay : $strings.ButtonStream }} @@ -210,6 +210,15 @@ export default { playerIsPlaying() { return this.$store.state.playerIsPlaying && this.isPlaying }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + if (!this.serverEpisodeId) return false + const mediaId = this.$store.state.playerStartingPlaybackMediaId + return mediaId === this.serverEpisodeId + }, userItemProgress() { if (this.isLocal) return this.localItemProgress return this.serverItemProgress @@ -332,10 +341,14 @@ export default { } }, async playClick() { + if (this.playerIsStartingPlayback) return + await this.$hapticsImpact() if (this.playerIsPlaying) { this.$eventBus.$emit('pause-item') } else { + this.$store.commit('setPlayerIsStartingPlayback', this.serverEpisodeId) + if (this.localEpisodeId && this.localLibraryItemId && !this.isLocal) { console.log('Play local episode', this.localEpisodeId, this.localLibraryItemId) diff --git a/pages/item/_id/index.vue b/pages/item/_id/index.vue index f340421f..55476524 100644 --- a/pages/item/_id/index.vue +++ b/pages/item/_id/index.vue @@ -45,7 +45,7 @@
- + {{ playerIsPlaying ? 'pause' : 'play_arrow' }} {{ playerIsPlaying ? $strings.ButtonPause : isPodcast ? $strings.ButtonNextEpisode : hasLocal ? $strings.ButtonPlay : $strings.ButtonStream }} @@ -205,7 +205,8 @@ export default { coverBgIsLight: false, windowWidth: 0, descriptionClamped: false, - showFullDescription: false + showFullDescription: false, + episodeStartingPlayback: null } }, computed: { @@ -393,6 +394,19 @@ export default { playerIsPlaying() { return this.$store.state.playerIsPlaying && (this.isStreaming || this.isPlaying) }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + const mediaId = this.$store.state.playerStartingPlaybackMediaId + if (this.isPodcast) { + if (!this.episodeStartingPlayback) return false + return mediaId === this.episodeStartingPlayback + } else { + return mediaId === this.serverLibraryItemId + } + }, tracks() { return this.media.tracks || [] }, @@ -488,6 +502,8 @@ export default { } }, async play(startTime = null) { + if (this.playerIsStartingPlayback) return + if (this.isPodcast) { this.episodes.sort((a, b) => { return String(b.publishedAt).localeCompare(String(a.publishedAt), undefined, { numeric: true, sensitivity: 'base' }) @@ -500,7 +516,7 @@ export default { } else { podcastProgress = this.$store.getters['globals/getLocalMediaProgressById'](this.libraryItemId, ep.id) } - return !podcastProgress || !podcastProgress.isFinished + return !podcastProgress?.isFinished }) if (!episode) episode = this.episodes[0] @@ -515,6 +531,8 @@ export default { } const serverEpisodeId = !this.isLocal ? episodeId : localEpisode?.serverEpisodeId || null + this.episodeStartingPlayback = serverEpisodeId + this.$store.commit('setPlayerIsStartingPlayback', serverEpisodeId) if (serverEpisodeId && this.serverLibraryItemId && this.isCasting) { // If casting and connected to server for local library item then send server library item id this.$eventBus.$emit('play-item', { libraryItemId: this.serverLibraryItemId, episodeId: serverEpisodeId }) @@ -543,6 +561,7 @@ export default { if (!value) return } + this.$store.commit('setPlayerIsStartingPlayback', this.serverLibraryItemId) this.$eventBus.$emit('play-item', { libraryItemId, serverLibraryItemId: this.serverLibraryItemId, startTime }) } }, diff --git a/pages/localMedia/folders/_id.vue b/pages/localMedia/folders/_id.vue index a7bc5d82..3c722b6c 100644 --- a/pages/localMedia/folders/_id.vue +++ b/pages/localMedia/folders/_id.vue @@ -110,9 +110,6 @@ export default { this.$router.replace('/localMedia/folders') } }, - play(mediaItem) { - this.$eventBus.$emit('play-item', { libraryItemId: mediaItem.id }) - }, async init() { var folder = await this.$db.getLocalFolder(this.folderId) this.folder = folder diff --git a/pages/localMedia/item/_id.vue b/pages/localMedia/item/_id.vue index 10dc2fca..71f7fbb4 100644 --- a/pages/localMedia/item/_id.vue +++ b/pages/localMedia/item/_id.vue @@ -232,6 +232,10 @@ export default { } ] } + }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback } }, methods: { @@ -279,7 +283,9 @@ export default { this.showDialog = true }, async play() { + if (this.playerIsStartingPlayback) return await this.$hapticsImpact() + this.$store.commit('setPlayerIsStartingPlayback', this.localLibraryItemId) this.$eventBus.$emit('play-item', { libraryItemId: this.localLibraryItemId }) }, getCapImageSrc(contentUrl) { diff --git a/pages/media/_id/history.vue b/pages/media/_id/history.vue index 9914248f..3a4e5415 100644 --- a/pages/media/_id/history.vue +++ b/pages/media/_id/history.vue @@ -39,8 +39,7 @@ export default { }, data() { return { - onMediaItemHistoryUpdatedListener: null, - startingPlayback: false + onMediaItemHistoryUpdatedListener: null } }, computed: { @@ -123,21 +122,21 @@ export default { }) return groups + }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback } }, methods: { async clickPlaybackTime(time) { - if (this.startingPlayback) return - this.startingPlayback = true - await this.$hapticsImpact() - console.log('Click playback time', time) - this.playAtTime(time) + if (this.playerIsStartingPlayback) return - setTimeout(() => { - this.startingPlayback = false - }, 1000) + await this.$hapticsImpact() + this.playAtTime(time) }, playAtTime(startTime) { + this.$store.commit('setPlayerIsStartingPlayback', this.mediaItemEpisodeId || this.mediaItemLibraryItemId) if (this.mediaItemIsLocal) { // Local only this.$eventBus.$emit('play-item', { libraryItemId: this.mediaItemLibraryItemId, episodeId: this.mediaItemEpisodeId, startTime }) diff --git a/pages/playlist/_id.vue b/pages/playlist/_id.vue index 8ece676b..14be40d3 100644 --- a/pages/playlist/_id.vue +++ b/pages/playlist/_id.vue @@ -10,7 +10,7 @@ {{ playlistName }}
- + play_arrow {{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }} @@ -76,7 +76,8 @@ export default { showMoreMenu: false, processing: false, selectedLibraryItem: null, - selectedEpisode: null + selectedEpisode: null, + mediaIdStartingPlayback: null } }, computed: { @@ -108,6 +109,15 @@ export default { }, showPlayButton() { return this.playableItems.length + }, + playerIsStartingPlayback() { + // Play has been pressed and waiting for native play response + return this.$store.state.playerIsStartingPlayback + }, + playerIsStartingForThisMedia() { + if (!this.mediaIdStartingPlayback) return false + const mediaId = this.$store.state.playerStartingPlaybackMediaId + return mediaId === this.mediaIdStartingPlayback } }, methods: { @@ -122,6 +132,8 @@ export default { return !prog?.isFinished }) if (nextItem) { + this.mediaIdStartingPlayback = nextItem.episodeId || nextItem.libraryItemId + this.$store.commit('setPlayerIsStartingPlayback', this.mediaIdStartingPlayback) if (nextItem.localLibraryItem) { this.$eventBus.$emit('play-item', { libraryItemId: nextItem.localLibraryItem.id, episodeId: nextItem.localEpisode?.id, serverLibraryItemId: nextItem.libraryItemId, serverEpisodeId: nextItem.episodeId }) } else { diff --git a/store/index.js b/store/index.js index 847c23f2..edb089e6 100644 --- a/store/index.js +++ b/store/index.js @@ -7,6 +7,8 @@ export const state = () => ({ currentPlaybackSession: null, playerIsPlaying: false, playerIsFullscreen: false, + playerIsStartingPlayback: false, // When pressing play before native play response + playerStartingPlaybackMediaId: null, isCasting: false, isCastAvailable: false, attemptingConnection: false, @@ -131,6 +133,14 @@ export const mutations = { setPlayerFullscreen(state, val) { state.playerIsFullscreen = val }, + setPlayerIsStartingPlayback(state, mediaId) { + state.playerStartingPlaybackMediaId = mediaId + state.playerIsStartingPlayback = true + }, + setPlayerDoneStartingPlayback(state) { + state.playerStartingPlaybackMediaId = null + state.playerIsStartingPlayback = false + }, setHasStoragePermission(state, val) { state.hasStoragePermission = val },