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
							
								
									af0365c81f
								
							
						
					
					
						commit
						a66a84bd2d
					
				@ -1,5 +1,5 @@
 | 
			
		||||
<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>
 | 
			
		||||
      <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>
 | 
			
		||||
@ -79,6 +79,10 @@ export default {
 | 
			
		||||
        this.$store.commit('setShowEditModal', val)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    height() {
 | 
			
		||||
      var maxHeightAllowed = window.innerHeight - 150
 | 
			
		||||
      return Math.min(maxHeightAllowed, 650)
 | 
			
		||||
    },
 | 
			
		||||
    tabName() {
 | 
			
		||||
      var _tab = this.tabs.find((t) => t.id === this.selectedTab)
 | 
			
		||||
      return _tab ? _tab.component : ''
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
      <span class="material-icons text-4xl">close</span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <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 />
 | 
			
		||||
      <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 />
 | 
			
		||||
@ -31,6 +31,10 @@ export default {
 | 
			
		||||
    height: {
 | 
			
		||||
      type: [String, Number],
 | 
			
		||||
      default: 'unset'
 | 
			
		||||
    },
 | 
			
		||||
    contentMarginTop: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      default: 50
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,8 @@
 | 
			
		||||
    <form @submit.prevent="submitForm">
 | 
			
		||||
      <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="w-3/4 px-1">
 | 
			
		||||
          <ui-text-input-with-label v-model="details.author" label="Author" />
 | 
			
		||||
@ -41,7 +43,13 @@
 | 
			
		||||
        </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>
 | 
			
		||||
        <div class="flex-grow" />
 | 
			
		||||
        <ui-btn type="submit">Submit</ui-btn>
 | 
			
		||||
@ -63,8 +71,10 @@ export default {
 | 
			
		||||
    return {
 | 
			
		||||
      details: {
 | 
			
		||||
        title: null,
 | 
			
		||||
        subtitle: null,
 | 
			
		||||
        description: null,
 | 
			
		||||
        author: null,
 | 
			
		||||
        narrarator: null,
 | 
			
		||||
        series: null,
 | 
			
		||||
        volumeNumber: null,
 | 
			
		||||
        publishYear: null,
 | 
			
		||||
@ -136,8 +146,10 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    init() {
 | 
			
		||||
      this.details.title = this.book.title
 | 
			
		||||
      this.details.subtitle = this.book.subtitle
 | 
			
		||||
      this.details.description = this.book.description
 | 
			
		||||
      this.details.author = this.book.author
 | 
			
		||||
      this.details.narrarator = this.book.narrarator
 | 
			
		||||
      this.details.genres = this.book.genres || []
 | 
			
		||||
      this.details.series = this.book.series
 | 
			
		||||
      this.details.volumeNumber = this.book.volumeNumber
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ export default {
 | 
			
		||||
      tooltip.style.top = top + 'px'
 | 
			
		||||
      tooltip.style.left = left + 'px'
 | 
			
		||||
      tooltip.style.zIndex = 100
 | 
			
		||||
      tooltip.innerText = this.text
 | 
			
		||||
      tooltip.innerHTML = this.text
 | 
			
		||||
      this.tooltip = tooltip
 | 
			
		||||
    },
 | 
			
		||||
    showTooltip() {
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,9 @@ export default {
 | 
			
		||||
        this.$store.commit('user/setUser', payload.user)
 | 
			
		||||
        this.$store.commit('user/setSettings', payload.user.settings)
 | 
			
		||||
      }
 | 
			
		||||
      if (payload.serverSettings) {
 | 
			
		||||
        this.$store.commit('setServerSettings', payload.serverSettings)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    streamOpen(stream) {
 | 
			
		||||
      if (this.$refs.streamContainer) this.$refs.streamContainer.streamOpen(stream)
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "audiobookshelf-client",
 | 
			
		||||
  "version": "1.0.1",
 | 
			
		||||
  "version": "1.0.2",
 | 
			
		||||
  "description": "Audiobook manager and player",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,8 @@
 | 
			
		||||
          <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.subtitle" label="Subtitle" class="mt-2" />
 | 
			
		||||
 | 
			
		||||
            <div class="flex mt-2 -mx-1">
 | 
			
		||||
              <div class="w-3/4 px-1">
 | 
			
		||||
                <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" />
 | 
			
		||||
              </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>
 | 
			
		||||
      </template>
 | 
			
		||||
 | 
			
		||||
@ -35,8 +35,16 @@
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="h-0.5 bg-primary bg-opacity-50 w-full" />
 | 
			
		||||
      <div class="py-4 mb-8">
 | 
			
		||||
        <div class="flex items-start py-2">
 | 
			
		||||
        <p class="text-2xl">Scanner</p>
 | 
			
		||||
        <div class="flex items-start py-2">
 | 
			
		||||
          <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="w-40 flex flex-col">
 | 
			
		||||
            <ui-btn color="success" class="mb-4" :loading="isScanning" :disabled="isScanningCovers" @click="scan">Scan</ui-btn>
 | 
			
		||||
@ -84,10 +92,24 @@ export default {
 | 
			
		||||
      isResettingAudiobooks: false,
 | 
			
		||||
      users: [],
 | 
			
		||||
      showAccountModal: false,
 | 
			
		||||
      isDeletingUser: false
 | 
			
		||||
      isDeletingUser: false,
 | 
			
		||||
      newServerSettings: {}
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    serverSettings(newVal, oldVal) {
 | 
			
		||||
      if (newVal && !oldVal) {
 | 
			
		||||
        this.newServerSettings = { ...this.serverSettings }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  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() {
 | 
			
		||||
      return this.$store.state.streamAudiobook
 | 
			
		||||
    },
 | 
			
		||||
@ -99,6 +121,19 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  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() {
 | 
			
		||||
      var value = !this.$store.state.developerMode
 | 
			
		||||
      this.$store.commit('setDeveloperMode', value)
 | 
			
		||||
@ -186,6 +221,8 @@ export default {
 | 
			
		||||
      this.$root.socket.on('user_added', this.addUpdateUser)
 | 
			
		||||
      this.$root.socket.on('user_updated', this.addUpdateUser)
 | 
			
		||||
      this.$root.socket.on('user_removed', this.userRemoved)
 | 
			
		||||
 | 
			
		||||
      this.newServerSettings = this.serverSettings ? { ...this.serverSettings } : {}
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,10 @@
 | 
			
		||||
export default function ({ $axios, store }) {
 | 
			
		||||
  $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:')) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    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}`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import Vue from 'vue'
 | 
			
		||||
 | 
			
		||||
export const state = () => ({
 | 
			
		||||
  serverSettings: null,
 | 
			
		||||
  streamAudiobook: null,
 | 
			
		||||
  showEditModal: false,
 | 
			
		||||
  selectedAudiobook: null,
 | 
			
		||||
@ -21,9 +22,29 @@ export const getters = {
 | 
			
		||||
  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 = {
 | 
			
		||||
  setServerSettings(state, settings) {
 | 
			
		||||
    state.serverSettings = settings
 | 
			
		||||
  },
 | 
			
		||||
  setStreamAudiobook(state, audiobook) {
 | 
			
		||||
    state.playOnLoad = true
 | 
			
		||||
    state.streamAudiobook = audiobook
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,6 @@ export const actions = {
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "audiobookshelf",
 | 
			
		||||
  "version": "1.0.1",
 | 
			
		||||
  "version": "1.0.2",
 | 
			
		||||
  "description": "Self-hosted audiobook server for managing and playing audiobooks.",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,12 @@ Android app is in beta, try it out on the [Google Play Store](https://play.googl
 | 
			
		||||
/Author/Series/Title/...
 | 
			
		||||
 | 
			
		||||
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/settings', this.userUpdateSettings.bind(this))
 | 
			
		||||
 | 
			
		||||
    this.router.patch('/serverSettings', this.updateServerSettings.bind(this))
 | 
			
		||||
 | 
			
		||||
    this.router.post('/authorize', this.authorize.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) {
 | 
			
		||||
    var downloadId = req.params.id
 | 
			
		||||
    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 Audiobook = require('./objects/Audiobook')
 | 
			
		||||
const User = require('./objects/User')
 | 
			
		||||
const ServerSettings = require('./objects/ServerSettings')
 | 
			
		||||
 | 
			
		||||
class Db {
 | 
			
		||||
  constructor(CONFIG_PATH) {
 | 
			
		||||
@ -19,6 +20,8 @@ class Db {
 | 
			
		||||
    this.users = []
 | 
			
		||||
    this.audiobooks = []
 | 
			
		||||
    this.settings = []
 | 
			
		||||
 | 
			
		||||
    this.serverSettings = null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getEntityDb(entityName) {
 | 
			
		||||
@ -39,15 +42,6 @@ class Db {
 | 
			
		||||
    return 'settings'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getDefaultSettings() {
 | 
			
		||||
    return {
 | 
			
		||||
      config: {
 | 
			
		||||
        version: 1,
 | 
			
		||||
        cardSize: 'md'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getDefaultUser(token) {
 | 
			
		||||
    return new User({
 | 
			
		||||
      id: 'root',
 | 
			
		||||
@ -71,6 +65,11 @@ class Db {
 | 
			
		||||
      Logger.debug('Generated default token', token)
 | 
			
		||||
      await this.insertUser(this.getDefaultUser(token))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this.serverSettings) {
 | 
			
		||||
      this.serverSettings = new ServerSettings()
 | 
			
		||||
      await this.insertSettings(this.serverSettings)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async load() {
 | 
			
		||||
@ -83,11 +82,26 @@ class Db {
 | 
			
		||||
      Logger.info(`Users Loaded ${this.users.length}`)
 | 
			
		||||
    })
 | 
			
		||||
    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])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
    return this.insertAudiobooks([audiobook])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,7 @@ class Scanner {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    audiobookDataFound = await this.setAudiobookDataInos(audiobookDataFound)
 | 
			
		||||
 | 
			
		||||
@ -50,8 +50,8 @@ class Server {
 | 
			
		||||
  get audiobooks() {
 | 
			
		||||
    return this.db.audiobooks
 | 
			
		||||
  }
 | 
			
		||||
  get settings() {
 | 
			
		||||
    return this.db.settings
 | 
			
		||||
  get serverSettings() {
 | 
			
		||||
    return this.db.serverSettings
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  emitter(ev, data) {
 | 
			
		||||
@ -239,7 +239,7 @@ class Server {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const initialPayload = {
 | 
			
		||||
      settings: this.settings,
 | 
			
		||||
      serverSettings: this.serverSettings.toJSON(),
 | 
			
		||||
      isScanning: this.isScanning,
 | 
			
		||||
      isInitialized: this.isInitialized,
 | 
			
		||||
      audiobookPath: this.AudiobookPath,
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,11 @@ class Book {
 | 
			
		||||
  constructor(book = null) {
 | 
			
		||||
    this.olid = null
 | 
			
		||||
    this.title = null
 | 
			
		||||
    this.subtitle = null
 | 
			
		||||
    this.author = null
 | 
			
		||||
    this.authorFL = null
 | 
			
		||||
    this.authorLF = null
 | 
			
		||||
    this.narrarator = null
 | 
			
		||||
    this.series = null
 | 
			
		||||
    this.volumeNumber = null
 | 
			
		||||
    this.publishYear = null
 | 
			
		||||
@ -23,15 +25,19 @@ class Book {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get _title() { return this.title || '' }
 | 
			
		||||
  get _subtitle() { return this.subtitle || '' }
 | 
			
		||||
  get _narrarator() { return this.narrarator || '' }
 | 
			
		||||
  get _author() { return this.author || '' }
 | 
			
		||||
  get _series() { return this.series || '' }
 | 
			
		||||
 | 
			
		||||
  construct(book) {
 | 
			
		||||
    this.olid = book.olid
 | 
			
		||||
    this.title = book.title
 | 
			
		||||
    this.subtitle = book.subtitle || null
 | 
			
		||||
    this.author = book.author
 | 
			
		||||
    this.authorFL = book.authorFL || null
 | 
			
		||||
    this.authorLF = book.authorLF || null
 | 
			
		||||
    this.narrarator = book.narrarator || null
 | 
			
		||||
    this.series = book.series
 | 
			
		||||
    this.volumeNumber = book.volumeNumber || null
 | 
			
		||||
    this.publishYear = book.publishYear
 | 
			
		||||
@ -45,9 +51,11 @@ class Book {
 | 
			
		||||
    return {
 | 
			
		||||
      olid: this.olid,
 | 
			
		||||
      title: this.title,
 | 
			
		||||
      subtitle: this.subtitle,
 | 
			
		||||
      author: this.author,
 | 
			
		||||
      authorFL: this.authorFL,
 | 
			
		||||
      authorLF: this.authorLF,
 | 
			
		||||
      narrarator: this.narrarator,
 | 
			
		||||
      series: this.series,
 | 
			
		||||
      volumeNumber: this.volumeNumber,
 | 
			
		||||
      publishYear: this.publishYear,
 | 
			
		||||
@ -80,7 +88,9 @@ class Book {
 | 
			
		||||
  setData(data) {
 | 
			
		||||
    this.olid = data.olid || null
 | 
			
		||||
    this.title = data.title || null
 | 
			
		||||
    this.subtitle = data.subtitle || null
 | 
			
		||||
    this.author = data.author || null
 | 
			
		||||
    this.narrarator = data.narrarator || null
 | 
			
		||||
    this.series = data.series || null
 | 
			
		||||
    this.volumeNumber = data.volumeNumber || null
 | 
			
		||||
    this.publishYear = data.publishYear || null
 | 
			
		||||
@ -151,7 +161,7 @@ class Book {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										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'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getAllAudiobookFiles(abRootPath) {
 | 
			
		||||
async function getAllAudiobookFiles(abRootPath, serverSettings = {}) {
 | 
			
		||||
  var parseSubtitle = !!serverSettings.scannerParseSubtitle
 | 
			
		||||
 | 
			
		||||
  var paths = await getPaths(abRootPath)
 | 
			
		||||
  var audiobooks = {}
 | 
			
		||||
 | 
			
		||||
@ -53,6 +55,7 @@ async function getAllAudiobookFiles(abRootPath) {
 | 
			
		||||
    var title = splitDir.shift()
 | 
			
		||||
 | 
			
		||||
    var publishYear = null
 | 
			
		||||
    var subtitle = null
 | 
			
		||||
 | 
			
		||||
    // If Title is of format 1999 - Title, then use 1999 as publish year
 | 
			
		||||
    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]) {
 | 
			
		||||
      audiobooks[path] = {
 | 
			
		||||
        author: author,
 | 
			
		||||
        title: title,
 | 
			
		||||
        author,
 | 
			
		||||
        title,
 | 
			
		||||
        subtitle,
 | 
			
		||||
        series: cleanString(series),
 | 
			
		||||
        publishYear: publishYear,
 | 
			
		||||
        path: path,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user