mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add:Generate book metadata file when book details are changed,Add:Server setting for storing book metadata in book folder
This commit is contained in:
		
							parent
							
								
									aa50cc2d81
								
							
						
					
					
						commit
						295c6b0c74
					
				@ -11,7 +11,17 @@
 | 
			
		||||
        <ui-toggle-switch v-model="newServerSettings.storeCoverWithBook" :disabled="updatingServerSettings" @input="(val) => updateSettingsKey('storeCoverWithBook', val)" />
 | 
			
		||||
        <ui-tooltip :text="tooltips.storeCoverWithBook">
 | 
			
		||||
          <p class="pl-4 text-lg">
 | 
			
		||||
            Store covers with audiobook
 | 
			
		||||
            Store covers with book
 | 
			
		||||
            <span class="material-icons icon-text">info_outlined</span>
 | 
			
		||||
          </p>
 | 
			
		||||
        </ui-tooltip>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="flex items-center py-2">
 | 
			
		||||
        <ui-toggle-switch v-model="newServerSettings.storeMetadataWithBook" :disabled="updatingServerSettings" @input="(val) => updateSettingsKey('storeMetadataWithBook', val)" />
 | 
			
		||||
        <ui-tooltip :text="tooltips.storeMetadataWithBook">
 | 
			
		||||
          <p class="pl-4 text-lg">
 | 
			
		||||
            Store metadata with book
 | 
			
		||||
            <span class="material-icons icon-text">info_outlined</span>
 | 
			
		||||
          </p>
 | 
			
		||||
        </ui-tooltip>
 | 
			
		||||
@ -181,10 +191,11 @@ export default {
 | 
			
		||||
        scannerDisableWatcher: 'Disables the automatic adding/updating of audiobooks when file changes are detected. *Requires server restart',
 | 
			
		||||
        scannerPreferOpfMetadata: 'OPF file metadata will be used for book details over folder names',
 | 
			
		||||
        scannerPreferAudioMetadata: 'Audio file ID3 meta tags will be used for book details over folder names',
 | 
			
		||||
        scannerParseSubtitle: '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"',
 | 
			
		||||
        scannerParseSubtitle: 'Extract subtitles from audiobook folder names.<br>Subtitle must be seperated by " - "<br>i.e. "Book Title - A Subtitle Here" has the subtitle "A Subtitle Here"',
 | 
			
		||||
        scannerFindCovers: 'If your audiobook does not have an embedded cover or a cover image inside the folder, the scanner will attempt to find a cover.<br>Note: This will extend scan time',
 | 
			
		||||
        bookshelfView: 'Alternative bookshelf view that shows title & author under book covers',
 | 
			
		||||
        storeCoverWithBook: 'By default covers are stored in /metadata/books, enabling this setting will store covers inside your audiobooks directory. Only one file named "cover" will be kept.',
 | 
			
		||||
        storeCoverWithBook: 'By default covers are stored in /metadata/books, enabling this setting will store covers in the books folder. Only one file named "cover" will be kept',
 | 
			
		||||
        storeMetadataWithBook: 'By default metadata files are stored in /metadata/books, enabling this setting will store metadata files in the books folder. Uses .abs file extension',
 | 
			
		||||
        coverAspectRatio: 'Prefer to use square covers over standard 1.6:1 book covers'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -124,8 +124,8 @@ class Db {
 | 
			
		||||
    if (!this.serverSettings) {
 | 
			
		||||
      this.serverSettings = new ServerSettings()
 | 
			
		||||
      await this.insertEntity('settings', this.serverSettings)
 | 
			
		||||
      global.ServerSettings = this.serverSettings.toJSON()
 | 
			
		||||
    }
 | 
			
		||||
    global.ServerSettings = this.serverSettings.toJSON()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async load() {
 | 
			
		||||
@ -311,5 +311,12 @@ class Db {
 | 
			
		||||
      return []
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check if server was updated and previous version was earlier than param
 | 
			
		||||
  checkPreviousVersionIsBefore(version) {
 | 
			
		||||
    if (!this.previousVersion) return false
 | 
			
		||||
    // true if version > previousVersion
 | 
			
		||||
    return version.localeCompare(this.previousVersion) >= 0
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
module.exports = Db
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,8 @@ class Server {
 | 
			
		||||
    this.Uid = isNaN(UID) ? 0 : Number(UID)
 | 
			
		||||
    this.Gid = isNaN(GID) ? 0 : Number(GID)
 | 
			
		||||
    this.Host = '0.0.0.0'
 | 
			
		||||
    global.Uid = this.Uid
 | 
			
		||||
    global.Gid = this.Gid
 | 
			
		||||
    global.ConfigPath = Path.normalize(CONFIG_PATH)
 | 
			
		||||
    global.AudiobookPath = Path.normalize(AUDIOBOOK_PATH)
 | 
			
		||||
    global.MetadataPath = Path.normalize(METADATA_PATH)
 | 
			
		||||
@ -132,6 +134,10 @@ class Server {
 | 
			
		||||
      Logger.info(`[Server] Running scan for duplicate book IDs`)
 | 
			
		||||
      await this.scanner.fixDuplicateIds()
 | 
			
		||||
    }
 | 
			
		||||
    // If server upgrade and last version was 1.7.0 or earlier - add abmetadata files
 | 
			
		||||
    // if (this.db.checkPreviousVersionIsBefore('1.7.1')) {
 | 
			
		||||
    // TODO: wait until stable
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    if (this.db.serverSettings.scannerDisableWatcher) {
 | 
			
		||||
      Logger.info(`[Server] Watcher is disabled`)
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ const { comparePaths, getIno, getId, elapsedPretty } = require('../utils/index')
 | 
			
		||||
const { parseOpfMetadataXML } = require('../utils/parseOpfMetadata')
 | 
			
		||||
const { extractCoverArt } = require('../utils/ffmpegHelpers')
 | 
			
		||||
const nfoGenerator = require('../utils/nfoGenerator')
 | 
			
		||||
const abmetadataGenerator = require('../utils/abmetadataGenerator')
 | 
			
		||||
const Logger = require('../Logger')
 | 
			
		||||
const Book = require('./Book')
 | 
			
		||||
const AudioTrack = require('./AudioTrack')
 | 
			
		||||
@ -44,6 +45,9 @@ class Audiobook {
 | 
			
		||||
    if (audiobook) {
 | 
			
		||||
      this.construct(audiobook)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Temp flags
 | 
			
		||||
    this.isSavingMetadata = false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  construct(audiobook) {
 | 
			
		||||
@ -424,6 +428,10 @@ class Audiobook {
 | 
			
		||||
 | 
			
		||||
    if (payload.book && this.book.update(payload.book)) {
 | 
			
		||||
      hasUpdates = true
 | 
			
		||||
 | 
			
		||||
      // TODO: Book may have updates where this save is not necessary
 | 
			
		||||
      //          add check first if metadata update is needed
 | 
			
		||||
      this.saveAbMetadata()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (hasUpdates) {
 | 
			
		||||
@ -1025,5 +1033,25 @@ class Audiobook {
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async saveAbMetadata() {
 | 
			
		||||
    if (this.isSavingMetadata) return
 | 
			
		||||
    this.isSavingMetadata = true
 | 
			
		||||
 | 
			
		||||
    var metadataPath = Path.join(global.MetadataPath, 'books', this.id)
 | 
			
		||||
    if (global.ServerSettings.storeMetadataWithBook) {
 | 
			
		||||
      metadataPath = this.fullPath
 | 
			
		||||
    } else {
 | 
			
		||||
      // Make sure metadata book dir exists
 | 
			
		||||
      await fs.ensureDir(metadataPath)
 | 
			
		||||
    }
 | 
			
		||||
    metadataPath = Path.join(metadataPath, 'metadata.abs')
 | 
			
		||||
 | 
			
		||||
    return abmetadataGenerator.generate(this, metadataPath).then((success) => {
 | 
			
		||||
      if (!success) Logger.error(`[Audiobook] Failed saving abmetadata to "${metadataPath}"`)
 | 
			
		||||
      else Logger.debug(`[Audiobook] Success saving abmetadata to "${metadataPath}"`)
 | 
			
		||||
      return success
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
module.exports = Audiobook
 | 
			
		||||
@ -19,7 +19,7 @@ const bookKeyMap = {
 | 
			
		||||
  genres: 'genresCommaSeparated'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generate(audiobook, outputPath, uid, gid) {
 | 
			
		||||
function generate(audiobook, outputPath) {
 | 
			
		||||
  var fileString = ';ABMETADATA1\n'
 | 
			
		||||
  fileString += `#audiobookshelf v${package.version}\n\n`
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ function generate(audiobook, outputPath, uid, gid) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return fs.writeFile(outputPath, fileString).then(() => {
 | 
			
		||||
    return filePerms(outputPath, 0o774, uid, gid).then(() => true)
 | 
			
		||||
    return filePerms(outputPath, 0o774, global.Uid, global.Gid).then(() => true)
 | 
			
		||||
  }).catch((error) => {
 | 
			
		||||
    Logger.error(`[absMetaFileGenerator] Failed to save abs file`, error)
 | 
			
		||||
    return false
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user