mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-07-24 12:54:31 +02:00
New data model save covers, scanner, new api routes
This commit is contained in:
parent
5f4e5cd3d8
commit
73257188f6
37 changed files with 1649 additions and 672 deletions
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue