From df2db634a72ef6429f00fbcedf5f4d07cf0fa015 Mon Sep 17 00:00:00 2001 From: Eyad <28269664+octopotato@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:22:57 +0000 Subject: [PATCH] Add per-book playback rate to web client #1173 Save playbackRate to mediaProgress via PATCH on speed change. Init from per-book rate with fallback to global user setting. Guard settingsUpdated from overwriting per-book rates. Resolve per-book rate in playLibraryItem before loading. --- .../components/app/MediaPlayerContainer.vue | 6 +++- client/components/player/PlayerUi.vue | 33 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/client/components/app/MediaPlayerContainer.vue b/client/components/app/MediaPlayerContainer.vue index 1a2b1d30a..da951c265 100644 --- a/client/components/app/MediaPlayerContainer.vue +++ b/client/components/app/MediaPlayerContainer.vue @@ -528,7 +528,11 @@ export default { if (this.$refs.audioPlayer) this.$refs.audioPlayer.checkUpdateChapterTrack() }) - this.playerHandler.load(libraryItem, episodeId, true, this.currentPlaybackRate, payload.startTime) + // Resolve per-book playback rate for the new item, falling back to current rate + const mediaProgress = this.$store.getters['user/getUserMediaProgress'](libraryItemId, episodeId) + const playbackRate = mediaProgress?.playbackRate || this.currentPlaybackRate + + this.playerHandler.load(libraryItem, episodeId, true, playbackRate, payload.startTime) }, pauseItem() { this.playerHandler.pause() diff --git a/client/components/player/PlayerUi.vue b/client/components/player/PlayerUi.vue index f929943c4..588c4724b 100644 --- a/client/components/player/PlayerUi.vue +++ b/client/components/player/PlayerUi.vue @@ -110,6 +110,12 @@ export default { useChapterTrack() { if (this.$refs.trackbar) this.$refs.trackbar.setUseChapterTrack(this.useChapterTrack) this.updateTimestamp() + }, + '$store.state.streamLibraryItem'() { + this.initPlaybackRate() + }, + '$store.state.streamEpisodeId'() { + this.initPlaybackRate() } }, computed: { @@ -236,6 +242,15 @@ export default { this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => { console.error('Failed to update settings', err) }) + + // Save per-book playback rate to mediaProgress + const libraryItemId = this.$store.state.streamLibraryItem?.id + if (!libraryItemId) return + const episodeId = this.$store.state.streamEpisodeId + const progressId = episodeId ? `${libraryItemId}-${episodeId}` : libraryItemId + this.$axios.$patch(`/api/me/progress/${progressId}`, { playbackRate }).catch((err) => { + console.error('Failed to save playback rate to progress', err) + }) }, setPlaybackRate(playbackRate) { this.$emit('setPlaybackRate', playbackRate) @@ -321,15 +336,27 @@ export default { showPlayerSettings() { this.showPlayerSettingsModal = !this.showPlayerSettingsModal }, + initPlaybackRate() { + const libraryItemId = this.$store.state.streamLibraryItem?.id + const episodeId = this.$store.state.streamEpisodeId + const mediaProgress = this.$store.getters['user/getUserMediaProgress'](libraryItemId, episodeId) + this.playbackRate = mediaProgress?.playbackRate || this.$store.getters['user/getUserSetting']('playbackRate') || 1 + this.setPlaybackRate(this.playbackRate) + }, init() { - this.playbackRate = this.$store.getters['user/getUserSetting']('playbackRate') || 1 + this.initPlaybackRate() if (this.$refs.trackbar) this.$refs.trackbar.setUseChapterTrack(this.useChapterTrack) - this.setPlaybackRate(this.playbackRate) }, settingsUpdated(settings) { if (settings.playbackRate && this.playbackRate !== settings.playbackRate) { - this.setPlaybackRate(settings.playbackRate) + // Don't let global setting override a per-book rate + const libraryItemId = this.$store.state.streamLibraryItem?.id + const episodeId = this.$store.state.streamEpisodeId + const mediaProgress = this.$store.getters['user/getUserMediaProgress'](libraryItemId, episodeId) + if (!mediaProgress?.playbackRate) { + this.setPlaybackRate(settings.playbackRate) + } } }, closePlayer() {