From de22177dbf7413d8cb128e7c1c0dea941583afbc Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 13 Mar 2025 17:49:05 -0500 Subject: [PATCH] Update opf parser to support refines meta elements --- server/scanner/OpfFileScanner.js | 16 +++++++------ server/utils/parsers/parseOpfMetadata.js | 29 ++++++++++++++++++++---- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/server/scanner/OpfFileScanner.js b/server/scanner/OpfFileScanner.js index 87c4f565..13f6cc16 100644 --- a/server/scanner/OpfFileScanner.js +++ b/server/scanner/OpfFileScanner.js @@ -2,24 +2,26 @@ const { parseOpfMetadataXML } = require('../utils/parsers/parseOpfMetadata') const { readTextFile } = require('../utils/fileUtils') class OpfFileScanner { - constructor() { } + constructor() {} /** * Parse metadata from .opf file found in library scan and update bookMetadata - * - * @param {import('../models/LibraryItem').LibraryFileObject} opfLibraryFileObj - * @param {Object} bookMetadata + * + * @param {import('../models/LibraryItem').LibraryFileObject} opfLibraryFileObj + * @param {Object} bookMetadata */ async scanBookOpfFile(opfLibraryFileObj, bookMetadata) { const xmlText = await readTextFile(opfLibraryFileObj.metadata.path) const opfMetadata = xmlText ? await parseOpfMetadataXML(xmlText) : null if (opfMetadata) { for (const key in opfMetadata) { - if (key === 'tags') { // Add tags only if tags are empty + if (key === 'tags') { + // Add tags only if tags are empty if (opfMetadata.tags.length) { bookMetadata.tags = opfMetadata.tags } - } else if (key === 'genres') { // Add genres only if genres are empty + } else if (key === 'genres') { + // Add genres only if genres are empty if (opfMetadata.genres.length) { bookMetadata.genres = opfMetadata.genres } @@ -42,4 +44,4 @@ class OpfFileScanner { } } } -module.exports = new OpfFileScanner() \ No newline at end of file +module.exports = new OpfFileScanner() diff --git a/server/utils/parsers/parseOpfMetadata.js b/server/utils/parsers/parseOpfMetadata.js index 8cf768cd..9a55c1f2 100644 --- a/server/utils/parsers/parseOpfMetadata.js +++ b/server/utils/parsers/parseOpfMetadata.js @@ -22,11 +22,22 @@ function parseCreators(metadata) { Object.keys(c['$']) .find((key) => key.startsWith('xmlns:')) ?.split(':')[1] || 'opf' - return { + const creator = { value: c['_'], role: c['$'][`${namespace}:role`] || null, fileAs: c['$'][`${namespace}:file-as`] || null } + + const id = c['$']['id'] + if (id && metadata.meta.refines?.some((r) => r.refines === `#${id}`)) { + const creatorMeta = metadata.meta.refines.filter((r) => r.refines === `#${id}`) + if (creatorMeta) { + creator.role = creatorMeta.find((r) => r.property === 'role')?.value || creator.role || null + creator.fileAs = creatorMeta.find((r) => r.property === 'file-as')?.value || creator.fileAs || null + } + } + + return creator }) } @@ -187,7 +198,6 @@ module.exports.parseOpfMetadataJson = (json) => { const prefix = packageKey.split(':').shift() let 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] @@ -198,12 +208,22 @@ module.exports.parseOpfMetadataJson = (json) => { metadata.meta = {} if (metadataMeta?.length) { metadataMeta.forEach((meta) => { - if (meta && meta['$'] && meta['$'].name) { + if (meta?.['$']?.name) { metadata.meta[meta['$'].name] = [meta['$'].content || ''] + } else if (meta?.['$']?.refines) { + // https://www.w3.org/TR/epub-33/#sec-meta-elem + + if (!metadata.meta.refines) { + metadata.meta.refines = [] + } + metadata.meta.refines.push({ + value: meta._, + refines: meta['$'].refines, + property: meta['$'].property + }) } }) } - const creators = parseCreators(metadata) const authors = (fetchCreators(creators, 'aut') || []).map((au) => au?.trim()).filter((au) => au) const narrators = (fetchNarrators(creators, metadata) || []).map((nrt) => nrt?.trim()).filter((nrt) => nrt) @@ -227,5 +247,6 @@ module.exports.parseOpfMetadataJson = (json) => { module.exports.parseOpfMetadataXML = async (xml) => { const json = await xmlToJSON(xml) if (!json) return null + return this.parseOpfMetadataJson(json) }