Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OPF fetchSeries so it deduplicates found series #3152

Merged
merged 1 commit into from
Jul 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions server/utils/parsers/parseOpfMetadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function parseCreators(metadata) {
if (!metadata['dc:creator']) return null
const creators = metadata['dc:creator']
if (!creators.length) return null
return creators.map(c => {
return creators.map((c) => {
if (typeof c !== 'object' || !c['$'] || !c['_']) return false
return {
value: c['_'],
Expand All @@ -17,7 +17,7 @@ function parseCreators(metadata) {

function fetchCreators(creators, role) {
if (!creators?.length) return null
return [...new Set(creators.filter(c => c.role === role && c.value).map(c => c.value))]
return [...new Set(creators.filter((c) => c.role === role && c.value).map((c) => c.value))]
}

function fetchTagString(metadata, tag) {
Expand Down Expand Up @@ -62,14 +62,14 @@ function fetchPublisher(metadata) {
function fetchISBN(metadata) {
if (!metadata['dc:identifier'] || !metadata['dc:identifier'].length) return null
const identifiers = metadata['dc:identifier']
const isbnObj = identifiers.find(i => i['$'] && i['$']['opf:scheme'] === 'ISBN')
const isbnObj = identifiers.find((i) => i['$'] && i['$']['opf:scheme'] === 'ISBN')
return isbnObj ? isbnObj['_'] || null : null
}

function fetchASIN(metadata) {
if (!metadata['dc:identifier'] || !metadata['dc:identifier'].length) return null
const identifiers = metadata['dc:identifier']
const asinObj = identifiers.find(i => i['$'] && i['$']['opf:scheme'] === 'ASIN')
const asinObj = identifiers.find((i) => i['$'] && i['$']['opf:scheme'] === 'ASIN')
return asinObj ? asinObj['_'] || null : null
}

Expand All @@ -92,7 +92,7 @@ function fetchDescription(metadata) {

function fetchGenres(metadata) {
if (!metadata['dc:subject'] || !metadata['dc:subject'].length) return []
return [...new Set(metadata['dc:subject'].filter(g => g && typeof g === 'string'))]
return [...new Set(metadata['dc:subject'].filter((g) => g && typeof g === 'string'))]
}

function fetchLanguage(metadata) {
Expand All @@ -116,28 +116,32 @@ function fetchSeries(metadataMeta) {
// If one series was found with no series_index then check if any series_index meta can be found
// this is to support when calibre:series_index is not directly underneath calibre:series
if (result.length === 1 && !result[0].sequence) {
const seriesIndexMeta = metadataMeta.find(m => m.$?.name === 'calibre:series_index' && m.$.content?.trim())
const seriesIndexMeta = metadataMeta.find((m) => m.$?.name === 'calibre:series_index' && m.$.content?.trim())
if (seriesIndexMeta) {
result[0].sequence = seriesIndexMeta.$.content.trim()
}
}
return result

// Remove duplicates
const dedupedResult = result.filter((se, idx) => result.findIndex((s) => s.name === se.name) === idx)

return dedupedResult
}

function fetchNarrators(creators, metadata) {
const narrators = fetchCreators(creators, 'nrt')
if (narrators?.length) return narrators
try {
const narratorsJSON = JSON.parse(fetchTagString(metadata.meta, "calibre:user_metadata:#narrators").replace(/"/g, '"'))
return narratorsJSON["#value#"]
const narratorsJSON = JSON.parse(fetchTagString(metadata.meta, 'calibre:user_metadata:#narrators').replace(/"/g, '"'))
return narratorsJSON['#value#']
} catch {
return null
}
}

function fetchTags(metadata) {
if (!metadata['dc:tag'] || !metadata['dc:tag'].length) return []
return [...new Set(metadata['dc:tag'].filter(tag => tag && typeof tag === 'string'))]
return [...new Set(metadata['dc:tag'].filter((tag) => tag && typeof tag === 'string'))]
}

function stripPrefix(str) {
Expand All @@ -147,7 +151,7 @@ function stripPrefix(str) {

module.exports.parseOpfMetadataJson = (json) => {
// Handle <package ...> or with prefix <ns0:package ...>
const packageKey = Object.keys(json).find(key => stripPrefix(key) === 'package')
const packageKey = Object.keys(json).find((key) => stripPrefix(key) === 'package')
if (!packageKey) return null
const prefix = packageKey.split(':').shift()
let metadata = prefix ? json[packageKey][`${prefix}:metadata`] || json[packageKey].metadata : json[packageKey].metadata
Expand All @@ -170,8 +174,8 @@ module.exports.parseOpfMetadataJson = (json) => {
}

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)
const authors = (fetchCreators(creators, 'aut') || []).map((au) => au?.trim()).filter((au) => au)
const narrators = (fetchNarrators(creators, metadata) || []).map((nrt) => nrt?.trim()).filter((nrt) => nrt)
return {
title: fetchTitle(metadata),
subtitle: fetchSubtitle(metadata),
Expand All @@ -193,4 +197,4 @@ module.exports.parseOpfMetadataXML = async (xml) => {
const json = await xmlToJSON(xml)
if (!json) return null
return this.parseOpfMetadataJson(json)
}
}
Loading