mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add Subtitle and Narrarator fields, add server settings object, scanner to parse out subtitles
This commit is contained in:
		
							parent
							
								
									e566c6c9d5
								
							
						
					
					
						commit
						af05e78cdf
					
				| @ -1,5 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <modals-modal v-model="show" :width="800" :height="500" :processing="processing"> |   <modals-modal v-model="show" :width="800" :height="height" :processing="processing" :content-margin-top="75"> | ||||||
|     <template #outer> |     <template #outer> | ||||||
|       <div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden"> |       <div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden"> | ||||||
|         <p class="font-book text-3xl text-white truncate">{{ title }}</p> |         <p class="font-book text-3xl text-white truncate">{{ title }}</p> | ||||||
| @ -79,6 +79,10 @@ export default { | |||||||
|         this.$store.commit('setShowEditModal', val) |         this.$store.commit('setShowEditModal', val) | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     height() { | ||||||
|  |       var maxHeightAllowed = window.innerHeight - 150 | ||||||
|  |       return Math.min(maxHeightAllowed, 650) | ||||||
|  |     }, | ||||||
|     tabName() { |     tabName() { | ||||||
|       var _tab = this.tabs.find((t) => t.id === this.selectedTab) |       var _tab = this.tabs.find((t) => t.id === this.selectedTab) | ||||||
|       return _tab ? _tab.component : '' |       return _tab ? _tab.component : '' | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
|       <span class="material-icons text-4xl">close</span> |       <span class="material-icons text-4xl">close</span> | ||||||
|     </div> |     </div> | ||||||
|     <slot name="outer" /> |     <slot name="outer" /> | ||||||
|     <div ref="content" style="min-width: 400px; min-height: 200px" class="relative text-white" :style="{ height: modalHeight, width: modalWidth }" v-click-outside="clickBg"> |     <div ref="content" style="min-width: 400px; min-height: 200px" class="relative text-white" :style="{ height: modalHeight, width: modalWidth, marginTop: contentMarginTop + 'px' }" v-click-outside="clickBg"> | ||||||
|       <slot /> |       <slot /> | ||||||
|       <div v-if="processing" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-black bg-opacity-60 rounded-lg flex items-center justify-center"> |       <div v-if="processing" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-black bg-opacity-60 rounded-lg flex items-center justify-center"> | ||||||
|         <ui-loading-indicator /> |         <ui-loading-indicator /> | ||||||
| @ -31,6 +31,10 @@ export default { | |||||||
|     height: { |     height: { | ||||||
|       type: [String, Number], |       type: [String, Number], | ||||||
|       default: 'unset' |       default: 'unset' | ||||||
|  |     }, | ||||||
|  |     contentMarginTop: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 50 | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|  | |||||||
| @ -12,6 +12,8 @@ | |||||||
|     <form @submit.prevent="submitForm"> |     <form @submit.prevent="submitForm"> | ||||||
|       <ui-text-input-with-label v-model="details.title" label="Title" /> |       <ui-text-input-with-label v-model="details.title" label="Title" /> | ||||||
| 
 | 
 | ||||||
|  |       <ui-text-input-with-label v-model="details.subtitle" label="Subtitle" class="mt-2" /> | ||||||
|  | 
 | ||||||
|       <div class="flex mt-2 -mx-1"> |       <div class="flex mt-2 -mx-1"> | ||||||
|         <div class="w-3/4 px-1"> |         <div class="w-3/4 px-1"> | ||||||
|           <ui-text-input-with-label v-model="details.author" label="Author" /> |           <ui-text-input-with-label v-model="details.author" label="Author" /> | ||||||
| @ -41,7 +43,13 @@ | |||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div class="flex py-4"> |       <div class="flex mt-2 -mx-1"> | ||||||
|  |         <div class="w-1/2 px-1"> | ||||||
|  |           <ui-text-input-with-label v-model="details.narrarator" label="Narrarator" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <div class="flex py-4 mt-2"> | ||||||
|         <ui-btn color="error" type="button" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn> |         <ui-btn color="error" type="button" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn> | ||||||
|         <div class="flex-grow" /> |         <div class="flex-grow" /> | ||||||
|         <ui-btn type="submit">Submit</ui-btn> |         <ui-btn type="submit">Submit</ui-btn> | ||||||
| @ -63,8 +71,10 @@ export default { | |||||||
|     return { |     return { | ||||||
|       details: { |       details: { | ||||||
|         title: null, |         title: null, | ||||||
|  |         subtitle: null, | ||||||
|         description: null, |         description: null, | ||||||
|         author: null, |         author: null, | ||||||
|  |         narrarator: null, | ||||||
|         series: null, |         series: null, | ||||||
|         volumeNumber: null, |         volumeNumber: null, | ||||||
|         publishYear: null, |         publishYear: null, | ||||||
| @ -136,8 +146,10 @@ export default { | |||||||
|     }, |     }, | ||||||
|     init() { |     init() { | ||||||
|       this.details.title = this.book.title |       this.details.title = this.book.title | ||||||
|  |       this.details.subtitle = this.book.subtitle | ||||||
|       this.details.description = this.book.description |       this.details.description = this.book.description | ||||||
|       this.details.author = this.book.author |       this.details.author = this.book.author | ||||||
|  |       this.details.narrarator = this.book.narrarator | ||||||
|       this.details.genres = this.book.genres || [] |       this.details.genres = this.book.genres || [] | ||||||
|       this.details.series = this.book.series |       this.details.series = this.book.series | ||||||
|       this.details.volumeNumber = this.book.volumeNumber |       this.details.volumeNumber = this.book.volumeNumber | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ export default { | |||||||
|       tooltip.style.top = top + 'px' |       tooltip.style.top = top + 'px' | ||||||
|       tooltip.style.left = left + 'px' |       tooltip.style.left = left + 'px' | ||||||
|       tooltip.style.zIndex = 100 |       tooltip.style.zIndex = 100 | ||||||
|       tooltip.innerText = this.text |       tooltip.innerHTML = this.text | ||||||
|       this.tooltip = tooltip |       this.tooltip = tooltip | ||||||
|     }, |     }, | ||||||
|     showTooltip() { |     showTooltip() { | ||||||
|  | |||||||
| @ -56,6 +56,9 @@ export default { | |||||||
|         this.$store.commit('user/setUser', payload.user) |         this.$store.commit('user/setUser', payload.user) | ||||||
|         this.$store.commit('user/setSettings', payload.user.settings) |         this.$store.commit('user/setSettings', payload.user.settings) | ||||||
|       } |       } | ||||||
|  |       if (payload.serverSettings) { | ||||||
|  |         this.$store.commit('setServerSettings', payload.serverSettings) | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|     streamOpen(stream) { |     streamOpen(stream) { | ||||||
|       if (this.$refs.streamContainer) this.$refs.streamContainer.streamOpen(stream) |       if (this.$refs.streamContainer) this.$refs.streamContainer.streamOpen(stream) | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "audiobookshelf-client", |   "name": "audiobookshelf-client", | ||||||
|   "version": "1.0.1", |   "version": "1.0.2", | ||||||
|   "description": "Audiobook manager and player", |   "description": "Audiobook manager and player", | ||||||
|   "main": "index.js", |   "main": "index.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ | |||||||
|           <div class="flex-grow pl-4"> |           <div class="flex-grow pl-4"> | ||||||
|             <ui-text-input-with-label v-model="audiobook.book.title" label="Title" /> |             <ui-text-input-with-label v-model="audiobook.book.title" label="Title" /> | ||||||
| 
 | 
 | ||||||
|  |             <ui-text-input-with-label v-model="audiobook.book.subtitle" label="Subtitle" class="mt-2" /> | ||||||
|  | 
 | ||||||
|             <div class="flex mt-2 -mx-1"> |             <div class="flex mt-2 -mx-1"> | ||||||
|               <div class="w-3/4 px-1"> |               <div class="w-3/4 px-1"> | ||||||
|                 <ui-text-input-with-label v-model="audiobook.book.author" label="Author" /> |                 <ui-text-input-with-label v-model="audiobook.book.author" label="Author" /> | ||||||
| @ -37,6 +39,12 @@ | |||||||
|                 <ui-multi-select v-model="audiobook.tags" label="Tags" :items="tags" /> |                 <ui-multi-select v-model="audiobook.tags" label="Tags" :items="tags" /> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|  | 
 | ||||||
|  |             <div class="flex mt-2 -mx-1"> | ||||||
|  |               <div class="w-1/2 px-1"> | ||||||
|  |                 <ui-text-input-with-label v-model="audiobook.book.narrarator" label="Narrarator" /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </template> |       </template> | ||||||
|  | |||||||
| @ -35,8 +35,16 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="h-0.5 bg-primary bg-opacity-50 w-full" /> |       <div class="h-0.5 bg-primary bg-opacity-50 w-full" /> | ||||||
|       <div class="py-4 mb-8"> |       <div class="py-4 mb-8"> | ||||||
|  |         <p class="text-2xl">Scanner</p> | ||||||
|         <div class="flex items-start py-2"> |         <div class="flex items-start py-2"> | ||||||
|           <p class="text-2xl">Scanner</p> |           <div class="py-2"> | ||||||
|  |             <div class="flex items-center"> | ||||||
|  |               <ui-toggle-switch v-model="newServerSettings.scannerParseSubtitle" @input="updateScannerParseSubtitle" /> | ||||||
|  |               <ui-tooltip :text="parseSubtitleTooltip"> | ||||||
|  |                 <p class="pl-4 text-lg">Parse Subtitles <span class="material-icons icon-text">info_outlined</span></p> | ||||||
|  |               </ui-tooltip> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|           <div class="flex-grow" /> |           <div class="flex-grow" /> | ||||||
|           <div class="w-40 flex flex-col"> |           <div class="w-40 flex flex-col"> | ||||||
|             <ui-btn color="success" class="mb-4" :loading="isScanning" :disabled="isScanningCovers" @click="scan">Scan</ui-btn> |             <ui-btn color="success" class="mb-4" :loading="isScanning" :disabled="isScanningCovers" @click="scan">Scan</ui-btn> | ||||||
| @ -84,10 +92,24 @@ export default { | |||||||
|       isResettingAudiobooks: false, |       isResettingAudiobooks: false, | ||||||
|       users: [], |       users: [], | ||||||
|       showAccountModal: false, |       showAccountModal: false, | ||||||
|       isDeletingUser: false |       isDeletingUser: false, | ||||||
|  |       newServerSettings: {} | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     serverSettings(newVal, oldVal) { | ||||||
|  |       if (newVal && !oldVal) { | ||||||
|  |         this.newServerSettings = { ...this.serverSettings } | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|  |     parseSubtitleTooltip() { | ||||||
|  |       return 'Extract subtitles from audiobook directory names.<br>Subtitle must be seperated by " - "<br>i.e. "Book Title - A Subtitle Here" has the subtitle "A Subtitle Here"' | ||||||
|  |     }, | ||||||
|  |     serverSettings() { | ||||||
|  |       return this.$store.state.serverSettings | ||||||
|  |     }, | ||||||
|     streamAudiobook() { |     streamAudiobook() { | ||||||
|       return this.$store.state.streamAudiobook |       return this.$store.state.streamAudiobook | ||||||
|     }, |     }, | ||||||
| @ -99,6 +121,19 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|  |     updateScannerParseSubtitle(val) { | ||||||
|  |       var payload = { | ||||||
|  |         scannerParseSubtitle: val | ||||||
|  |       } | ||||||
|  |       this.$store | ||||||
|  |         .dispatch('updateServerSettings', payload) | ||||||
|  |         .then((success) => { | ||||||
|  |           console.log('Updated Server Settings', success) | ||||||
|  |         }) | ||||||
|  |         .catch((error) => { | ||||||
|  |           console.error('Failed to update server settings', error) | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|     setDeveloperMode() { |     setDeveloperMode() { | ||||||
|       var value = !this.$store.state.developerMode |       var value = !this.$store.state.developerMode | ||||||
|       this.$store.commit('setDeveloperMode', value) |       this.$store.commit('setDeveloperMode', value) | ||||||
| @ -186,6 +221,8 @@ export default { | |||||||
|       this.$root.socket.on('user_added', this.addUpdateUser) |       this.$root.socket.on('user_added', this.addUpdateUser) | ||||||
|       this.$root.socket.on('user_updated', this.addUpdateUser) |       this.$root.socket.on('user_updated', this.addUpdateUser) | ||||||
|       this.$root.socket.on('user_removed', this.userRemoved) |       this.$root.socket.on('user_removed', this.userRemoved) | ||||||
|  | 
 | ||||||
|  |       this.newServerSettings = this.serverSettings ? { ...this.serverSettings } : {} | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
|  | |||||||
| @ -1,11 +1,10 @@ | |||||||
| export default function ({ $axios, store }) { | export default function ({ $axios, store }) { | ||||||
|   $axios.onRequest(config => { |   $axios.onRequest(config => { | ||||||
|     console.log('Making request to ' + config.url) |     // console.log('Making request to ' + config.url)
 | ||||||
|     if (config.url.startsWith('http:') || config.url.startsWith('https:')) { |     if (config.url.startsWith('http:') || config.url.startsWith('https:')) { | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|     var bearerToken = store.state.user.user ? store.state.user.user.token : null |     var bearerToken = store.state.user.user ? store.state.user.user.token : null | ||||||
|     // console.log('Bearer token', bearerToken)
 |  | ||||||
|     if (bearerToken) { |     if (bearerToken) { | ||||||
|       config.headers.common['Authorization'] = `Bearer ${bearerToken}` |       config.headers.common['Authorization'] = `Bearer ${bearerToken}` | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import Vue from 'vue' | import Vue from 'vue' | ||||||
| 
 | 
 | ||||||
| export const state = () => ({ | export const state = () => ({ | ||||||
|  |   serverSettings: null, | ||||||
|   streamAudiobook: null, |   streamAudiobook: null, | ||||||
|   showEditModal: false, |   showEditModal: false, | ||||||
|   selectedAudiobook: null, |   selectedAudiobook: null, | ||||||
| @ -21,9 +22,29 @@ export const getters = { | |||||||
|   getNumAudiobooksSelected: state => state.selectedAudiobooks.length |   getNumAudiobooksSelected: state => state.selectedAudiobooks.length | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const actions = {} | export const actions = { | ||||||
|  |   updateServerSettings({ commit }, payload) { | ||||||
|  |     var updatePayload = { | ||||||
|  |       ...payload | ||||||
|  |     } | ||||||
|  |     return this.$axios.$patch('/api/serverSettings', updatePayload).then((result) => { | ||||||
|  |       if (result.success) { | ||||||
|  |         commit('setServerSettings', result.settings) | ||||||
|  |         return true | ||||||
|  |       } else { | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  |     }).catch((error) => { | ||||||
|  |       console.error('Failed to update server settings', error) | ||||||
|  |       return false | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| export const mutations = { | export const mutations = { | ||||||
|  |   setServerSettings(state, settings) { | ||||||
|  |     state.serverSettings = settings | ||||||
|  |   }, | ||||||
|   setStreamAudiobook(state, audiobook) { |   setStreamAudiobook(state, audiobook) { | ||||||
|     state.playOnLoad = true |     state.playOnLoad = true | ||||||
|     state.streamAudiobook = audiobook |     state.streamAudiobook = audiobook | ||||||
|  | |||||||
| @ -35,7 +35,6 @@ export const actions = { | |||||||
|     return this.$axios.$patch('/api/user/settings', updatePayload).then((result) => { |     return this.$axios.$patch('/api/user/settings', updatePayload).then((result) => { | ||||||
|       if (result.success) { |       if (result.success) { | ||||||
|         commit('setSettings', result.settings) |         commit('setSettings', result.settings) | ||||||
|         console.log('Settings updated', result.settings) |  | ||||||
|         return true |         return true | ||||||
|       } else { |       } else { | ||||||
|         return false |         return false | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "audiobookshelf", |   "name": "audiobookshelf", | ||||||
|   "version": "1.0.1", |   "version": "1.0.2", | ||||||
|   "description": "Self-hosted audiobook server for managing and playing audiobooks.", |   "description": "Self-hosted audiobook server for managing and playing audiobooks.", | ||||||
|   "main": "index.js", |   "main": "index.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
| @ -17,7 +17,12 @@ Android app is in beta, try it out on the [Google Play Store](https://play.googl | |||||||
| /Author/Series/Title/... | /Author/Series/Title/... | ||||||
| 
 | 
 | ||||||
| Title can start with the publish year like so: | Title can start with the publish year like so: | ||||||
| /1989 - Awesome Book/... | /1989 - Book Title/... | ||||||
|  | 
 | ||||||
|  | (Optional Setting) Subtitle can be seperated to its own field: | ||||||
|  | /Book Title - With a Subtitle/... | ||||||
|  | /1989 - Book Title - With a Subtitle/... | ||||||
|  | will store "With a Subtitle" as the subtitle | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,6 +41,8 @@ class ApiController { | |||||||
|     this.router.patch('/user/password', this.userChangePassword.bind(this)) |     this.router.patch('/user/password', this.userChangePassword.bind(this)) | ||||||
|     this.router.patch('/user/settings', this.userUpdateSettings.bind(this)) |     this.router.patch('/user/settings', this.userUpdateSettings.bind(this)) | ||||||
| 
 | 
 | ||||||
|  |     this.router.patch('/serverSettings', this.updateServerSettings.bind(this)) | ||||||
|  | 
 | ||||||
|     this.router.post('/authorize', this.authorize.bind(this)) |     this.router.post('/authorize', this.authorize.bind(this)) | ||||||
| 
 | 
 | ||||||
|     this.router.get('/genres', this.getGenres.bind(this)) |     this.router.get('/genres', this.getGenres.bind(this)) | ||||||
| @ -308,6 +310,21 @@ class ApiController { | |||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   async updateServerSettings(req, res) { | ||||||
|  |     var settingsUpdate = req.body | ||||||
|  |     if (!settingsUpdate || !isObject(settingsUpdate)) { | ||||||
|  |       return res.sendStatus(500) | ||||||
|  |     } | ||||||
|  |     var madeUpdates = this.db.serverSettings.update(settingsUpdate) | ||||||
|  |     if (madeUpdates) { | ||||||
|  |       await this.db.updateEntity('settings', this.db.serverSettings) | ||||||
|  |     } | ||||||
|  |     return res.json({ | ||||||
|  |       success: true, | ||||||
|  |       serverSettings: this.db.serverSettings | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async download(req, res) { |   async download(req, res) { | ||||||
|     var downloadId = req.params.id |     var downloadId = req.params.id | ||||||
|     Logger.info('Download Request', downloadId) |     Logger.info('Download Request', downloadId) | ||||||
|  | |||||||
							
								
								
									
										34
									
								
								server/Db.js
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								server/Db.js
									
									
									
									
									
								
							| @ -4,6 +4,7 @@ const jwt = require('jsonwebtoken') | |||||||
| const Logger = require('./Logger') | const Logger = require('./Logger') | ||||||
| const Audiobook = require('./objects/Audiobook') | const Audiobook = require('./objects/Audiobook') | ||||||
| const User = require('./objects/User') | const User = require('./objects/User') | ||||||
|  | const ServerSettings = require('./objects/ServerSettings') | ||||||
| 
 | 
 | ||||||
| class Db { | class Db { | ||||||
|   constructor(CONFIG_PATH) { |   constructor(CONFIG_PATH) { | ||||||
| @ -19,6 +20,8 @@ class Db { | |||||||
|     this.users = [] |     this.users = [] | ||||||
|     this.audiobooks = [] |     this.audiobooks = [] | ||||||
|     this.settings = [] |     this.settings = [] | ||||||
|  | 
 | ||||||
|  |     this.serverSettings = null | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getEntityDb(entityName) { |   getEntityDb(entityName) { | ||||||
| @ -39,15 +42,6 @@ class Db { | |||||||
|     return 'settings' |     return 'settings' | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getDefaultSettings() { |  | ||||||
|     return { |  | ||||||
|       config: { |  | ||||||
|         version: 1, |  | ||||||
|         cardSize: 'md' |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   getDefaultUser(token) { |   getDefaultUser(token) { | ||||||
|     return new User({ |     return new User({ | ||||||
|       id: 'root', |       id: 'root', | ||||||
| @ -71,6 +65,11 @@ class Db { | |||||||
|       Logger.debug('Generated default token', token) |       Logger.debug('Generated default token', token) | ||||||
|       await this.insertUser(this.getDefaultUser(token)) |       await this.insertUser(this.getDefaultUser(token)) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if (!this.serverSettings) { | ||||||
|  |       this.serverSettings = new ServerSettings() | ||||||
|  |       await this.insertSettings(this.serverSettings) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async load() { |   async load() { | ||||||
| @ -83,11 +82,26 @@ class Db { | |||||||
|       Logger.info(`Users Loaded ${this.users.length}`) |       Logger.info(`Users Loaded ${this.users.length}`) | ||||||
|     }) |     }) | ||||||
|     var p3 = this.settingsDb.select(() => true).then((results) => { |     var p3 = this.settingsDb.select(() => true).then((results) => { | ||||||
|       this.settings = results |       if (results.data && results.data.length) { | ||||||
|  |         this.settings = results.data | ||||||
|  |         var serverSettings = this.settings.find(s => s.id === 'server-settings') | ||||||
|  |         if (serverSettings) { | ||||||
|  |           this.serverSettings = new ServerSettings(serverSettings) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|     }) |     }) | ||||||
|     await Promise.all([p1, p2, p3]) |     await Promise.all([p1, p2, p3]) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   insertSettings(settings) { | ||||||
|  |     return this.settingsDb.insert(settings).then((results) => { | ||||||
|  |       Logger.debug(`[DB] Inserted ${results.inserted} settings`) | ||||||
|  |       this.settings = this.settings.concat(settings) | ||||||
|  |     }).catch((error) => { | ||||||
|  |       Logger.error(`[DB] Insert settings Failed ${error}`) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   insertAudiobook(audiobook) { |   insertAudiobook(audiobook) { | ||||||
|     return this.insertAudiobooks([audiobook]) |     return this.insertAudiobooks([audiobook]) | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -80,7 +80,7 @@ class Scanner { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const scanStart = Date.now() |     const scanStart = Date.now() | ||||||
|     var audiobookDataFound = await getAllAudiobookFiles(this.AudiobookPath) |     var audiobookDataFound = await getAllAudiobookFiles(this.AudiobookPath, this.db.serverSettings) | ||||||
| 
 | 
 | ||||||
|     // Set ino for each ab data as a string
 |     // Set ino for each ab data as a string
 | ||||||
|     audiobookDataFound = await this.setAudiobookDataInos(audiobookDataFound) |     audiobookDataFound = await this.setAudiobookDataInos(audiobookDataFound) | ||||||
|  | |||||||
| @ -50,8 +50,8 @@ class Server { | |||||||
|   get audiobooks() { |   get audiobooks() { | ||||||
|     return this.db.audiobooks |     return this.db.audiobooks | ||||||
|   } |   } | ||||||
|   get settings() { |   get serverSettings() { | ||||||
|     return this.db.settings |     return this.db.serverSettings | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   emitter(ev, data) { |   emitter(ev, data) { | ||||||
| @ -239,7 +239,7 @@ class Server { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const initialPayload = { |     const initialPayload = { | ||||||
|       settings: this.settings, |       serverSettings: this.serverSettings.toJSON(), | ||||||
|       isScanning: this.isScanning, |       isScanning: this.isScanning, | ||||||
|       isInitialized: this.isInitialized, |       isInitialized: this.isInitialized, | ||||||
|       audiobookPath: this.AudiobookPath, |       audiobookPath: this.AudiobookPath, | ||||||
|  | |||||||
| @ -6,9 +6,11 @@ class Book { | |||||||
|   constructor(book = null) { |   constructor(book = null) { | ||||||
|     this.olid = null |     this.olid = null | ||||||
|     this.title = null |     this.title = null | ||||||
|  |     this.subtitle = null | ||||||
|     this.author = null |     this.author = null | ||||||
|     this.authorFL = null |     this.authorFL = null | ||||||
|     this.authorLF = null |     this.authorLF = null | ||||||
|  |     this.narrarator = null | ||||||
|     this.series = null |     this.series = null | ||||||
|     this.volumeNumber = null |     this.volumeNumber = null | ||||||
|     this.publishYear = null |     this.publishYear = null | ||||||
| @ -23,15 +25,19 @@ class Book { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get _title() { return this.title || '' } |   get _title() { return this.title || '' } | ||||||
|  |   get _subtitle() { return this.subtitle || '' } | ||||||
|  |   get _narrarator() { return this.narrarator || '' } | ||||||
|   get _author() { return this.author || '' } |   get _author() { return this.author || '' } | ||||||
|   get _series() { return this.series || '' } |   get _series() { return this.series || '' } | ||||||
| 
 | 
 | ||||||
|   construct(book) { |   construct(book) { | ||||||
|     this.olid = book.olid |     this.olid = book.olid | ||||||
|     this.title = book.title |     this.title = book.title | ||||||
|  |     this.subtitle = book.subtitle || null | ||||||
|     this.author = book.author |     this.author = book.author | ||||||
|     this.authorFL = book.authorFL || null |     this.authorFL = book.authorFL || null | ||||||
|     this.authorLF = book.authorLF || null |     this.authorLF = book.authorLF || null | ||||||
|  |     this.narrarator = book.narrarator || null | ||||||
|     this.series = book.series |     this.series = book.series | ||||||
|     this.volumeNumber = book.volumeNumber || null |     this.volumeNumber = book.volumeNumber || null | ||||||
|     this.publishYear = book.publishYear |     this.publishYear = book.publishYear | ||||||
| @ -45,9 +51,11 @@ class Book { | |||||||
|     return { |     return { | ||||||
|       olid: this.olid, |       olid: this.olid, | ||||||
|       title: this.title, |       title: this.title, | ||||||
|  |       subtitle: this.subtitle, | ||||||
|       author: this.author, |       author: this.author, | ||||||
|       authorFL: this.authorFL, |       authorFL: this.authorFL, | ||||||
|       authorLF: this.authorLF, |       authorLF: this.authorLF, | ||||||
|  |       narrarator: this.narrarator, | ||||||
|       series: this.series, |       series: this.series, | ||||||
|       volumeNumber: this.volumeNumber, |       volumeNumber: this.volumeNumber, | ||||||
|       publishYear: this.publishYear, |       publishYear: this.publishYear, | ||||||
| @ -80,7 +88,9 @@ class Book { | |||||||
|   setData(data) { |   setData(data) { | ||||||
|     this.olid = data.olid || null |     this.olid = data.olid || null | ||||||
|     this.title = data.title || null |     this.title = data.title || null | ||||||
|  |     this.subtitle = data.subtitle || null | ||||||
|     this.author = data.author || null |     this.author = data.author || null | ||||||
|  |     this.narrarator = data.narrarator || null | ||||||
|     this.series = data.series || null |     this.series = data.series || null | ||||||
|     this.volumeNumber = data.volumeNumber || null |     this.volumeNumber = data.volumeNumber || null | ||||||
|     this.publishYear = data.publishYear || null |     this.publishYear = data.publishYear || null | ||||||
| @ -151,7 +161,7 @@ class Book { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   isSearchMatch(search) { |   isSearchMatch(search) { | ||||||
|     return this._title.toLowerCase().includes(search) || this._author.toLowerCase().includes(search) || this._series.toLowerCase().includes(search) |     return this._title.toLowerCase().includes(search) || this._subtitle.toLowerCase().includes(search) || this._author.toLowerCase().includes(search) || this._series.toLowerCase().includes(search) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| module.exports = Book | module.exports = Book | ||||||
							
								
								
									
										39
									
								
								server/objects/ServerSettings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								server/objects/ServerSettings.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | class ServerSettings { | ||||||
|  |   constructor(settings) { | ||||||
|  |     this.id = 'server-settings' | ||||||
|  |     this.autoTagNew = false | ||||||
|  |     this.newTagExpireDays = 15 | ||||||
|  |     this.scannerParseSubtitle = false | ||||||
|  | 
 | ||||||
|  |     if (settings) { | ||||||
|  |       this.construct(settings) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   construct(settings) { | ||||||
|  |     this.autoTagNew = settings.autoTagNew | ||||||
|  |     this.newTagExpireDays = settings.newTagExpireDays | ||||||
|  |     this.scannerParseSubtitle = settings.scannerParseSubtitle | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   toJSON() { | ||||||
|  |     return { | ||||||
|  |       id: this.id, | ||||||
|  |       autoTagNew: this.autoTagNew, | ||||||
|  |       newTagExpireDays: this.newTagExpireDays, | ||||||
|  |       scannerParseSubtitle: this.scannerParseSubtitle | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   update(payload) { | ||||||
|  |     var hasUpdates = false | ||||||
|  |     for (const key in payload) { | ||||||
|  |       if (this[key] !== payload[key]) { | ||||||
|  |         this[key] = payload[key] | ||||||
|  |         hasUpdates = true | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return hasUpdates | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | module.exports = ServerSettings | ||||||
| @ -30,7 +30,9 @@ function getFileType(ext) { | |||||||
|   return 'unknown' |   return 'unknown' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function getAllAudiobookFiles(abRootPath) { | async function getAllAudiobookFiles(abRootPath, serverSettings = {}) { | ||||||
|  |   var parseSubtitle = !!serverSettings.scannerParseSubtitle | ||||||
|  | 
 | ||||||
|   var paths = await getPaths(abRootPath) |   var paths = await getPaths(abRootPath) | ||||||
|   var audiobooks = {} |   var audiobooks = {} | ||||||
| 
 | 
 | ||||||
| @ -53,6 +55,7 @@ async function getAllAudiobookFiles(abRootPath) { | |||||||
|     var title = splitDir.shift() |     var title = splitDir.shift() | ||||||
| 
 | 
 | ||||||
|     var publishYear = null |     var publishYear = null | ||||||
|  |     var subtitle = null | ||||||
| 
 | 
 | ||||||
|     // If Title is of format 1999 - Title, then use 1999 as publish year
 |     // If Title is of format 1999 - Title, then use 1999 as publish year
 | ||||||
|     var publishYearMatch = title.match(/^([0-9]{4}) - (.+)/) |     var publishYearMatch = title.match(/^([0-9]{4}) - (.+)/) | ||||||
| @ -63,10 +66,17 @@ async function getAllAudiobookFiles(abRootPath) { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (parseSubtitle && title.includes(' - ')) { | ||||||
|  |       var splitOnSubtitle = title.split(' - ') | ||||||
|  |       title = splitOnSubtitle.shift() | ||||||
|  |       subtitle = splitOnSubtitle.join(' - ') | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (!audiobooks[path]) { |     if (!audiobooks[path]) { | ||||||
|       audiobooks[path] = { |       audiobooks[path] = { | ||||||
|         author: author, |         author, | ||||||
|         title: title, |         title, | ||||||
|  |         subtitle, | ||||||
|         series: cleanString(series), |         series: cleanString(series), | ||||||
|         publishYear: publishYear, |         publishYear: publishYear, | ||||||
|         path: path, |         path: path, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user