Initial commit for server side approach

This is the first commit for bringing this over to the server side.

It works! Right now it fails if the autoscanner or or the manual
individual book scanner try to do it's thing. I'll need to update those
This commit is contained in:
jmt-gh 2022-06-11 23:17:22 -07:00
parent effc63755b
commit b3d9323f66
6 changed files with 175 additions and 42 deletions

View file

@ -360,10 +360,12 @@ class Book {
this.rebuildTracks()
}
rebuildTracks() {
rebuildTracks(preferOverdriveMediaMarker = false) {
Logger.debug(`[Book] we are rebuilding the tracks!`)
Logger.debug(`[Book] preferOverdriveMediaMarker: ${preferOverdriveMediaMarker}`)
this.audioFiles.sort((a, b) => a.index - b.index)
this.missingParts = []
this.setChapters()
this.setChapters(preferOverdriveMediaMarker)
this.checkUpdateMissingTracks()
}
@ -395,7 +397,103 @@ class Book {
return wasUpdated
}
setChapters() {
generateChaptersFromOverdriveMediaMarkers(overdriveMediaMarkers, includedAudioFiles) {
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)
})
// go from an array of arrays of objects to an array of objects
// end result looks like:
// [
// {
// "Name": "Chapter 1: The Worst Birthday",
// "Time": "0:00.000"
// },
// {
// "Name": "Chapter 2: Dobby's Warning",
// "Time": "15:51.000"
// },
// { redacted }
// ]
parsedOverdriveMediaMarkers = parsedOverdriveMediaMarkers
var index = 0
var time = 0.0
// 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 = []
const weirdChapterFilterRegex = /([(]\d|[cC]ontinued)/
includedAudioFiles.forEach((track, track_index) => {
parsedOverdriveMediaMarkers[track_index].forEach((chapter) => {
Logger.debug(`[Book] Attempting regex check for ${chapter.Name}!`)
if (weirdChapterFilterRegex.test(chapter.Name)) {
Logger.debug(`[Book] 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
})
Logger.debug(`[Book] newOChapters: ${JSON.stringify(newOChapters)}`)
return newOChapters
}
setChapters(preferOverdriveMediaMarker = false) {
Logger.debug('[Book] inside setChapters!')
// If 1 audio file without chapters, then no chapters will be set
var includedAudioFiles = this.audioFiles.filter(af => !af.exclude)
if (includedAudioFiles.length === 1) {
@ -407,44 +505,55 @@ class Book {
this.chapters = []
var currChapterId = 0
var currStartTime = 0
includedAudioFiles.forEach((file) => {
// 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 overdriveMediaMarkers = includedAudioFiles.map((af) => af.metaTags.tagOverdriveMediaMarker).filter(notUndefined => notUndefined !== undefined) || []
Logger.debug(`[setChapters] overdriveMediaMarkers: ${JSON.stringify(overdriveMediaMarkers)}`)
// If preferOverdriveMediaMarker is set, try and use that first
if (preferOverdriveMediaMarker) {
Logger.debug(`[Book] preferring overdrive media markers! Lets generate em.`)
this.chapters = this.generateChaptersFromOverdriveMediaMarkers(overdriveMediaMarkers, includedAudioFiles)
} else {
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
}
}
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
}
})
})
} 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
}
})
}
}
}

View file

@ -13,6 +13,7 @@ class ServerSettings {
this.scannerPreferOpfMetadata = false
this.scannerPreferMatchedMetadata = false
this.scannerDisableWatcher = false
this.scannerPreferOverdriveMediaMarker = false
// Metadata - choose to store inside users library item folder
this.storeCoverWithItem = false
@ -65,6 +66,7 @@ class ServerSettings {
this.scannerPreferOpfMetadata = !!settings.scannerPreferOpfMetadata
this.scannerPreferMatchedMetadata = !!settings.scannerPreferMatchedMetadata
this.scannerDisableWatcher = !!settings.scannerDisableWatcher
this.scannerPreferOverdriveMediaMarker = !!settings.scannerPreferOverdriveMediaMarker
this.storeCoverWithItem = !!settings.storeCoverWithItem
if (settings.storeCoverWithBook != undefined) { // storeCoverWithBook was old name of setting < v2
@ -111,6 +113,7 @@ class ServerSettings {
scannerPreferOpfMetadata: this.scannerPreferOpfMetadata,
scannerPreferMatchedMetadata: this.scannerPreferMatchedMetadata,
scannerDisableWatcher: this.scannerDisableWatcher,
scannerPreferOverdriveMediaMarker: this.scannerPreferOverdriveMediaMarker,
storeCoverWithItem: this.storeCoverWithItem,
storeMetadataWithItem: this.storeMetadataWithItem,
rateLimitLoginRequests: this.rateLimitLoginRequests,