From 139ee013a7ad8c173e126cc8ebd0531a225b3ac6 Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 18 May 2022 19:25:18 -0500 Subject: [PATCH] Add:Parsing tags from OPF metadata #602, Update:OPF parser to check for prefix on package/metadata/meta objects --- server/objects/mediaTypes/Book.js | 14 ++++++--- server/utils/parseOpfMetadata.js | 52 ++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/server/objects/mediaTypes/Book.js b/server/objects/mediaTypes/Book.js index 864dd15a..5f1f507d 100644 --- a/server/objects/mediaTypes/Book.js +++ b/server/objects/mediaTypes/Book.js @@ -225,6 +225,7 @@ class Book { // Look for desc.txt, reader.txt, metadata.abs and opf file then update details if found async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) { var metadataUpdatePayload = {} + var tagsUpdated = false var descTxt = textMetadataFiles.find(lf => lf.metadata.filename === 'desc.txt') if (descTxt) { @@ -264,8 +265,13 @@ class Book { var opfMetadata = await parseOpfMetadataXML(xmlText) if (opfMetadata) { for (const key in opfMetadata) { - // Add genres only if genres are empty - if (key === 'genres') { + + if (key === 'tags') { // Add tags only if tags are empty + if (opfMetadata.tags.length && (!this.tags.length || opfMetadataOverrideDetails)) { + this.tags = opfMetadata.tags + tagsUpdated = true + } + } else if (key === 'genres') { // Add genres only if genres are empty if (opfMetadata.genres.length && (!this.metadata.genres.length || opfMetadataOverrideDetails)) { metadataUpdatePayload[key] = opfMetadata.genres } @@ -290,9 +296,9 @@ class Book { } if (Object.keys(metadataUpdatePayload).length) { - return this.metadata.update(metadataUpdatePayload) + return this.metadata.update(metadataUpdatePayload) || tagsUpdated } - return false + return tagsUpdated } searchQuery(query) { diff --git a/server/utils/parseOpfMetadata.js b/server/utils/parseOpfMetadata.js index 8a9bb4f3..95d74775 100644 --- a/server/utils/parseOpfMetadata.js +++ b/server/utils/parseOpfMetadata.js @@ -70,14 +70,14 @@ function fetchLanguage(metadata) { return fetchTagString(metadata, 'dc:language') } -function fetchSeries(metadata) { - if (typeof metadata.meta == "undefined") return null - return fetchTagString(metadata.meta, "calibre:series") +function fetchSeries(metadataMeta) { + if (!metadataMeta) return null + return fetchTagString(metadataMeta, "calibre:series") } -function fetchVolumeNumber(metadata) { - if (typeof metadata.meta == "undefined") return null - return fetchTagString(metadata.meta, "calibre:series_index") +function fetchVolumeNumber(metadataMeta) { + if (!metadataMeta) return null + return fetchTagString(metadataMeta, "calibre:series_index") } function fetchNarrators(creators, metadata) { @@ -91,21 +91,42 @@ function fetchNarrators(creators, metadata) { } } +function fetchTags(metadata) { + if (!metadata['dc:tag'] || !metadata['dc:tag'].length) return [] + return metadata['dc:tag'].filter(tag => (typeof tag === 'string')) +} + +function stripPrefix(str) { + if (!str) return '' + return str.split(':').pop() +} + module.exports.parseOpfMetadataXML = async (xml) => { var json = await xmlToJSON(xml) - if (!json || !json.package || !json.package.metadata) return null - var metadata = json.package.metadata + + if (!json) return null + + // Handle or with prefix + const packageKey = Object.keys(json).find(key => stripPrefix(key) === 'package') + if (!packageKey) return null + const prefix = packageKey.split(':').shift() + var metadata = prefix ? json[packageKey][`${prefix}:metadata`] || json[packageKey].metadata : json[packageKey].metadata + if (!metadata) return null if (Array.isArray(metadata)) { if (!metadata.length) return null metadata = metadata[0] } - if (typeof metadata.meta != "undefined") { - metadata.meta = {} - for (var match of xml.matchAll(//g)) { - metadata.meta[match.groups['name']] = [match.groups['content']] - } + const metadataMeta = prefix ? metadata[`${prefix}:meta`] || metadata.meta : metadata.meta + + metadata.meta = {} + if (metadataMeta && metadataMeta.length) { + metadataMeta.forEach((meta) => { + if (meta && meta['$'] && meta['$'].name) { + metadata.meta[meta['$'].name] = [meta['$'].content || ''] + } + }) } var creators = parseCreators(metadata) @@ -119,8 +140,9 @@ module.exports.parseOpfMetadataXML = async (xml) => { description: fetchDescription(metadata), genres: fetchGenres(metadata), language: fetchLanguage(metadata), - series: fetchSeries(metadata), - sequence: fetchVolumeNumber(metadata) + series: fetchSeries(metadata.meta), + sequence: fetchVolumeNumber(metadata.meta), + tags: fetchTags(metadata) } return data } \ No newline at end of file