diff --git a/server/scanner/AbsMetadataFileScanner.js b/server/scanner/AbsMetadataFileScanner.js index d9d077c0..037726f6 100644 --- a/server/scanner/AbsMetadataFileScanner.js +++ b/server/scanner/AbsMetadataFileScanner.js @@ -55,7 +55,7 @@ class AbsMetadataFileScanner { bookMetadata.chapters = abMetadata.chapters } for (const key in abMetadata.metadata) { - if (abMetadata.metadata[key] === undefined) continue + if (abMetadata.metadata[key] === undefined || abMetadata.metadata[key] === null) continue bookMetadata[key] = abMetadata.metadata[key] } } diff --git a/server/scanner/LibraryItemScanData.js b/server/scanner/LibraryItemScanData.js index c272127f..576280c8 100644 --- a/server/scanner/LibraryItemScanData.js +++ b/server/scanner/LibraryItemScanData.js @@ -309,7 +309,7 @@ class LibraryItemScanData { * @param {Object} bookMetadata */ setBookMetadataFromFilenames(bookMetadata) { - const keysToMap = ['title', 'subtitle', 'publishedYear'] + const keysToMap = ['title', 'subtitle', 'publishedYear', 'asin'] for (const key in this.mediaMetadata) { if (keysToMap.includes(key) && this.mediaMetadata[key]) { bookMetadata[key] = this.mediaMetadata[key] diff --git a/server/utils/scandir.js b/server/utils/scandir.js index df6639e0..21c28b8c 100644 --- a/server/utils/scandir.js +++ b/server/utils/scandir.js @@ -8,6 +8,7 @@ const parseNameString = require('./parsers/parseNameString') * @typedef LibraryItemFilenameMetadata * @property {string} title * @property {string} subtitle Book mediaType only + * @property {string} asin Book mediaType only * @property {string[]} authors Book mediaType only * @property {string[]} narrators Book mediaType only * @property {string} seriesName Book mediaType only @@ -237,14 +238,17 @@ function getBookDataFromDir(relPath, parseSubtitle = false) { author = (splitDir.length > 0) ? splitDir.pop() : null // There could be many more directories, but only the top 3 are used for naming /author/series/title/ // The may contain various other pieces of metadata, these functions extract it. + var [folder, asin] = getASIN(folder) var [folder, narrators] = getNarrator(folder) var [folder, sequence] = series ? getSequence(folder) : [folder, null] var [folder, publishedYear] = getPublishedYear(folder) var [title, subtitle] = parseSubtitle ? getSubtitle(folder) : [folder, null] + return { title, subtitle, + asin, authors: parseNameString.parse(author)?.names || [], narrators: parseNameString.parse(narrators)?.names || [], seriesName: series, @@ -254,27 +258,36 @@ function getBookDataFromDir(relPath, parseSubtitle = false) { } module.exports.getBookDataFromDir = getBookDataFromDir +/** + * Extract narrator from folder name + * + * @param {string} folder + * @returns {[string, string]} [folder, narrator] + */ function getNarrator(folder) { let pattern = /^(?.*) \{(?<narrators>.*)\}$/ let match = folder.match(pattern) return match ? [match.groups.title, match.groups.narrators] : [folder, null] } +/** + * Extract series sequence from folder name + * + * @example + * 'Book 2 - Title - Subtitle' + * 'Title - Subtitle - Vol 12' + * 'Title - volume 9 - Subtitle' + * 'Vol. 3 Title Here - Subtitle' + * '1980 - Book 2 - Title' + * 'Volume 12. Title - Subtitle' + * '100 - Book Title' + * '6. Title' + * '0.5 - Book Title' + * + * @param {string} folder + * @returns {[string, string]} [folder, sequence] + */ function getSequence(folder) { - // Valid ways of including a volume number: - // [ - // 'Book 2 - Title - Subtitle', - // 'Title - Subtitle - Vol 12', - // 'Title - volume 9 - Subtitle', - // 'Vol. 3 Title Here - Subtitle', - // '1980 - Book 2 - Title', - // 'Volume 12. Title - Subtitle', - // '100 - Book Title', - // '2 - Book Title', - // '6. Title', - // '0.5 - Book Title' - // ] - // Matches a valid volume string. Also matches a book whose title starts with a 1 to 3 digit number. Will handle that later. let pattern = /^(?<volumeLabel>vol\.? |volume |book )?(?<sequence>\d{0,3}(?:\.\d{1,2})?)(?<trailingDot>\.?)(?: (?<suffix>.*))?$/i @@ -295,6 +308,12 @@ function getSequence(folder) { return [folder, volumeNumber] } +/** + * Extract published year from folder name + * + * @param {string} folder + * @returns {[string, string]} [folder, publishedYear] + */ function getPublishedYear(folder) { var publishedYear = null @@ -308,12 +327,36 @@ function getPublishedYear(folder) { return [folder, publishedYear] } +/** + * Extract subtitle from folder name + * + * @param {string} folder + * @returns {[string, string]} [folder, subtitle] + */ function getSubtitle(folder) { // Subtitle is everything after " - " var splitTitle = folder.split(' - ') return [splitTitle.shift(), splitTitle.join(' - ')] } +/** + * Extract asin from folder name + * + * @param {string} folder + * @returns {[string, string]} [folder, asin] + */ +function getASIN(folder) { + let asin = null + + let pattern = /(?: |^)\[([A-Z0-9]{10})](?= |$)/ // Matches "[B0015T963C]" + const match = folder.match(pattern) + if (match) { + asin = match[1] + folder = folder.replace(match[0], '') + } + return [folder.trim(), asin] +} + /** * * @param {string} relPath