mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Moving settings to be user specific, adding playbackRate setting, update playbackRate picker to go up to 3x
This commit is contained in:
		
							parent
							
								
									2548aba840
								
							
						
					
					
						commit
						f83c5dd440
					
				| @ -27,7 +27,7 @@ | ||||
|           <div class="cursor-pointer flex items-center justify-center text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="forward10"> | ||||
|             <span class="material-icons text-3xl">forward_10</span> | ||||
|           </div> | ||||
|           <controls-playback-speed-control v-model="playbackRate" @change="updatePlaybackRate" /> | ||||
|           <controls-playback-speed-control v-model="playbackRate" @change="playbackRateChanged" /> | ||||
|         </template> | ||||
|         <template v-else> | ||||
|           <div class="cursor-pointer p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin"> | ||||
| @ -89,7 +89,7 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     token() { | ||||
|       return this.$store.getters.getToken | ||||
|       return this.$store.getters['user/getToken'] | ||||
|     }, | ||||
|     totalDurationPretty() { | ||||
|       return this.$secondsToTimestamp(this.totalDuration) | ||||
| @ -130,12 +130,22 @@ export default { | ||||
|     }, | ||||
|     updatePlaybackRate(playbackRate) { | ||||
|       if (this.audioEl) { | ||||
|         console.log('UpdatePlaybackRate', playbackRate) | ||||
|         try { | ||||
|           this.audioEl.playbackRate = playbackRate | ||||
|           this.audioEl.defaultPlaybackRate = playbackRate | ||||
|         } catch (error) { | ||||
|           console.error('Update playback rate failed', error) | ||||
|         } | ||||
|       } else { | ||||
|         console.error('No Audio El updatePlaybackRate') | ||||
|       } | ||||
|     }, | ||||
|     playbackRateChanged(playbackRate) { | ||||
|       this.updatePlaybackRate(playbackRate) | ||||
|       this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => { | ||||
|         console.error('Failed to update settings', err) | ||||
|       }) | ||||
|     }, | ||||
|     mousemoveTrack(e) { | ||||
|       var offsetX = e.offsetX | ||||
|       var time = (offsetX / this.trackWidth) * this.totalDuration | ||||
| @ -355,7 +365,8 @@ export default { | ||||
|       this.hlsInstance = new Hls(hlsOptions) | ||||
|       var audio = this.$refs.audio | ||||
|       audio.volume = this.volume | ||||
|       audio.playbackRate = this.playbackRate | ||||
|       audio.defaultPlaybackRate = this.playbackRate | ||||
| 
 | ||||
|       this.hlsInstance.attachMedia(audio) | ||||
|       this.hlsInstance.on(Hls.Events.MEDIA_ATTACHED, () => { | ||||
|         // console.log('[HLS] MEDIA ATTACHED') | ||||
| @ -410,17 +421,27 @@ export default { | ||||
|       this.set(this.url, startTime, true) | ||||
|     }, | ||||
|     init() { | ||||
|       this.playbackRate = this.$store.getters['user/getUserSetting']('playbackRate') || 1 | ||||
| 
 | ||||
|       this.audioEl = this.$refs.audio | ||||
|       if (this.$refs.track) { | ||||
|         this.trackWidth = this.$refs.track.clientWidth | ||||
|       } else { | ||||
|         console.error('Track not loaded', this.$refs) | ||||
|       } | ||||
|     }, | ||||
|     settingsUpdated(settings) { | ||||
|       if (settings.playbackRate && this.playbackRate !== settings.playbackRate) { | ||||
|         this.updatePlaybackRate(settings.playbackRate) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     // this.$nextTick(this.init) | ||||
|     this.$store.commit('user/addSettingsListener', { id: 'audioplayer', meth: this.settingsUpdated }) | ||||
|     this.init() | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     this.$store.commit('user/removeSettingsListener', 'audioplayer') | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -43,7 +43,7 @@ export default { | ||||
|       return this.$route.name !== 'index' | ||||
|     }, | ||||
|     user() { | ||||
|       return this.$store.state.user | ||||
|       return this.$store.state.user.user | ||||
|     }, | ||||
|     username() { | ||||
|       return this.user ? this.user.username : 'err' | ||||
|  | ||||
| @ -32,13 +32,13 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     userAudiobooks() { | ||||
|       return this.$store.state.user ? this.$store.state.user.audiobooks || {} : {} | ||||
|       return this.$store.state.user.user ? this.$store.state.user.user.audiobooks || {} : {} | ||||
|     }, | ||||
|     audiobooks() { | ||||
|       return this.$store.state.audiobooks.audiobooks | ||||
|     }, | ||||
|     filterOrderKey() { | ||||
|       return this.$store.getters['settings/getFilterOrderKey'] | ||||
|       return this.$store.getters['user/getFilterOrderKey'] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @ -100,7 +100,7 @@ export default { | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$store.commit('audiobooks/addListener', { id: 'bookshelf', meth: this.audiobooksUpdated }) | ||||
|     this.$store.commit('settings/addListener', { id: 'bookshelf', meth: this.settingsUpdated }) | ||||
|     this.$store.commit('user/addSettingsListener', { id: 'bookshelf', meth: this.settingsUpdated }) | ||||
| 
 | ||||
|     this.$store.dispatch('audiobooks/load') | ||||
|     this.init() | ||||
| @ -108,7 +108,7 @@ export default { | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     this.$store.commit('audiobooks/removeListener', 'bookshelf') | ||||
|     this.$store.commit('settings/removeListener', 'bookshelf') | ||||
|     this.$store.commit('user/removeSettingsListener', 'bookshelf') | ||||
|     window.removeEventListener('resize', this.resize) | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -14,7 +14,8 @@ | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       settings: {} | ||||
|       settings: {}, | ||||
|       hasInit: false | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
| @ -30,15 +31,24 @@ export default { | ||||
|       this.saveSettings() | ||||
|     }, | ||||
|     saveSettings() { | ||||
|       // Send to server | ||||
|       this.$store.commit('settings/setSettings', this.settings) | ||||
|       this.$store.commit('user/setSettings', this.settings) // Immediate update | ||||
|       this.$store.dispatch('user/updateUserSettings', this.settings) | ||||
|     }, | ||||
|     init() { | ||||
|       this.settings = { ...this.$store.state.settings.settings } | ||||
|       this.settings = { ...this.$store.state.user.settings } | ||||
|     }, | ||||
|     settingsUpdated(settings) { | ||||
|       for (const key in settings) { | ||||
|         this.settings[key] = settings[key] | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.init() | ||||
|     this.$store.commit('user/addSettingsListener', { id: 'bookshelftoolbar', meth: this.settingsUpdated }) | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     this.$store.commit('user/removeSettingsListener', 'bookshelftoolbar') | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       audioPlayerReady: false, | ||||
|       lastServerUpdateSentSeconds: 0, | ||||
|       stream: null | ||||
|     } | ||||
| @ -32,7 +33,7 @@ export default { | ||||
|       return 'Logo.png' | ||||
|     }, | ||||
|     user() { | ||||
|       return this.$store.state.user | ||||
|       return this.$store.state.user.user | ||||
|     }, | ||||
|     isLoading() { | ||||
|       if (!this.streamAudiobook) return false | ||||
| @ -63,6 +64,7 @@ export default { | ||||
|   }, | ||||
|   methods: { | ||||
|     audioPlayerMounted() { | ||||
|       this.audioPlayerReady = true | ||||
|       if (this.stream) { | ||||
|         console.log('[STREAM-CONTAINER] audioPlayerMounted w/ Stream', this.stream) | ||||
|         this.openStream() | ||||
| @ -104,7 +106,7 @@ export default { | ||||
|       if (this.$refs.audioPlayer) { | ||||
|         console.log('[STREAM-CONTAINER] streamOpen', stream) | ||||
|         this.openStream() | ||||
|       } else { | ||||
|       } else if (this.audioPlayerReady) { | ||||
|         console.error('No Audio Ref') | ||||
|       } | ||||
|     }, | ||||
|  | ||||
| @ -8,14 +8,26 @@ | ||||
|         <div class="arrow-down" /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="w-full h-full no-scroll flex"> | ||||
|         <template v-for="(rate, index) in rates"> | ||||
|           <div :key="rate" class="flex items-center justify-center border-black-300 w-11 hover:bg-black hover:bg-opacity-10 cursor-pointer" :class="index < rates.length - 1 ? 'border-r' : ''" style="min-width: 44px; max-width: 44px" @click="set(rate)"> | ||||
|             <p class="text-xs text-center font-mono">{{ rate.toFixed(1) }}<span class="text-sm">⨯</span></p> | ||||
|       <div class="w-full h-full no-scroll flex px-7 relative overflow-hidden"> | ||||
|         <div class="absolute left-0 top-0 h-full w-7 border-r border-black-300 bg-black-300 rounded-l-lg flex items-center justify-center cursor-pointer" :class="rateIndex === 0 ? 'bg-black-400 text-gray-400' : 'hover:bg-black-200'" @mousedown.prevent @mouseup.prevent @click="leftArrowClick"> | ||||
|           <span class="material-icons" style="font-size: 1.2rem">chevron_left</span> | ||||
|         </div> | ||||
|         <div class="overflow-hidden relative" style="width: 220px"> | ||||
|           <div class="flex items-center h-full absolute top-0 left-0 transition-transform duration-100" :style="{ transform: `translateX(${xPos}px)` }"> | ||||
|             <template v-for="rate in rates"> | ||||
|               <div :key="rate" class="h-full border-black-300 w-11 cursor-pointer border-r" :class="value === rate ? 'bg-black-100' : 'hover:bg-black hover:bg-opacity-10'" style="min-width: 44px; max-width: 44px" @click="set(rate)"> | ||||
|                 <div class="w-full h-full flex justify-center items-center"> | ||||
|                   <p class="text-xs text-center font-mono">{{ rate }}<span class="text-sm">⨯</span></p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </template> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="absolute top-0 right-0 h-full w-7 bg-black-300 rounded-r-lg flex items-center justify-center cursor-pointer" :class="rateIndex === rates.length - numVisible ? 'bg-black-400 text-gray-400' : 'hover:bg-black-200'" @mousedown.prevent @mouseup.prevent @click="rightArrowClick"> | ||||
|           <span class="material-icons" style="font-size: 1.2rem">chevron_right</span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| @ -29,7 +41,9 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       showMenu: false | ||||
|       showMenu: false, | ||||
|       rateIndex: 1, | ||||
|       numVisible: 5 | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
| @ -42,7 +56,10 @@ export default { | ||||
|       } | ||||
|     }, | ||||
|     rates() { | ||||
|       return [0.5, 0.8, 1.0, 1.3, 1.5, 2.0] | ||||
|       return [0.25, 0.5, 0.8, 1, 1.3, 1.5, 2, 2.5, 3] | ||||
|     }, | ||||
|     xPos() { | ||||
|       return -1 * this.rateIndex * 44 | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @ -55,6 +72,12 @@ export default { | ||||
|       this.playbackRate = newPlaybackRate | ||||
|       if (hasChanged) this.$emit('change', newPlaybackRate) | ||||
|       this.showMenu = false | ||||
|     }, | ||||
|     leftArrowClick() { | ||||
|       this.rateIndex = Math.max(0, this.rateIndex - 4) | ||||
|     }, | ||||
|     rightArrowClick() { | ||||
|       this.rateIndex = Math.min(this.rates.length - this.numVisible, this.rateIndex + 4) | ||||
|     } | ||||
|   }, | ||||
|   mounted() {} | ||||
|  | ||||
| @ -92,7 +92,7 @@ export default { | ||||
|       return this.audiobook ? this.audiobook.book || {} : {} | ||||
|     }, | ||||
|     userAudiobook() { | ||||
|       return this.$store.getters['getUserAudiobook'](this.audiobookId) | ||||
|       return this.$store.getters['user/getUserAudiobook'](this.audiobookId) | ||||
|     }, | ||||
|     userProgress() { | ||||
|       return this.userAudiobook ? this.userAudiobook.progress : 0 | ||||
|  | ||||
| @ -24,13 +24,13 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     user() { | ||||
|       return this.$store.state.user | ||||
|       return this.$store.state.user.user | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     connect() { | ||||
|       console.log('[SOCKET] Connected') | ||||
|       var token = this.$store.getters.getToken | ||||
|       var token = this.$store.getters['user/getToken'] | ||||
|       this.socket.emit('auth', token) | ||||
|     }, | ||||
|     connectError() {}, | ||||
| @ -49,7 +49,8 @@ export default { | ||||
|         } | ||||
|       } | ||||
|       if (payload.user) { | ||||
|         this.$store.commit('setUser', payload.user) | ||||
|         this.$store.commit('user/setUser', payload.user) | ||||
|         this.$store.commit('user/setSettings', payload.user.settings) | ||||
|       } | ||||
|     }, | ||||
|     streamOpen(stream) { | ||||
| @ -92,8 +93,9 @@ export default { | ||||
|       this.$store.commit('setScanProgress', progress) | ||||
|     }, | ||||
|     userUpdated(user) { | ||||
|       if (this.$store.state.user.id === user.id) { | ||||
|         this.$store.commit('setUser', user) | ||||
|       if (this.$store.state.user.user.id === user.id) { | ||||
|         this.$store.commit('user/setUser', user) | ||||
|         this.$store.commit('user/setSettings', user.settings) | ||||
|       } | ||||
|     }, | ||||
|     initializeSocket() { | ||||
| @ -139,7 +141,7 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   beforeMount() { | ||||
|     if (!this.$store.state.user) { | ||||
|     if (!this.$store.state.user.user) { | ||||
|       this.$router.replace(`/login?redirect=${this.$route.path}`) | ||||
|     } | ||||
|   }, | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "audiobookshelf-client", | ||||
|   "version": "0.9.65-beta", | ||||
|   "version": "0.9.7-beta", | ||||
|   "description": "Audiobook manager and player", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|  | ||||
| @ -43,7 +43,7 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     user() { | ||||
|       return this.$store.state.user || null | ||||
|       return this.$store.state.user.user || null | ||||
|     }, | ||||
|     username() { | ||||
|       return this.user.username | ||||
|  | ||||
| @ -66,7 +66,7 @@ export default { | ||||
|     draggable | ||||
|   }, | ||||
|   async asyncData({ store, params, app, redirect, route }) { | ||||
|     if (!store.state.user) { | ||||
|     if (!store.state.user.user) { | ||||
|       return redirect(`/login?redirect=${route.path}`) | ||||
|     } | ||||
|     var audiobook = await app.$axios.$get(`/api/audiobook/${params.id}`).catch((error) => { | ||||
|  | ||||
| @ -63,7 +63,7 @@ | ||||
| <script> | ||||
| export default { | ||||
|   async asyncData({ store, params, app, redirect, route }) { | ||||
|     if (!store.state.user) { | ||||
|     if (!store.state.user.user) { | ||||
|       return redirect(`/login?redirect=${route.path}`) | ||||
|     } | ||||
|     var audiobook = await app.$axios.$get(`/api/audiobook/${params.id}`).catch((error) => { | ||||
| @ -163,7 +163,7 @@ export default { | ||||
|       return this.book.description || 'No Description' | ||||
|     }, | ||||
|     userAudiobooks() { | ||||
|       return this.$store.state.user ? this.$store.state.user.audiobooks || {} : {} | ||||
|       return this.$store.state.user.user ? this.$store.state.user.user.audiobooks || {} : {} | ||||
|     }, | ||||
|     userAudiobook() { | ||||
|       return this.userAudiobooks[this.audiobookId] || null | ||||
|  | ||||
| @ -49,7 +49,7 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     user() { | ||||
|       return this.$store.state.user | ||||
|       return this.$store.state.user.user | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @ -71,7 +71,7 @@ export default { | ||||
|       } else if (authRes.error) { | ||||
|         this.error = authRes.error | ||||
|       } else { | ||||
|         this.$store.commit('setUser', authRes.user) | ||||
|         this.$store.commit('user/setUser', authRes.user) | ||||
|       } | ||||
|       this.processing = false | ||||
|     }, | ||||
| @ -90,7 +90,7 @@ export default { | ||||
|               } | ||||
|             }) | ||||
|             .then((res) => { | ||||
|               this.$store.commit('setUser', res.user) | ||||
|               this.$store.commit('user/setUser', res.user) | ||||
|               this.processing = false | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|  | ||||
| @ -4,7 +4,7 @@ export default function ({ $axios, store }) { | ||||
|     if (config.url.startsWith('http:') || config.url.startsWith('https:')) { | ||||
|       return | ||||
|     } | ||||
|     var bearerToken = store.state.user ? store.state.user.token : null | ||||
|     var bearerToken = store.state.user.user ? store.state.user.user.token : null | ||||
|     // console.log('Bearer token', bearerToken)
 | ||||
|     if (bearerToken) { | ||||
|       config.headers.common['Authorization'] = `Bearer ${bearerToken}` | ||||
|  | ||||
| @ -13,7 +13,7 @@ export const state = () => ({ | ||||
| export const getters = { | ||||
|   getFiltered: (state, getters, rootState) => () => { | ||||
|     var filtered = state.audiobooks | ||||
|     var settings = rootState.settings.settings || {} | ||||
|     var settings = rootState.user.settings || {} | ||||
|     var filterBy = settings.filterBy || '' | ||||
| 
 | ||||
|     var searchGroups = ['genres', 'tags', 'series'] | ||||
| @ -27,7 +27,7 @@ export const getters = { | ||||
|     return filtered | ||||
|   }, | ||||
|   getFilteredAndSorted: (state, getters, rootState) => () => { | ||||
|     var settings = rootState.settings.settings | ||||
|     var settings = rootState.user.settings | ||||
|     var direction = settings.orderDesc ? 'desc' : 'asc' | ||||
| 
 | ||||
|     var filtered = getters.getFiltered() | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| 
 | ||||
| export const state = () => ({ | ||||
|   user: null, | ||||
|   streamAudiobook: null, | ||||
|   showEditModal: false, | ||||
|   selectedAudiobook: null, | ||||
| @ -10,26 +9,11 @@ export const state = () => ({ | ||||
|   developerMode: false | ||||
| }) | ||||
| 
 | ||||
| export const getters = { | ||||
|   getToken: (state) => { | ||||
|     return state.user ? state.user.token : null | ||||
|   }, | ||||
|   getUserAudiobook: (state) => (audiobookId) => { | ||||
|     return state.user && state.user.audiobooks ? state.user.audiobooks[audiobookId] || null : null | ||||
|   } | ||||
| } | ||||
| export const getters = {} | ||||
| 
 | ||||
| export const actions = { | ||||
| 
 | ||||
| } | ||||
| export const actions = {} | ||||
| 
 | ||||
| export const mutations = { | ||||
|   setUser(state, user) { | ||||
|     state.user = user | ||||
|     if (user.token) { | ||||
|       localStorage.setItem('token', user.token) | ||||
|     } | ||||
|   }, | ||||
|   setStreamAudiobook(state, audiobook) { | ||||
|     state.playOnLoad = true | ||||
|     state.streamAudiobook = audiobook | ||||
|  | ||||
| @ -1,39 +0,0 @@ | ||||
| 
 | ||||
| export const state = () => ({ | ||||
|   settings: { | ||||
|     orderBy: 'book.title', | ||||
|     orderDesc: false, | ||||
|     filterBy: 'all' | ||||
|   }, | ||||
| 
 | ||||
|   listeners: [] | ||||
| }) | ||||
| 
 | ||||
| export const getters = { | ||||
|   getFilterOrderKey: (state) => { | ||||
|     return Object.values(state.settings).join('-') | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const actions = { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const mutations = { | ||||
|   setSettings(state, settings) { | ||||
|     state.settings = { | ||||
|       ...settings | ||||
|     } | ||||
|     state.listeners.forEach((listener) => { | ||||
|       listener.meth() | ||||
|     }) | ||||
|   }, | ||||
|   addListener(state, listener) { | ||||
|     var index = state.listeners.findIndex(l => l.id === listener.id) | ||||
|     if (index >= 0) state.listeners.splice(index, 1, listener) | ||||
|     else state.listeners.push(listener) | ||||
|   }, | ||||
|   removeListener(state, listenerId) { | ||||
|     state.listeners = state.listeners.filter(l => l.id !== listenerId) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										81
									
								
								client/store/user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								client/store/user.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| 
 | ||||
| export const state = () => ({ | ||||
|   user: null, | ||||
|   settings: { | ||||
|     orderBy: 'book.title', | ||||
|     orderDesc: false, | ||||
|     filterBy: 'all', | ||||
|     playbackRate: 1 | ||||
|   }, | ||||
|   settingsListeners: [] | ||||
| }) | ||||
| 
 | ||||
| export const getters = { | ||||
|   getToken: (state) => { | ||||
|     return state.user ? state.user.token : null | ||||
|   }, | ||||
|   getUserAudiobook: (state) => (audiobookId) => { | ||||
|     return state.user && state.user.audiobooks ? state.user.audiobooks[audiobookId] || null : null | ||||
|   }, | ||||
|   getUserSetting: (state) => (key) => { | ||||
|     return state.settings ? state.settings[key] || null : null | ||||
|   }, | ||||
|   getFilterOrderKey: (state) => { | ||||
|     return Object.values(state.settings).join('-') | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const actions = { | ||||
|   updateUserSettings({ commit }, payload) { | ||||
|     var updatePayload = { | ||||
|       ...payload | ||||
|     } | ||||
|     return this.$axios.$patch('/api/user/settings', updatePayload).then((result) => { | ||||
|       if (result.success) { | ||||
|         commit('setSettings', result.settings) | ||||
|         console.log('Settings updated', result.settings) | ||||
|         return true | ||||
|       } else { | ||||
|         return false | ||||
|       } | ||||
|     }).catch((error) => { | ||||
|       console.error('Failed to update settings', error) | ||||
|       return false | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const mutations = { | ||||
|   setUser(state, user) { | ||||
|     state.user = user | ||||
|     if (user && user.token) { | ||||
|       localStorage.setItem('token', user.token) | ||||
|     } else if (user) { | ||||
|       localStorage.removeItem('token') | ||||
|     } | ||||
|   }, | ||||
|   setSettings(state, settings) { | ||||
|     if (!settings) return | ||||
| 
 | ||||
|     var hasChanges = false | ||||
|     for (const key in settings) { | ||||
|       if (state.settings[key] !== settings[key]) { | ||||
|         hasChanges = true | ||||
|         state.settings[key] = settings[key] | ||||
|       } | ||||
|     } | ||||
|     if (hasChanges) { | ||||
|       state.settingsListeners.forEach((listener) => { | ||||
|         listener.meth(state.settings) | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   addSettingsListener(state, listener) { | ||||
|     var index = state.settingsListeners.findIndex(l => l.id === listener.id) | ||||
|     if (index >= 0) state.settingsListeners.splice(index, 1, listener) | ||||
|     else state.settingsListeners.push(listener) | ||||
|   }, | ||||
|   removeSettingsListener(state, listenerId) { | ||||
|     state.settingsListeners = state.settingsListeners.filter(l => l.id !== listenerId) | ||||
|   } | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "audiobookshelf", | ||||
|   "version": "0.9.65-beta", | ||||
|   "version": "0.9.7-beta", | ||||
|   "description": "Self-hosted audiobook server for managing and playing audiobooks.", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| const express = require('express') | ||||
| const Logger = require('./Logger') | ||||
| const { isObject } = require('./utils/index') | ||||
| 
 | ||||
| class ApiController { | ||||
|   constructor(db, scanner, auth, streamManager, rssFeeds, emitter) { | ||||
| @ -32,6 +33,7 @@ class ApiController { | ||||
|     this.router.get('/users', this.getUsers.bind(this)) | ||||
|     this.router.delete('/user/audiobook/:id', this.resetUserAudiobookProgress.bind(this)) | ||||
|     this.router.patch('/user/password', this.userChangePassword.bind(this)) | ||||
|     this.router.patch('/user/settings', this.userUpdateSettings.bind(this)) | ||||
| 
 | ||||
|     this.router.post('/authorize', this.authorize.bind(this)) | ||||
| 
 | ||||
| @ -185,6 +187,21 @@ class ApiController { | ||||
|     res.json(feed) | ||||
|   } | ||||
| 
 | ||||
|   async userUpdateSettings(req, res) { | ||||
|     var settingsUpdate = req.body | ||||
|     if (!settingsUpdate || !isObject(settingsUpdate)) { | ||||
|       return res.sendStatus(500) | ||||
|     } | ||||
|     var madeUpdates = req.user.updateSettings(settingsUpdate) | ||||
|     if (madeUpdates) { | ||||
|       await this.db.updateEntity('user', req.user) | ||||
|     } | ||||
|     return res.json({ | ||||
|       success: true, | ||||
|       settings: req.user.settings | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   getGenres(req, res) { | ||||
|     res.json({ | ||||
|       genres: this.db.getGenres() | ||||
|  | ||||
| @ -8,12 +8,22 @@ class User { | ||||
|     this.token = null | ||||
|     this.createdAt = null | ||||
|     this.audiobooks = null | ||||
|     this.settings = {} | ||||
| 
 | ||||
|     if (user) { | ||||
|       this.construct(user) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getDefaultUserSettings() { | ||||
|     return { | ||||
|       orderBy: 'book.title', | ||||
|       orderDesc: false, | ||||
|       filterBy: 'all', | ||||
|       playbackRate: 1 | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   toJSON() { | ||||
|     return { | ||||
|       id: this.id, | ||||
| @ -23,7 +33,8 @@ class User { | ||||
|       stream: this.stream, | ||||
|       token: this.token, | ||||
|       audiobooks: this.audiobooks, | ||||
|       createdAt: this.createdAt | ||||
|       createdAt: this.createdAt, | ||||
|       settings: this.settings | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -35,7 +46,8 @@ class User { | ||||
|       stream: this.stream, | ||||
|       token: this.token, | ||||
|       audiobooks: this.audiobooks, | ||||
|       createdAt: this.createdAt | ||||
|       createdAt: this.createdAt, | ||||
|       settings: this.settings | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -48,6 +60,7 @@ class User { | ||||
|     this.token = user.token | ||||
|     this.audiobooks = user.audiobooks || null | ||||
|     this.createdAt = user.createdAt | ||||
|     this.settings = user.settings || this.getDefaultUserSettings() | ||||
|   } | ||||
| 
 | ||||
|   updateAudiobookProgress(stream) { | ||||
| @ -64,6 +77,32 @@ class User { | ||||
|     this.audiobooks[stream.audiobookId].currentTime = stream.clientCurrentTime | ||||
|   } | ||||
| 
 | ||||
|   // Returns Boolean If update was made
 | ||||
|   updateSettings(settings) { | ||||
|     if (!this.settings) { | ||||
|       this.settings = { ...settings } | ||||
|       return true | ||||
|     } | ||||
|     var madeUpdates = false | ||||
| 
 | ||||
|     for (const key in this.settings) { | ||||
|       if (settings[key] !== undefined && this.settings[key] !== settings[key]) { | ||||
|         this.settings[key] = settings[key] | ||||
|         madeUpdates = true | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Check if new settings update has keys not currently in user settings
 | ||||
|     for (const key in settings) { | ||||
|       if (settings[key] !== undefined && this.settings[key] === undefined) { | ||||
|         this.settings[key] = settings[key] | ||||
|         madeUpdates = true | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return madeUpdates | ||||
|   } | ||||
| 
 | ||||
|   resetAudiobookProgress(audiobookId) { | ||||
|     if (!this.audiobooks || !this.audiobooks[audiobookId]) { | ||||
|       return false | ||||
|  | ||||
| @ -41,3 +41,7 @@ const cleanString = (str) => { | ||||
|   return cleaned | ||||
| } | ||||
| module.exports.cleanString = cleanString | ||||
| 
 | ||||
| module.exports.isObject = (val) => { | ||||
|   return val !== null && typeof val === 'object' | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user