New data model save covers, scanner, new api routes

This commit is contained in:
advplyr 2022-03-12 17:45:32 -06:00
parent 5f4e5cd3d8
commit 73257188f6
37 changed files with 1649 additions and 672 deletions

View file

@ -9,9 +9,9 @@ const { LogLevel } = require('../utils/constants')
class AudioFileScanner {
constructor() { }
getTrackAndDiscNumberFromFilename(bookScanData, audioFileData) {
const { title, author, series, publishYear } = bookScanData
const { filename, path } = audioFileData
getTrackAndDiscNumberFromFilename(mediaMetadataFromScan, audioLibraryFile) {
const { title, author, series, publishYear } = mediaMetadataFromScan
const { filename, path } = audioLibraryFile.metadata
var partbasename = Path.basename(filename, Path.extname(filename))
// Remove title, author, series, and publishYear from filename if there
@ -54,25 +54,23 @@ class AudioFileScanner {
return Math.floor(total / results.length)
}
async scan(audioFileData, bookScanData, verbose = false) {
async scan(audioLibraryFile, mediaMetadataFromScan, verbose = false) {
var probeStart = Date.now()
// Logger.debug(`[AudioFileScanner] Start Probe ${audioFileData.fullPath}`)
var probeData = await prober.probe(audioFileData.fullPath, verbose)
var probeData = await prober.probe(audioLibraryFile.metadata.path, verbose)
if (probeData.error) {
Logger.error(`[AudioFileScanner] ${probeData.error} : "${audioFileData.fullPath}"`)
Logger.error(`[AudioFileScanner] ${probeData.error} : "${audioLibraryFile.metadata.path}"`)
return null
}
// Logger.debug(`[AudioFileScanner] Finished Probe ${audioFileData.fullPath} elapsed ${msToTimestamp(Date.now() - probeStart, true)}`)
var audioFile = new AudioFile()
audioFileData.trackNumFromMeta = probeData.trackNumber
audioFileData.discNumFromMeta = probeData.discNumber
audioFile.trackNumFromMeta = probeData.trackNumber
audioFile.discNumFromMeta = probeData.discNumber
const { trackNumber, discNumber } = this.getTrackAndDiscNumberFromFilename(bookScanData, audioFileData)
audioFileData.trackNumFromFilename = trackNumber
audioFileData.discNumFromFilename = discNumber
const { trackNumber, discNumber } = this.getTrackAndDiscNumberFromFilename(mediaMetadataFromScan, audioLibraryFile)
audioFile.trackNumFromFilename = trackNumber
audioFile.discNumFromFilename = discNumber
audioFile.setDataFromProbe(audioFileData, probeData)
audioFile.setDataFromProbe(audioLibraryFile, probeData)
return {
audioFile,
@ -81,10 +79,11 @@ class AudioFileScanner {
}
// Returns array of { AudioFile, elapsed, averageScanDuration } from audio file scan objects
async executeAudioFileScans(audioFileDataArray, bookScanData) {
async executeAudioFileScans(audioLibraryFiles, scanData) {
var mediaMetadataFromScan = scanData.mediaMetadata || null
var proms = []
for (let i = 0; i < audioFileDataArray.length; i++) {
proms.push(this.scan(audioFileDataArray[i], bookScanData))
for (let i = 0; i < audioLibraryFiles.length; i++) {
proms.push(this.scan(audioLibraryFiles[i], mediaMetadataFromScan))
}
var scanStart = Date.now()
var results = await Promise.all(proms).then((scanResults) => scanResults.filter(sr => sr))
@ -117,7 +116,7 @@ class AudioFileScanner {
return nodupes
}
runSmartTrackOrder(audiobook, audioFiles) {
runSmartTrackOrder(libraryItem, audioFiles) {
var discsFromFilename = []
var tracksFromFilename = []
var discsFromMeta = []
@ -153,75 +152,78 @@ class AudioFileScanner {
if (discKey !== null) {
Logger.debug(`[AudioFileScanner] Smart track order for "${audiobook.title}" using disc key ${discKey} and track key ${trackKey}`)
Logger.debug(`[AudioFileScanner] Smart track order for "${libraryItem.media.metadata.title}" using disc key ${discKey} and track key ${trackKey}`)
audioFiles.sort((a, b) => {
let Dx = a[discKey] - b[discKey]
if (Dx === 0) Dx = a[trackKey] - b[trackKey]
return Dx
})
} else {
Logger.debug(`[AudioFileScanner] Smart track order for "${audiobook.title}" using track key ${trackKey}`)
Logger.debug(`[AudioFileScanner] Smart track order for "${libraryItem.media.metadata.title}" using track key ${trackKey}`)
audioFiles.sort((a, b) => a[trackKey] - b[trackKey])
}
for (let i = 0; i < audioFiles.length; i++) {
audioFiles[i].index = i + 1
var existingAF = audiobook.getAudioFileByIno(audioFiles[i].ino)
var existingAF = libraryItem.media.findFileWithInode(audioFiles[i].ino)
if (existingAF) {
audiobook.updateAudioFile(audioFiles[i])
if (existingAF.updateFromScan) existingAF.updateFromScan(audioFiles[i])
} else {
audiobook.addAudioFile(audioFiles[i])
libraryItem.media.audioFiles.push(audioFiles[i])
}
}
}
async scanAudioFiles(audioFileDataArray, bookScanData, audiobook, preferAudioMetadata, libraryScan = null) {
async scanAudioFiles(audioLibraryFiles, scanData, libraryItem, preferAudioMetadata, libraryScan = null) {
var hasUpdated = false
var audioScanResult = await this.executeAudioFileScans(audioFileDataArray, bookScanData)
var audioScanResult = await this.executeAudioFileScans(audioLibraryFiles, scanData)
if (audioScanResult.audioFiles.length) {
if (libraryScan) {
libraryScan.addLog(LogLevel.DEBUG, `Book "${bookScanData.path}" Audio file scan took ${audioScanResult.elapsed}ms for ${audioScanResult.audioFiles.length} with average time of ${audioScanResult.averageScanDuration}ms`)
libraryScan.addLog(LogLevel.DEBUG, `Library Item "${scanData.path}" Audio file scan took ${audioScanResult.elapsed}ms for ${audioScanResult.audioFiles.length} with average time of ${audioScanResult.averageScanDuration}ms`)
}
var totalAudioFilesToInclude = audioScanResult.audioFiles.length
var newAudioFiles = audioScanResult.audioFiles.filter(af => {
return !audiobook.audioFilesToInclude.find(_af => _af.ino === af.ino)
return !libraryItem.libraryFiles.find(lf => lf.ino === af.ino)
})
if (newAudioFiles.length) {
// Single Track Audiobooks
if (totalAudioFilesToInclude === 1) {
var af = audioScanResult.audioFiles[0]
af.index = 1
audiobook.addAudioFile(af)
hasUpdated = true
// Adding audio files to book media
if (libraryItem.mediaType === 'book') {
if (newAudioFiles.length) {
// Single Track Audiobooks
if (totalAudioFilesToInclude === 1) {
var af = audioScanResult.audioFiles[0]
af.index = 1
libraryItem.media.audioFiles.push(af)
hasUpdated = true
} else {
this.runSmartTrackOrder(libraryItem, audioScanResult.audioFiles)
hasUpdated = true
}
} else {
this.runSmartTrackOrder(audiobook, audioScanResult.audioFiles)
Logger.debug(`[AudioFileScanner] No audio track re-order required`)
// Only update metadata not index
audioScanResult.audioFiles.forEach((af) => {
var existingAF = libraryItem.media.findFileWithInode(af.ino)
if (existingAF) {
af.index = existingAF.index
if (existingAF.updateFromScan && existingAF.updateFromScan(af)) {
hasUpdated = true
}
}
})
}
// Set book details from audio file ID3 tags, optional prefer
if (libraryItem.media.setMetadataFromAudioFile(preferAudioMetadata)) {
hasUpdated = true
}
} else {
Logger.debug(`[AudioFileScanner] No audio track re-order required`)
// Only update metadata not index
audioScanResult.audioFiles.forEach((af) => {
var existingAF = audiobook.getAudioFileByIno(af.ino)
if (existingAF) {
af.index = existingAF.index
if (audiobook.updateAudioFile(af)) {
hasUpdated = true
}
}
})
}
// Set book details from audio file ID3 tags, optional prefer
if (audiobook.setDetailsFromFileMetadata(preferAudioMetadata)) {
hasUpdated = true
}
if (hasUpdated) {
audiobook.rebuildTracks()
}
if (hasUpdated) {
libraryItem.media.rebuildTracks()
}
} // End Book media type
}
return hasUpdated
}