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)
}
diff --git a/test/server/utils/parsers/parseOpfMetadata.test.js b/test/server/utils/parsers/parseOpfMetadata.test.js
index ca033cca..32dae922 100644
--- a/test/server/utils/parsers/parseOpfMetadata.test.js
+++ b/test/server/utils/parsers/parseOpfMetadata.test.js
@@ -3,8 +3,8 @@ const expect = chai.expect
const { parseOpfMetadataXML } = require('../../../../server/utils/parsers/parseOpfMetadata')
describe('parseOpfMetadata - test series', async () => {
- it('test one series', async () => {
- const opf = `
+ it('test one series', async () => {
+ const opf = `
@@ -13,12 +13,12 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([{ "name": "Serie", "sequence": "1" }])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([{ name: 'Serie', sequence: '1' }])
+ })
- it('test more then 1 series - in correct order', async () => {
- const opf = `
+ it('test more then 1 series - in correct order', async () => {
+ const opf = `
@@ -31,16 +31,16 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([
- { "name": "Serie 1", "sequence": "1" },
- { "name": "Serie 2", "sequence": "2" },
- { "name": "Serie 3", "sequence": "3" },
- ])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([
+ { name: 'Serie 1', sequence: '1' },
+ { name: 'Serie 2', sequence: '2' },
+ { name: 'Serie 3', sequence: '3' }
+ ])
+ })
- it('test messed order of series content and index', async () => {
- const opf = `
+ it('test messed order of series content and index', async () => {
+ const opf = `
@@ -52,15 +52,15 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([
- { "name": "Serie 1", "sequence": "1" },
- { "name": "Serie 3", "sequence": null },
- ])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([
+ { name: 'Serie 1', sequence: '1' },
+ { name: 'Serie 3', sequence: null }
+ ])
+ })
- it('test different values of series content and index', async () => {
- const opf = `
+ it('test different values of series content and index', async () => {
+ const opf = `
@@ -73,16 +73,16 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([
- { "name": "Serie 1", "sequence": null },
- { "name": "Serie 2", "sequence": "abc" },
- { "name": "Serie 3", "sequence": null },
- ])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([
+ { name: 'Serie 1', sequence: null },
+ { name: 'Serie 2', sequence: 'abc' },
+ { name: 'Serie 3', sequence: null }
+ ])
+ })
- it('test empty series content', async () => {
- const opf = `
+ it('test empty series content', async () => {
+ const opf = `
@@ -91,12 +91,12 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([])
+ })
- it('test series and index using an xml namespace', async () => {
- const opf = `
+ it('test series and index using an xml namespace', async () => {
+ const opf = `
@@ -105,14 +105,12 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([
- { "name": "Serie 1", "sequence": null }
- ])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([{ name: 'Serie 1', sequence: null }])
+ })
- it('test series and series index not directly underneath', async () => {
- const opf = `
+ it('test series and series index not directly underneath', async () => {
+ const opf = `
@@ -122,9 +120,21 @@ describe('parseOpfMetadata - test series', async () => {
`
- const parsedOpf = await parseOpfMetadataXML(opf)
- expect(parsedOpf.series).to.deep.equal([
- { "name": "Serie 1", "sequence": "1" }
- ])
- })
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.series).to.deep.equal([{ name: 'Serie 1', sequence: '1' }])
+ })
+
+ it('test author is parsed from refines meta', async () => {
+ const opf = `
+
+
+ Nevil Shute
+ aut
+ Shute, Nevil
+
+
+ `
+ const parsedOpf = await parseOpfMetadataXML(opf)
+ expect(parsedOpf.authors).to.deep.equal(['Nevil Shute'])
+ })
})