diff --git a/components/app/AudioPlayer.vue b/components/app/AudioPlayer.vue index caac1e39..c118a041 100644 --- a/components/app/AudioPlayer.vue +++ b/components/app/AudioPlayer.vue @@ -15,7 +15,7 @@

{{ isDirectPlayMethod ? $strings.LabelPlaybackDirect : isLocalPlayMethod ? $strings.LabelPlaybackLocal : $strings.LabelPlaybackTranscode }}

-
+

{{ currentTimePretty }}

@@ -66,17 +66,17 @@
-
- first_page - {{ jumpBackwardsIcon }} +
+ first_page + {{ jumpBackwardsIcon }}
{{ seekLoading ? 'autorenew' : !isPlaying ? 'play_arrow' : 'pause' }}
- {{ jumpForwardIcon }} - last_page + {{ jumpForwardIcon }} + last_page
@@ -90,7 +90,7 @@
-
+
@@ -98,7 +98,7 @@
- +
@@ -146,9 +146,12 @@ export default { touchStartY: 0, touchStartTime: 0, touchEndY: 0, - useChapterTrack: false, - useTotalTrack: true, - lockUi: false, + playerSettings: { + useChapterTrack: false, + useTotalTrack: true, + scaleElapsedTimeBySpeed: true, + lockUi: false + }, isLoading: false, isDraggingCursor: false, draggingTouchStartX: 0, @@ -164,7 +167,7 @@ export default { showFullscreen(val) { this.updateScreenSize() this.$store.commit('setPlayerFullscreen', !!val) - document.querySelector('body').style.backgroundColor = this.showFullscreen ? this.coverRgb : "" + document.querySelector('body').style.backgroundColor = this.showFullscreen ? this.coverRgb : '' }, bookCoverAspectRatio() { this.updateScreenSize() @@ -187,17 +190,22 @@ export default { { text: this.$strings.LabelTotalTrack, value: 'total_track', - icon: this.useTotalTrack ? 'check_box' : 'check_box_outline_blank' + icon: this.playerSettings.useTotalTrack ? 'check_box' : 'check_box_outline_blank' }, { text: this.$strings.LabelChapterTrack, value: 'chapter_track', - icon: this.useChapterTrack ? 'check_box' : 'check_box_outline_blank' + icon: this.playerSettings.useChapterTrack ? 'check_box' : 'check_box_outline_blank' }, { - text: this.lockUi ? this.$strings.LabelUnlockPlayer : this.$strings.LabelLockPlayer, + text: this.$strings.LabelScaleElapsedTimeBySpeed, + value: 'scale_elapsed_time', + icon: this.playerSettings.scaleElapsedTimeBySpeed ? 'check_box' : 'check_box_outline_blank' + }, + { + text: this.playerSettings.lockUi ? this.$strings.LabelUnlockPlayer : this.$strings.LabelLockPlayer, value: 'lock', - icon: this.lockUi ? 'lock' : 'lock_open' + icon: this.playerSettings.lockUi ? 'lock' : 'lock_open' }, { text: this.$strings.LabelClosePlayer, @@ -322,11 +330,14 @@ export default { }, currentTimePretty() { let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime - return this.$secondsToTimestamp(currentTimeToUse / this.currentPlaybackRate) + if (this.playerSettings.scaleElapsedTimeBySpeed) { + currentTimeToUse = currentTimeToUse / this.currentPlaybackRate + } + return this.$secondsToTimestamp(currentTimeToUse) }, timeRemaining() { let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime - if (this.useChapterTrack && this.currentChapter) { + if (this.playerSettings.useChapterTrack && this.currentChapter) { var currChapTime = currentTimeToUse - this.currentChapter.start return (this.currentChapterDuration - currChapTime) / this.currentPlaybackRate } @@ -494,7 +505,7 @@ export default { this.updateReadyTrack() }, updateReadyTrack() { - if (this.useChapterTrack) { + if (this.playerSettings.useChapterTrack) { if (this.$refs.totalReadyTrack) { this.$refs.totalReadyTrack.style.width = this.readyTrackWidth + 'px' } @@ -511,11 +522,14 @@ export default { } let currentTime = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime - if (this.useChapterTrack && this.currentChapter) { + if (this.playerSettings.useChapterTrack && this.currentChapter) { currentTime = Math.max(0, currentTime - this.currentChapter.start) } + if (this.playerSettings.scaleElapsedTimeBySpeed) { + currentTime = currentTime / this.currentPlaybackRate + } - ts.innerText = this.$secondsToTimestamp(currentTime / this.currentPlaybackRate) + ts.innerText = this.$secondsToTimestamp(currentTime) }, timeupdate() { if (!this.$refs.playedTrack) { @@ -543,7 +557,7 @@ export default { let bufferedPercent = this.bufferedTime / this.totalDuration const totalBufferedPercent = bufferedPercent - if (this.useChapterTrack && this.currentChapter) { + if (this.playerSettings.useChapterTrack && this.currentChapter) { const currChapTime = currentTimeToUse - this.currentChapter.start percentDone = currChapTime / this.currentChapterDuration bufferedPercent = Math.max(0, Math.min(1, (this.bufferedTime - this.currentChapter.start) / this.currentChapterDuration)) @@ -557,7 +571,7 @@ export default { this.$refs.trackCursor.style.left = ptWidth - 14 + 'px' } - if (this.useChapterTrack) { + if (this.playerSettings.useChapterTrack) { if (this.$refs.totalPlayedTrack) this.$refs.totalPlayedTrack.style.width = Math.round(totalPercentDone * this.trackWidth) + 'px' if (this.$refs.totalBufferedTrack) this.$refs.totalBufferedTrack.style.width = Math.round(totalBufferedPercent * this.trackWidth) + 'px' } @@ -584,7 +598,7 @@ export default { } }, async touchstartCursor(e) { - if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.lockUi) return + if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.playerSettings.lockUi) return await this.$hapticsImpact() this.isDraggingCursor = true @@ -670,7 +684,7 @@ export default { let duration = this.totalDuration let minTime = 0 let maxTime = duration - if (this.useChapterTrack && this.currentChapter) { + if (this.playerSettings.useChapterTrack && this.currentChapter) { duration = this.currentChapterDuration minTime = this.currentChapter.start maxTime = minTime + duration @@ -690,37 +704,40 @@ export default { if (action === 'history') { this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`) this.showFullscreen = false + } else if (action === 'scale_elapsed_time') { + this.playerSettings.scaleElapsedTimeBySpeed = !this.playerSettings.scaleElapsedTimeBySpeed + this.updateTimestamp() + this.savePlayerSettings() } else if (action === 'lock') { - this.lockUi = !this.lockUi - this.$localStore.setPlayerLock(this.lockUi) + this.playerSettings.lockUi = !this.playerSettings.lockUi + this.savePlayerSettings() } else if (action === 'chapter_track') { - this.useChapterTrack = !this.useChapterTrack - this.useTotalTrack = !this.useChapterTrack || this.useTotalTrack + this.playerSettings.useChapterTrack = !this.playerSettings.useChapterTrack + this.playerSettings.useTotalTrack = !this.playerSettings.useChapterTrack || this.playerSettings.useTotalTrack this.updateTimestamp() this.updateTrack() this.updateReadyTrack() this.updateUseChapterTrack() - this.$localStore.setUseTotalTrack(this.useTotalTrack) + this.savePlayerSettings() } else if (action === 'total_track') { - this.useTotalTrack = !this.useTotalTrack - this.useChapterTrack = !this.useTotalTrack || this.useChapterTrack + this.playerSettings.useTotalTrack = !this.playerSettings.useTotalTrack + this.playerSettings.useChapterTrack = !this.playerSettings.useTotalTrack || this.playerSettings.useChapterTrack this.updateTimestamp() this.updateTrack() this.updateReadyTrack() this.updateUseChapterTrack() - this.$localStore.setUseTotalTrack(this.useTotalTrack) + this.savePlayerSettings() } else if (action === 'close') { this.closePlayback() } }) }, updateUseChapterTrack() { - this.$localStore.setUseChapterTrack(this.useChapterTrack) // Chapter track in NowPlaying only supported on iOS for now if (this.$platform === 'ios') { - AbsAudioPlayer.setChapterTrack({ enabled: this.useChapterTrack }) + AbsAudioPlayer.setChapterTrack({ enabled: this.playerSettings.useChapterTrack }) } }, forceCloseDropdownMenu() { @@ -739,6 +756,30 @@ export default { this.isLoading = false this.playbackSession = null }, + async loadPlayerSettings() { + const savedPlayerSettings = await this.$localStore.getPlayerSettings() + if (!savedPlayerSettings) { + // In 0.9.72-beta 'useChapterTrack', 'useTotalTrack' and 'playerLock' was replaced with 'playerSettings' JSON object + // Check if this old key was set and if so migrate them over to 'playerSettings' + const chapterTrackPref = await this.$localStore.getPreferenceByKey('useChapterTrack') + if (chapterTrackPref) { + this.playerSettings.useChapterTrack = chapterTrackPref === '1' + const totalTrackPref = await this.$localStore.getPreferenceByKey('useTotalTrack') + this.playerSettings.useTotalTrack = totalTrackPref === '1' + const playerLockPref = await this.$localStore.getPreferenceByKey('playerLock') + this.playerSettings.lockUi = playerLockPref === '1' + } + this.savePlayerSettings() + } else { + this.playerSettings.useChapterTrack = !!savedPlayerSettings.useChapterTrack + this.playerSettings.useTotalTrack = !!savedPlayerSettings.useTotalTrack + this.playerSettings.lockUi = !!savedPlayerSettings.lockUi + this.playerSettings.scaleElapsedTimeBySpeed = !!savedPlayerSettings.scaleElapsedTimeBySpeed + } + }, + savePlayerSettings() { + return this.$localStore.setPlayerSettings({ ...this.playerSettings }) + }, // // Listeners from audio AbsAudioPlayer // @@ -805,9 +846,7 @@ export default { this.updateTimestamp() }, async init() { - this.useChapterTrack = await this.$localStore.getUseChapterTrack() - this.useTotalTrack = await this.$localStore.getUseTotalTrack() - this.lockUi = await this.$localStore.getPlayerLock() + await this.loadPlayerSettings() this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession) this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed) diff --git a/components/modals/Dialog.vue b/components/modals/Dialog.vue index 4e5c71c3..814f43ea 100644 --- a/components/modals/Dialog.vue +++ b/components/modals/Dialog.vue @@ -40,7 +40,7 @@ export default { default: '16px' }, width: { - type: Number, + type: [String, Number], default: 300 } }, diff --git a/plugins/localStore.js b/plugins/localStore.js index 009aa6e0..13e2fcd3 100644 --- a/plugins/localStore.js +++ b/plugins/localStore.js @@ -42,56 +42,20 @@ class LocalStorage { } } - async setUseChapterTrack(useChapterTrack) { + async setPlayerSettings(playerSettings) { try { - await Preferences.set({ key: 'useChapterTrack', value: useChapterTrack ? '1' : '0' }) + await Preferences.set({ key: 'playerSettings', value: JSON.stringify(playerSettings) }) } catch (error) { - console.error('[LocalStorage] Failed to set use chapter track', error) + console.error('[LocalStorage] Failed to set player settings', error) } } - async getUseChapterTrack() { + async getPlayerSettings() { try { - var obj = await Preferences.get({ key: 'useChapterTrack' }) || {} - return obj.value === '1' + const playerSettingsObj = await Preferences.get({ key: 'playerSettings' }) || {} + return playerSettingsObj.value ? JSON.parse(playerSettingsObj.value) : null } catch (error) { - console.error('[LocalStorage] Failed to get use chapter track', error) - return false - } - } - - async setUseTotalTrack(useTotalTrack) { - try { - await Preferences.set({ key: 'useTotalTrack', value: useTotalTrack ? '1' : '0' }) - } catch (error) { - console.error('[LocalStorage] Failed to set use total track', error) - } - } - - async getUseTotalTrack() { - try { - var obj = await Preferences.get({ key: 'useTotalTrack' }) || {} - return obj.value === '1' - } catch (error) { - console.error('[LocalStorage] Failed to get use total track', error) - return false - } - } - - async setPlayerLock(lock) { - try { - await Preferences.set({ key: 'playerLock', value: lock ? '1' : '0' }) - } catch (error) { - console.error('[LocalStorage] Failed to set player lock', error) - } - } - - async getPlayerLock() { - try { - var obj = await Preferences.get({ key: 'playerLock' }) || {} - return obj.value === '1' - } catch (error) { - console.error('[LocalStorage] Failed to get player lock', error) + console.error('[LocalStorage] Failed to get player settings', error) return false } } @@ -179,6 +143,22 @@ class LocalStorage { return false } } + + /** + * Get preference value by key + * + * @param {string} key + * @returns {Promise} + */ + async getPreferenceByKey(key) { + try { + const obj = await Preferences.get({ key }) || {} + return obj.value || null + } catch (error) { + console.error(`[LocalStorage] Failed to get preference "${key}"`, error) + return null + } + } } diff --git a/strings/cs.json b/strings/cs.json index 1c170eda..7169787c 100644 --- a/strings/cs.json +++ b/strings/cs.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Vlastní jméno vlastníka", "LabelRSSFeedPreventIndexing": "Zabránit indexování", "LabelRSSFeedSlug": "RSS kanál Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Sezóna", "LabelSelectADevice": "Select a device", "LabelSeries": "Série", diff --git a/strings/da.json b/strings/da.json index 2c5f6d05..15b5ebfb 100644 --- a/strings/da.json +++ b/strings/da.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Brugerdefineret ejerens navn", "LabelRSSFeedPreventIndexing": "Forhindrer indeksering", "LabelRSSFeedSlug": "RSS-feed-slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Sæson", "LabelSelectADevice": "Select a device", "LabelSeries": "Serie", diff --git a/strings/de.json b/strings/de.json index dfe9f26c..21fb3561 100644 --- a/strings/de.json +++ b/strings/de.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers", "LabelRSSFeedPreventIndexing": "Indizierung verhindern", "LabelRSSFeedSlug": "RSS Feed Schlagwort", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Staffel", "LabelSelectADevice": "Wähle ein Gerät", "LabelSeries": "Serien", diff --git a/strings/en-us.json b/strings/en-us.json index 5eaf71c1..55464878 100644 --- a/strings/en-us.json +++ b/strings/en-us.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Season", "LabelSelectADevice": "Select a device", "LabelSeries": "Series", diff --git a/strings/es.json b/strings/es.json index 90f815a9..21cae895 100644 --- a/strings/es.json +++ b/strings/es.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado", "LabelRSSFeedPreventIndexing": "Prevenir Indexado", "LabelRSSFeedSlug": "Fuente RSS Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Temporada", "LabelSelectADevice": "Select a device", "LabelSeries": "Series", diff --git a/strings/fr.json b/strings/fr.json index 63e81f0e..2c2e4e28 100644 --- a/strings/fr.json +++ b/strings/fr.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Nom propriétaire personnalisé", "LabelRSSFeedPreventIndexing": "Empêcher l’indexation", "LabelRSSFeedSlug": "Identificateur d’adresse du Flux RSS ", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Saison", "LabelSelectADevice": "Select a device", "LabelSeries": "Séries", diff --git a/strings/gu.json b/strings/gu.json index 212c9772..6802d2ca 100644 --- a/strings/gu.json +++ b/strings/gu.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Season", "LabelSelectADevice": "Select a device", "LabelSeries": "Series", diff --git a/strings/hi.json b/strings/hi.json index 19b97a39..00651ceb 100644 --- a/strings/hi.json +++ b/strings/hi.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Season", "LabelSelectADevice": "Select a device", "LabelSeries": "Series", diff --git a/strings/hr.json b/strings/hr.json index 3bf0f4bc..047ca4e8 100644 --- a/strings/hr.json +++ b/strings/hr.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Sezona", "LabelSelectADevice": "Select a device", "LabelSeries": "Serije", diff --git a/strings/it.json b/strings/it.json index 8d96bf79..458596ea 100644 --- a/strings/it.json +++ b/strings/it.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Nome del proprietario personalizzato", "LabelRSSFeedPreventIndexing": "Impedisci l'indicizzazione", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Stagione", "LabelSelectADevice": "Select a device", "LabelSeries": "Serie", diff --git a/strings/lt.json b/strings/lt.json index 7161615c..b3ae845c 100644 --- a/strings/lt.json +++ b/strings/lt.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Pasirinktinis savininko vardas", "LabelRSSFeedPreventIndexing": "Neleisti indeksuoti", "LabelRSSFeedSlug": "RSS srauto identifikatorius", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Sezonas", "LabelSelectADevice": "Select a device", "LabelSeries": "Serija", diff --git a/strings/nl.json b/strings/nl.json index f96a488f..8d91fe2d 100644 --- a/strings/nl.json +++ b/strings/nl.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Aangepaste naam eigenaar", "LabelRSSFeedPreventIndexing": "Voorkom indexering", "LabelRSSFeedSlug": "RSS-feed slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Seizoen", "LabelSelectADevice": "Select a device", "LabelSeries": "Serie", diff --git a/strings/no.json b/strings/no.json index b828ead7..f4b44a82 100644 --- a/strings/no.json +++ b/strings/no.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Tilpasset eier Navn", "LabelRSSFeedPreventIndexing": "Forhindre indeksering", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Sesong", "LabelSelectADevice": "Select a device", "LabelSeries": "Serier", diff --git a/strings/pl.json b/strings/pl.json index 8b457d4e..6ff9afdb 100644 --- a/strings/pl.json +++ b/strings/pl.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedSlug": "RSS Feed Slug", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Sezon", "LabelSelectADevice": "Select a device", "LabelSeries": "Serie", diff --git a/strings/ru.json b/strings/ru.json index 4ef3d641..fb3334e9 100644 --- a/strings/ru.json +++ b/strings/ru.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Пользовательское Имя владельца", "LabelRSSFeedPreventIndexing": "Запретить индексирование", "LabelRSSFeedSlug": "Встроить RSS-канал", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Сезон", "LabelSelectADevice": "Выбор девайса", "LabelSeries": "Серия", diff --git a/strings/sv.json b/strings/sv.json index f2bc678a..abbaf278 100644 --- a/strings/sv.json +++ b/strings/sv.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "Anpassat ägarnamn", "LabelRSSFeedPreventIndexing": "Förhindra indexering", "LabelRSSFeedSlug": "RSS-flödesslag", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "Säsong", "LabelSelectADevice": "Select a device", "LabelSeries": "Serie", diff --git a/strings/zh-cn.json b/strings/zh-cn.json index 1f8fc068..c44d8f47 100644 --- a/strings/zh-cn.json +++ b/strings/zh-cn.json @@ -194,6 +194,7 @@ "LabelRSSFeedCustomOwnerName": "自定义所有者名称", "LabelRSSFeedPreventIndexing": "防止索引", "LabelRSSFeedSlug": "RSS 源段", + "LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed", "LabelSeason": "季", "LabelSelectADevice": "选择设备", "LabelSeries": "系列",