From 8754f0c25ff8d70f00e37d1d51b53bf97fa34c33 Mon Sep 17 00:00:00 2001 From: jmt-gh Date: Sun, 12 Jun 2022 01:38:09 -0700 Subject: [PATCH] Cleaning up server code Doing some literal cleaning --- server/objects/mediaTypes/Book.js | 109 ++++++------- .../parsers/parseOverdriveMediaMarkers.js | 154 ++++++++++-------- 2 files changed, 136 insertions(+), 127 deletions(-) diff --git a/server/objects/mediaTypes/Book.js b/server/objects/mediaTypes/Book.js index c9d4d429..ed791e31 100644 --- a/server/objects/mediaTypes/Book.js +++ b/server/objects/mediaTypes/Book.js @@ -3,7 +3,7 @@ const Logger = require('../../Logger') const BookMetadata = require('../metadata/BookMetadata') const { areEquivalent, copyValue } = require('../../utils/index') const { parseOpfMetadataXML } = require('../../utils/parsers/parseOpfMetadata') -const { getOverdriveMediaMarkersFromFiles, parseOverdriveMediaMarkers } = require('../../utils/parsers/parseOverdriveMediaMarkers') +const { overdriveMediaMarkersExist, parseOverdriveMediaMarkersAsChapters } = require('../../utils/parsers/parseOverdriveMediaMarkers') const abmetadataGenerator = require('../../utils/abmetadataGenerator') const { readTextFile } = require('../../utils/fileUtils') const AudioFile = require('../files/AudioFile') @@ -403,63 +403,60 @@ class Book { // If 1 audio file without chapters, then no chapters will be set var includedAudioFiles = this.audioFiles.filter(af => !af.exclude) - var overdriveMediaMarkers = getOverdriveMediaMarkersFromFiles(includedAudioFiles) + // If overdrive media markers are present and preferred, use those instead + if (preferOverdriveMediaMarker && overdriveMediaMarkersExist(includedAudioFiles)) { + Logger.info('[Book] Overdrive Media Markers and preference found! Using these for chapter definitions') + return this.chapters = parseOverdriveMediaMarkersAsChapters(includedAudioFiles) + } - // If preferOverdriveMediaMarker is set, try and use that first - // fallback to non-overdrive chapters if there are no Overdrive Media Markers available - if (preferOverdriveMediaMarker && (overdriveMediaMarkers.length > 0)) { - Logger.debug(`[Book] preferring overdrive media markers! Lets generate em.`) - this.chapters = parseOverdriveMediaMarkers(overdriveMediaMarkers, includedAudioFiles) - } else { - if (includedAudioFiles.length === 1) { - // 1 audio file with chapters - if (includedAudioFiles[0].chapters) { - this.chapters = includedAudioFiles[0].chapters.map(c => ({ ...c })) - } - } else { - this.chapters = [] - var currChapterId = 0 - var currStartTime = 0 - includedAudioFiles.forEach((file) => { - //console.log(`audiofile MetaTags Overdrive: ${JSON.stringify(file.metaTags.tagOverdriveMediaMarker)}}`) - // If audio file has chapters use chapters - if (file.chapters && file.chapters.length) { - file.chapters.forEach((chapter) => { - if (chapter.start > this.duration) { - Logger.warn(`[Book] Invalid chapter start time > duration`) - } else { - var chapterAlreadyExists = this.chapters.find(ch => ch.start === chapter.start) - if (!chapterAlreadyExists) { - var chapterDuration = chapter.end - chapter.start - if (chapterDuration > 0) { - var title = `Chapter ${currChapterId}` - if (chapter.title) { - title += ` (${chapter.title})` - } - var endTime = Math.min(this.duration, currStartTime + chapterDuration) - this.chapters.push({ - id: currChapterId++, - start: currStartTime, - end: endTime, - title - }) - currStartTime += chapterDuration - } - } - } - }) - } else if (file.duration) { - // Otherwise just use track has chapter - this.chapters.push({ - id: currChapterId++, - start: currStartTime, - end: currStartTime + file.duration, - title: file.metadata.filename ? Path.basename(file.metadata.filename, Path.extname(file.metadata.filename)) : `Chapter ${currChapterId}` - }) - currStartTime += file.duration - } - }) + if (includedAudioFiles.length === 1) { + // 1 audio file with chapters + if (includedAudioFiles[0].chapters) { + this.chapters = includedAudioFiles[0].chapters.map(c => ({ ...c })) } + } else { + this.chapters = [] + var currChapterId = 0 + var currStartTime = 0 + includedAudioFiles.forEach((file) => { + //console.log(`audiofile MetaTags Overdrive: ${JSON.stringify(file.metaTags.tagOverdriveMediaMarker)}}`) + // If audio file has chapters use chapters + if (file.chapters && file.chapters.length) { + file.chapters.forEach((chapter) => { + if (chapter.start > this.duration) { + Logger.warn(`[Book] Invalid chapter start time > duration`) + } else { + var chapterAlreadyExists = this.chapters.find(ch => ch.start === chapter.start) + if (!chapterAlreadyExists) { + var chapterDuration = chapter.end - chapter.start + if (chapterDuration > 0) { + var title = `Chapter ${currChapterId}` + if (chapter.title) { + title += ` (${chapter.title})` + } + var endTime = Math.min(this.duration, currStartTime + chapterDuration) + this.chapters.push({ + id: currChapterId++, + start: currStartTime, + end: endTime, + title + }) + currStartTime += chapterDuration + } + } + } + }) + } else if (file.duration) { + // Otherwise just use track has chapter + this.chapters.push({ + id: currChapterId++, + start: currStartTime, + end: currStartTime + file.duration, + title: file.metadata.filename ? Path.basename(file.metadata.filename, Path.extname(file.metadata.filename)) : `Chapter ${currChapterId}` + }) + currStartTime += file.duration + } + }) } } diff --git a/server/utils/parsers/parseOverdriveMediaMarkers.js b/server/utils/parsers/parseOverdriveMediaMarkers.js index 58d5003c..da323924 100644 --- a/server/utils/parsers/parseOverdriveMediaMarkers.js +++ b/server/utils/parsers/parseOverdriveMediaMarkers.js @@ -1,53 +1,16 @@ const Logger = require('../../Logger') -// given an array of audioFiles, return an array of unparsed MediaMarkers -module.exports.getOverdriveMediaMarkersFromFiles = (audioFiles) => { - var markers = audioFiles.map((af) => af.metaTags.tagOverdriveMediaMarker).filter(notUndefined => notUndefined !== undefined).filter(elem => { return elem !== null }) || [] +// given a list of audio files, extract all of the Overdrive Media Markers metaTags, and return an array of them as XML +function overdriveMediaMarkers(includedAudioFiles) { + var markers = includedAudioFiles.map((af) => af.metaTags.tagOverdriveMediaMarker).filter(notUndefined => notUndefined !== undefined).filter(elem => { return elem !== null }) || [] return markers } -module.exports.parseOverdriveMediaMarkers = (overdriveMediaMarkers, includedAudioFiles) => { +// given the array of Overdrive Media Markers from generateOverdriveMediaMarkers() +// parse and clean them in to something a bit more usable +function cleanOverdriveMediaMarkers(overdriveMediaMarkers) { var parseString = require('xml2js').parseString; // function to convert xml to JSON - - var parsedOverdriveMediaMarkers = [] // an array of objects. each object being a chapter with a name and time key. the values are arrays of strings - - overdriveMediaMarkers.forEach(function (item, index) { - var parsed_result - parseString(item, function (err, result) { - // result.Markers.Marker is the result of parsing the XML for the MediaMarker tags for the MP3 file (Part##.mp3) - // it is shaped like this: - // [ - // { - // "Name": [ - // "Chapter 1: " - // ], - // "Time": [ - // "0:00.000" - // ] - // }, - // { - // "Name": [ - // "Chapter 2: " - // ], - // "Time": [ - // "15:51.000" - // ] - // } - // ] - - parsed_result = result.Markers.Marker - - // The values for Name and Time in parsed_results are returned as Arrays from parseString - // update them to be strings - parsed_result.forEach((item, index) => { - Object.keys(item).forEach(key => { - item[key] = item[key].toString() - }) - }) - }) - - parsedOverdriveMediaMarkers.push(parsed_result) - }) + var parsedOverdriveMediaMarkers = [] // go from an array of arrays of objects to an array of objects // end result looks like: @@ -62,40 +25,89 @@ module.exports.parseOverdriveMediaMarkers = (overdriveMediaMarkers, includedAudi // }, // { redacted } // ] - parsedOverdriveMediaMarkers = parsedOverdriveMediaMarkers + overdriveMediaMarkers.forEach(function (item, index) { + var parsed_result + parseString(item, function (err, result) { + // result.Markers.Marker is the result of parsing the XML for the MediaMarker tags for the MP3 file (Part##.mp3) + // it is shaped like this: + // [ + // { + // "Name": [ + // "Chapter 1: " + // ], + // "Time": [ + // "0:00.000" + // ] + // }, + // { + // "Name": [ + // "Chapter 2: " + // ], + // "Time": [ + // "15:51.000" + // ] + // } + // ] - var index = 0 - - var time = 0.0 - + parsed_result = result.Markers.Marker + // The values for Name and Time in parsed_results are returned as Arrays from parseString + // update them to be strings + parsed_result.forEach((item, index) => { + Object.keys(item).forEach(key => { + item[key] = item[key].toString() + }) + }) + }) + + parsedOverdriveMediaMarkers.push(parsed_result) + }) + + return parsedOverdriveMediaMarkers +} + +// The function that actually generates the Chapters object that we update ABS with +function generateParsedChapters(includedAudioFiles, cleanedOverdriveMediaMarkers) { // actually generate the chapter object // logic ported over from benonymity's OverdriveChapterizer: // https://github.com/benonymity/OverdriveChapterizer/blob/main/chapters.py var length = 0.0 - var newOChapters = [] + var index = 0 + var time = 0.0 + var newChapters = [] const weirdChapterFilterRegex = /([(]\d|[cC]ontinued)/ includedAudioFiles.forEach((track, track_index) => { - parsedOverdriveMediaMarkers[track_index].forEach((chapter) => { - Logger.debug(`[parseOverdriveMediaMarkers] Attempting regex check for ${chapter.Name}!`) - if (weirdChapterFilterRegex.test(chapter.Name)) { - Logger.debug(`[parseOverdriveMediaMarkers] That shit weird yo`) - return - } - time = chapter.Time.split(":") - time = length + parseFloat(time[0]) * 60 + parseFloat(time[1]) - newOChapters.push( - { - id: index++, - start: time, - end: length, - title: chapter.Name - } - ) - }) - length += track.duration + cleanedOverdriveMediaMarkers[track_index].forEach((chapter) => { + Logger.debug(`[parseOverdriveMediaMarkers] Attempting regex check for ${chapter.Name}...`) + if (weirdChapterFilterRegex.test(chapter.Name)) { + Logger.debug(`[parseOverdriveMediaMarkers] Regex matched. Skipping ${chapter.Name}!`) + return + } + time = chapter.Time.split(":") + time = length + parseFloat(time[0]) * 60 + parseFloat(time[1]) + newChapters.push( + { + id: index++, + start: time, + end: length, + title: chapter.Name + } + ) + }) + length += track.duration }) - Logger.debug(`[parseOverdriveMediaMarkers] newOChapters: ${JSON.stringify(newOChapters)}`) - return newOChapters -} \ No newline at end of file + return newChapters +} + +module.exports.overdriveMediaMarkersExist = (includedAudioFiles) => { + return overdriveMediaMarkers(includedAudioFiles).length > 1 +} + +module.exports.parseOverdriveMediaMarkersAsChapters = (includedAudioFiles) => { + Logger.info('[parseOverdriveMediaMarkers] Parsing of Overdrive Media Markers started') + var cleanedOverdriveMediaMarkers = cleanOverdriveMediaMarkers(overdriveMediaMarkers(includedAudioFiles)) + var parsedChapters = generateParsedChapters(includedAudioFiles, cleanedOverdriveMediaMarkers) + + return parsedChapters +}