From 295c6b0c746254ff4de591d90eef88f9d3dac8a1 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 27 Feb 2022 14:28:18 -0600 Subject: [PATCH] Add:Generate book metadata file when book details are changed,Add:Server setting for storing book metadata in book folder --- client/pages/config/index.vue | 17 ++++++++++++++--- server/Db.js | 9 ++++++++- server/Server.js | 6 ++++++ server/objects/Audiobook.js | 28 ++++++++++++++++++++++++++++ server/utils/abmetadataGenerator.js | 4 ++-- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index 9f09e750..d4d099fb 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -11,7 +11,17 @@

- Store covers with audiobook + Store covers with book + info_outlined +

+
+ + +
+ + +

+ Store metadata with book info_outlined

@@ -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.
Subtitle must be seperated by " - "
i.e. "Book Title - A Subtitle Here" has the subtitle "A Subtitle Here"', + scannerParseSubtitle: 'Extract subtitles from audiobook folder names.
Subtitle must be seperated by " - "
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.
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' } } diff --git a/server/Db.js b/server/Db.js index 75868ccf..3f430953 100644 --- a/server/Db.js +++ b/server/Db.js @@ -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 diff --git a/server/Server.js b/server/Server.js index 90a78422..2838f21e 100644 --- a/server/Server.js +++ b/server/Server.js @@ -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`) diff --git a/server/objects/Audiobook.js b/server/objects/Audiobook.js index a7e41262..7ee9f23a 100644 --- a/server/objects/Audiobook.js +++ b/server/objects/Audiobook.js @@ -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 \ No newline at end of file diff --git a/server/utils/abmetadataGenerator.js b/server/utils/abmetadataGenerator.js index 668b7eee..39fd0005 100644 --- a/server/utils/abmetadataGenerator.js +++ b/server/utils/abmetadataGenerator.js @@ -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