mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-05-15 16:01:30 +00:00
feat: implement smart speed UI and state persistence
This commit is contained in:
parent
4299fdce59
commit
fa2460868e
7 changed files with 150 additions and 2 deletions
|
|
@ -17,6 +17,18 @@
|
|||
<div class="flex items-center mb-4">
|
||||
<ui-select-input v-model="playbackRateIncrementDecrement" :label="$strings.LabelPlaybackRateIncrementDecrement" menuMaxHeight="250px" :items="playbackRateIncrementDecrementValues" @input="setPlaybackRateIncrementDecrementAmount" />
|
||||
</div>
|
||||
|
||||
<div v-if="!isCasting" class="w-full h-px bg-white/10 my-6"></div>
|
||||
|
||||
<div v-if="!isCasting" class="flex items-center mb-4">
|
||||
<ui-toggle-switch v-model="enableSmartSpeed" @input="setEnableSmartSpeed" />
|
||||
<div class="pl-4">
|
||||
<span>{{ $strings.LabelEnableSmartSpeed || 'Enable Smart Speed' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!isCasting" class="flex items-center mb-4" :class="{'opacity-50 pointer-events-none': !enableSmartSpeed}">
|
||||
<ui-select-input v-model="smartSpeedRatio" :label="$strings.LabelSmartSpeedRatio || 'Smart Speed Compression Ratio'" menuMaxHeight="250px" :items="smartSpeedRatioValues" @input="setSmartSpeedRatio" />
|
||||
</div>
|
||||
</div>
|
||||
</modals-modal>
|
||||
</template>
|
||||
|
|
@ -40,7 +52,17 @@ export default {
|
|||
jumpForwardAmount: 10,
|
||||
jumpBackwardAmount: 10,
|
||||
playbackRateIncrementDecrementValues: [0.1, 0.05],
|
||||
playbackRateIncrementDecrement: 0.1
|
||||
playbackRateIncrementDecrement: 0.1,
|
||||
enableSmartSpeed: false,
|
||||
smartSpeedRatio: 2.5,
|
||||
smartSpeedRatioValues: [
|
||||
{ text: '1.5x', value: 1.5 },
|
||||
{ text: '2.0x', value: 2.0 },
|
||||
{ text: '2.5x', value: 2.5 },
|
||||
{ text: '3.0x', value: 3.0 },
|
||||
{ text: '4.0x', value: 4.0 },
|
||||
{ text: '5.0x', value: 5.0 }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -51,6 +73,9 @@ export default {
|
|||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
},
|
||||
isCasting() {
|
||||
return this.$store.state.globals.isCasting || false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -69,11 +94,24 @@ export default {
|
|||
this.playbackRateIncrementDecrement = val
|
||||
this.$store.dispatch('user/updateUserSettings', { playbackRateIncrementDecrement: val })
|
||||
},
|
||||
setEnableSmartSpeed() {
|
||||
this.$store.commit('user/SET_SMART_SPEED_ENABLED', this.enableSmartSpeed)
|
||||
},
|
||||
setSmartSpeedRatio(val) {
|
||||
this.smartSpeedRatio = val
|
||||
this.$store.commit('user/SET_SMART_SPEED_RATIO', val)
|
||||
},
|
||||
settingsUpdated() {
|
||||
this.useChapterTrack = this.$store.getters['user/getUserSetting']('useChapterTrack')
|
||||
this.jumpForwardAmount = this.$store.getters['user/getUserSetting']('jumpForwardAmount')
|
||||
this.jumpBackwardAmount = this.$store.getters['user/getUserSetting']('jumpBackwardAmount')
|
||||
this.playbackRateIncrementDecrement = this.$store.getters['user/getUserSetting']('playbackRateIncrementDecrement')
|
||||
|
||||
const enableSmartSpeed = this.$store.getters['user/getUserSetting']('enableSmartSpeed')
|
||||
this.enableSmartSpeed = enableSmartSpeed !== null ? enableSmartSpeed : false
|
||||
|
||||
const smartSpeedRatio = this.$store.getters['user/getUserSetting']('smartSpeedRatio')
|
||||
this.smartSpeedRatio = smartSpeedRatio !== null ? smartSpeedRatio : 2.5
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<div class="relative">
|
||||
<!-- Smart Speed Indicator -->
|
||||
<div v-if="isSmartSpeedEnabled && !isCasting" class="absolute -top-6 right-0 text-xs text-yellow-400 flex items-center bg-black/50 px-2 py-0.5 rounded shadow-sm z-10 pointer-events-none">
|
||||
<span class="material-symbols text-sm mr-1">bolt</span>
|
||||
<span>Smart Speed Active</span>
|
||||
</div>
|
||||
|
||||
<!-- Track -->
|
||||
<div ref="track" class="w-full h-2 bg-gray-700 relative cursor-pointer transform duration-100 hover:scale-y-125 overflow-hidden" @mousemove="mousemoveTrack" @mouseleave="mouseleaveTrack" @click.stop="clickTrack">
|
||||
<div ref="readyTrack" class="h-full bg-gray-600 absolute top-0 left-0 pointer-events-none" />
|
||||
|
|
@ -63,6 +69,12 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
isCasting() {
|
||||
return this.$store.state.globals.isCasting || false
|
||||
},
|
||||
isSmartSpeedEnabled() {
|
||||
return this.$store.getters['user/getUserSetting']('enableSmartSpeed') || false
|
||||
},
|
||||
_playbackRate() {
|
||||
if (!this.playbackRate || isNaN(this.playbackRate)) return 1
|
||||
return this.playbackRate
|
||||
|
|
|
|||
|
|
@ -326,11 +326,22 @@ export default {
|
|||
|
||||
if (this.$refs.trackbar) this.$refs.trackbar.setUseChapterTrack(this.useChapterTrack)
|
||||
this.setPlaybackRate(this.playbackRate)
|
||||
|
||||
const enableSmartSpeed = this.$store.getters['user/getUserSetting']('enableSmartSpeed')
|
||||
const smartSpeedRatio = this.$store.getters['user/getUserSetting']('smartSpeedRatio')
|
||||
if (this.playerHandler && this.playerHandler.isPlayingLocalItem) {
|
||||
this.playerHandler.setSmartSpeed(enableSmartSpeed || false, smartSpeedRatio || 2.5)
|
||||
}
|
||||
},
|
||||
settingsUpdated(settings) {
|
||||
if (settings.playbackRate && this.playbackRate !== settings.playbackRate) {
|
||||
this.setPlaybackRate(settings.playbackRate)
|
||||
}
|
||||
if (this.playerHandler && this.playerHandler.isPlayingLocalItem && (settings.enableSmartSpeed !== undefined || settings.smartSpeedRatio !== undefined)) {
|
||||
const enableSmartSpeed = settings.enableSmartSpeed !== undefined ? settings.enableSmartSpeed : this.$store.getters['user/getUserSetting']('enableSmartSpeed')
|
||||
const smartSpeedRatio = settings.smartSpeedRatio !== undefined ? settings.smartSpeedRatio : this.$store.getters['user/getUserSetting']('smartSpeedRatio')
|
||||
this.playerHandler.setSmartSpeed(enableSmartSpeed || false, smartSpeedRatio || 2.5)
|
||||
}
|
||||
},
|
||||
closePlayer() {
|
||||
if (this.isFullscreen) {
|
||||
|
|
|
|||
|
|
@ -383,6 +383,13 @@ export default class PlayerHandler {
|
|||
this.player.setPlaybackRate(playbackRate)
|
||||
}
|
||||
|
||||
setSmartSpeed(enabled, ratio = 2.5) {
|
||||
if (this.player && this.player instanceof LocalAudioPlayer) {
|
||||
this.player.smartSpeedRatio = ratio
|
||||
this.player.setSmartSpeed(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
seek(time, shouldSync = true) {
|
||||
if (!this.player) return
|
||||
this.player.seek(time, this.playerPlaying)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export const state = () => ({
|
|||
authorSortDesc: false,
|
||||
jumpForwardAmount: 10,
|
||||
jumpBackwardAmount: 10,
|
||||
enableSmartSpeed: false
|
||||
enableSmartSpeed: false,
|
||||
smartSpeedRatio: 2.5
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -200,5 +201,17 @@ export const mutations = {
|
|||
if (!settings) return
|
||||
localStorage.setItem('userSettings', JSON.stringify(settings))
|
||||
state.settings = settings
|
||||
},
|
||||
SET_SMART_SPEED_ENABLED(state, enabled) {
|
||||
state.settings.enableSmartSpeed = enabled !== undefined ? enabled : !state.settings.enableSmartSpeed
|
||||
localStorage.setItem('userSettings', JSON.stringify(state.settings))
|
||||
},
|
||||
SET_SMART_SPEED_RATIO(state, ratio) {
|
||||
let clampedRatio = Number(ratio)
|
||||
if (isNaN(clampedRatio)) clampedRatio = 2.5
|
||||
if (clampedRatio < 1.5) clampedRatio = 1.5
|
||||
if (clampedRatio > 5.0) clampedRatio = 5.0
|
||||
state.settings.smartSpeedRatio = clampedRatio
|
||||
localStorage.setItem('userSettings', JSON.stringify(state.settings))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -712,6 +712,8 @@
|
|||
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
|
||||
"LabelUseAdvancedOptions": "Use Advanced Options",
|
||||
"LabelUseChapterTrack": "Use chapter track",
|
||||
"LabelEnableSmartSpeed": "Enable Smart Speed",
|
||||
"LabelSmartSpeedRatio": "Smart Speed Compression Ratio",
|
||||
"LabelUseFullTrack": "Use full track",
|
||||
"LabelUseZeroForUnlimited": "Use 0 for unlimited",
|
||||
"LabelUser": "User",
|
||||
|
|
|
|||
65
test/client/store/user.test.js
Normal file
65
test/client/store/user.test.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { state, mutations } from '../../../client/store/user.js'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('User Store Mutations', () => {
|
||||
let mockState
|
||||
|
||||
beforeEach(() => {
|
||||
mockState = state()
|
||||
// Mock localStorage
|
||||
global.localStorage = {
|
||||
store: {},
|
||||
getItem(key) {
|
||||
return this.store[key] || null
|
||||
},
|
||||
setItem(key, value) {
|
||||
this.store[key] = value
|
||||
},
|
||||
removeItem(key) {
|
||||
delete this.store[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('Default state has enableSmartSpeed = false', () => {
|
||||
expect(mockState.settings.enableSmartSpeed).to.be.false
|
||||
})
|
||||
|
||||
it('Default state has smartSpeedRatio = 2.5', () => {
|
||||
expect(mockState.settings.smartSpeedRatio).to.equal(2.5)
|
||||
})
|
||||
|
||||
it('SET_SMART_SPEED_ENABLED mutation toggles the value', () => {
|
||||
mutations.SET_SMART_SPEED_ENABLED(mockState)
|
||||
expect(mockState.settings.enableSmartSpeed).to.be.true
|
||||
mutations.SET_SMART_SPEED_ENABLED(mockState)
|
||||
expect(mockState.settings.enableSmartSpeed).to.be.false
|
||||
|
||||
// Check setting explicitly
|
||||
mutations.SET_SMART_SPEED_ENABLED(mockState, true)
|
||||
expect(mockState.settings.enableSmartSpeed).to.be.true
|
||||
})
|
||||
|
||||
it('SET_SMART_SPEED_RATIO mutation sets the value', () => {
|
||||
mutations.SET_SMART_SPEED_RATIO(mockState, 3.0)
|
||||
expect(mockState.settings.smartSpeedRatio).to.equal(3.0)
|
||||
})
|
||||
|
||||
it('Ratio is clamped to valid range [1.5, 5.0]', () => {
|
||||
mutations.SET_SMART_SPEED_RATIO(mockState, 1.0)
|
||||
expect(mockState.settings.smartSpeedRatio).to.equal(1.5)
|
||||
|
||||
mutations.SET_SMART_SPEED_RATIO(mockState, 6.0)
|
||||
expect(mockState.settings.smartSpeedRatio).to.equal(5.0)
|
||||
})
|
||||
|
||||
it('Settings persist to localStorage', () => {
|
||||
mutations.SET_SMART_SPEED_ENABLED(mockState, true)
|
||||
let savedSettings = JSON.parse(localStorage.getItem('userSettings'))
|
||||
expect(savedSettings.enableSmartSpeed).to.be.true
|
||||
|
||||
mutations.SET_SMART_SPEED_RATIO(mockState, 4.0)
|
||||
savedSettings = JSON.parse(localStorage.getItem('userSettings'))
|
||||
expect(savedSettings.smartSpeedRatio).to.equal(4.0)
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue