mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Update:Book series embeds in grouping meta tag as semicolon deliminated, book meta tag parser falls back to using grouping tag for series if set #3473
This commit is contained in:
parent
72e59e77a7
commit
953ffe889e
@ -9,6 +9,7 @@ class AudioMetaTags {
|
||||
this.tagTitleSort = null
|
||||
this.tagSeries = null
|
||||
this.tagSeriesPart = null
|
||||
this.tagGrouping = null
|
||||
this.tagTrack = null
|
||||
this.tagDisc = null
|
||||
this.tagSubtitle = null
|
||||
@ -116,6 +117,7 @@ class AudioMetaTags {
|
||||
this.tagTitleSort = metadata.tagTitleSort || null
|
||||
this.tagSeries = metadata.tagSeries || null
|
||||
this.tagSeriesPart = metadata.tagSeriesPart || null
|
||||
this.tagGrouping = metadata.tagGrouping || null
|
||||
this.tagTrack = metadata.tagTrack || null
|
||||
this.tagDisc = metadata.tagDisc || null
|
||||
this.tagSubtitle = metadata.tagSubtitle || null
|
||||
@ -156,6 +158,7 @@ class AudioMetaTags {
|
||||
this.tagTitleSort = payload.file_tag_titlesort || null
|
||||
this.tagSeries = payload.file_tag_series || null
|
||||
this.tagSeriesPart = payload.file_tag_seriespart || null
|
||||
this.tagGrouping = payload.file_tag_grouping || null
|
||||
this.tagTrack = payload.file_tag_track || null
|
||||
this.tagDisc = payload.file_tag_disc || null
|
||||
this.tagSubtitle = payload.file_tag_subtitle || null
|
||||
@ -196,6 +199,7 @@ class AudioMetaTags {
|
||||
tagTitleSort: payload.file_tag_titlesort || null,
|
||||
tagSeries: payload.file_tag_series || null,
|
||||
tagSeriesPart: payload.file_tag_seriespart || null,
|
||||
tagGrouping: payload.file_tag_grouping || null,
|
||||
tagTrack: payload.file_tag_track || null,
|
||||
tagDisc: payload.file_tag_disc || null,
|
||||
tagSubtitle: payload.file_tag_subtitle || null,
|
||||
|
@ -4,6 +4,7 @@ const prober = require('../utils/prober')
|
||||
const { LogLevel } = require('../utils/constants')
|
||||
const { parseOverdriveMediaMarkersAsChapters } = require('../utils/parsers/parseOverdriveMediaMarkers')
|
||||
const parseNameString = require('../utils/parsers/parseNameString')
|
||||
const parseSeriesString = require('../utils/parsers/parseSeriesString')
|
||||
const LibraryItem = require('../models/LibraryItem')
|
||||
const AudioFile = require('../objects/files/AudioFile')
|
||||
|
||||
@ -256,6 +257,7 @@ class AudioFileScanner {
|
||||
},
|
||||
{
|
||||
tag: 'tagSeries',
|
||||
altTag: 'tagGrouping',
|
||||
key: 'series'
|
||||
},
|
||||
{
|
||||
@ -276,8 +278,10 @@ class AudioFileScanner {
|
||||
const audioFileMetaTags = firstScannedFile.metaTags
|
||||
MetadataMapArray.forEach((mapping) => {
|
||||
let value = audioFileMetaTags[mapping.tag]
|
||||
let isAltTag = false
|
||||
if (!value && mapping.altTag) {
|
||||
value = audioFileMetaTags[mapping.altTag]
|
||||
isAltTag = true
|
||||
}
|
||||
|
||||
if (value && typeof value === 'string') {
|
||||
@ -290,12 +294,28 @@ class AudioFileScanner {
|
||||
} else if (mapping.key === 'genres') {
|
||||
bookMetadata.genres = this.parseGenresString(value)
|
||||
} else if (mapping.key === 'series') {
|
||||
bookMetadata.series = [
|
||||
{
|
||||
name: value,
|
||||
sequence: audioFileMetaTags.tagSeriesPart || null
|
||||
// If series was embedded in the grouping tag, then parse it with semicolon separator and sequence in the same string
|
||||
// e.g. "Test Series; Series Name #1; Other Series #2"
|
||||
if (isAltTag) {
|
||||
const series = value
|
||||
.split(';')
|
||||
.map((seriesWithPart) => {
|
||||
seriesWithPart = seriesWithPart.trim()
|
||||
return parseSeriesString.parse(seriesWithPart)
|
||||
})
|
||||
.filter(Boolean)
|
||||
if (series.length) {
|
||||
bookMetadata.series = series
|
||||
}
|
||||
]
|
||||
} else {
|
||||
// Original embed used "series" and "series-part" tags
|
||||
bookMetadata.series = [
|
||||
{
|
||||
name: value,
|
||||
sequence: audioFileMetaTags.tagSeriesPart || null
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
bookMetadata[mapping.key] = value
|
||||
}
|
||||
|
@ -380,9 +380,8 @@ function getFFMetadataObject(libraryItem, audioFilesLength) {
|
||||
copyright: metadata.publisher,
|
||||
publisher: metadata.publisher, // mp3 only
|
||||
TRACKTOTAL: `${audioFilesLength}`, // mp3 only
|
||||
grouping: metadata.series?.map((s) => s.name + (s.sequence ? ` #${s.sequence}` : '')).join(', ')
|
||||
grouping: metadata.series?.map((s) => s.name + (s.sequence ? ` #${s.sequence}` : '')).join('; ')
|
||||
}
|
||||
|
||||
Object.keys(ffmetadata).forEach((key) => {
|
||||
if (!ffmetadata[key]) {
|
||||
delete ffmetadata[key]
|
||||
|
@ -1,4 +1,5 @@
|
||||
const Logger = require('../../Logger')
|
||||
const parseSeriesString = require('../parsers/parseSeriesString')
|
||||
|
||||
function parseJsonMetadataText(text) {
|
||||
try {
|
||||
@ -19,39 +20,25 @@ function parseJsonMetadataText(text) {
|
||||
delete abmetadataData.metadata
|
||||
|
||||
if (abmetadataData.series?.length) {
|
||||
abmetadataData.series = [...new Set(abmetadataData.series.map(t => t?.trim()).filter(t => t))]
|
||||
abmetadataData.series = abmetadataData.series.map(series => {
|
||||
let sequence = null
|
||||
let name = series
|
||||
// Series sequence match any characters after " #" other than whitespace and another #
|
||||
// e.g. "Name #1a" is valid. "Name #1#a" or "Name #1 a" is not valid.
|
||||
const matchResults = series.match(/ #([^#\s]+)$/) // Pull out sequence #
|
||||
if (matchResults && matchResults.length && matchResults.length > 1) {
|
||||
sequence = matchResults[1] // Group 1
|
||||
name = series.replace(matchResults[0], '')
|
||||
}
|
||||
return {
|
||||
name,
|
||||
sequence
|
||||
}
|
||||
})
|
||||
abmetadataData.series = [...new Set(abmetadataData.series.map((t) => t?.trim()).filter((t) => t))]
|
||||
abmetadataData.series = abmetadataData.series.map((series) => parseSeriesString.parse(series))
|
||||
}
|
||||
// clean tags & remove dupes
|
||||
if (abmetadataData.tags?.length) {
|
||||
abmetadataData.tags = [...new Set(abmetadataData.tags.map(t => t?.trim()).filter(t => t))]
|
||||
abmetadataData.tags = [...new Set(abmetadataData.tags.map((t) => t?.trim()).filter((t) => t))]
|
||||
}
|
||||
if (abmetadataData.chapters?.length) {
|
||||
abmetadataData.chapters = cleanChaptersArray(abmetadataData.chapters, abmetadataData.title)
|
||||
}
|
||||
// clean remove dupes
|
||||
if (abmetadataData.authors?.length) {
|
||||
abmetadataData.authors = [...new Set(abmetadataData.authors.map(t => t?.trim()).filter(t => t))]
|
||||
abmetadataData.authors = [...new Set(abmetadataData.authors.map((t) => t?.trim()).filter((t) => t))]
|
||||
}
|
||||
if (abmetadataData.narrators?.length) {
|
||||
abmetadataData.narrators = [...new Set(abmetadataData.narrators.map(t => t?.trim()).filter(t => t))]
|
||||
abmetadataData.narrators = [...new Set(abmetadataData.narrators.map((t) => t?.trim()).filter((t) => t))]
|
||||
}
|
||||
if (abmetadataData.genres?.length) {
|
||||
abmetadataData.genres = [...new Set(abmetadataData.genres.map(t => t?.trim()).filter(t => t))]
|
||||
abmetadataData.genres = [...new Set(abmetadataData.genres.map((t) => t?.trim()).filter((t) => t))]
|
||||
}
|
||||
return abmetadataData
|
||||
} catch (error) {
|
||||
|
27
server/utils/parsers/parseSeriesString.js
Normal file
27
server/utils/parsers/parseSeriesString.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Parse a series string into a name and sequence
|
||||
*
|
||||
* @example
|
||||
* Name #1a => { name: 'Name', sequence: '1a' }
|
||||
* Name #1 => { name: 'Name', sequence: '1' }
|
||||
*
|
||||
* @param {string} seriesString
|
||||
* @returns {{name: string, sequence: string}|null}
|
||||
*/
|
||||
module.exports.parse = (seriesString) => {
|
||||
if (!seriesString || typeof seriesString !== 'string') return null
|
||||
|
||||
let sequence = null
|
||||
let name = seriesString
|
||||
// Series sequence match any characters after " #" other than whitespace and another #
|
||||
// e.g. "Name #1a" is valid. "Name #1#a" or "Name #1 a" is not valid.
|
||||
const matchResults = seriesString.match(/ #([^#\s]+)$/) // Pull out sequence #
|
||||
if (matchResults && matchResults.length && matchResults.length > 1) {
|
||||
sequence = matchResults[1] // Group 1
|
||||
name = seriesString.replace(matchResults[0], '')
|
||||
}
|
||||
return {
|
||||
name,
|
||||
sequence
|
||||
}
|
||||
}
|
@ -189,6 +189,7 @@ function parseTags(format, verbose) {
|
||||
file_tag_genre: tryGrabTags(format, 'genre', 'tcon', 'tco'),
|
||||
file_tag_series: tryGrabTags(format, 'series', 'show', 'mvnm'),
|
||||
file_tag_seriespart: tryGrabTags(format, 'series-part', 'episode_id', 'mvin', 'part'),
|
||||
file_tag_grouping: tryGrabTags(format, 'grouping'),
|
||||
file_tag_isbn: tryGrabTags(format, 'isbn'), // custom
|
||||
file_tag_language: tryGrabTags(format, 'language', 'lang'),
|
||||
file_tag_asin: tryGrabTags(format, 'asin', 'audible_asin'), // custom
|
||||
|
Loading…
Reference in New Issue
Block a user