mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-08-08 23:04:51 +02:00
Add:Audiobooks only library settings, supplementary ebooks #1664
This commit is contained in:
@ -4,7 +4,7 @@ const Logger = require('../Logger')
|
||||
const SocketAuthority = require('../SocketAuthority')
|
||||
|
||||
// Utils
|
||||
const { groupFilesIntoLibraryItemPaths, getLibraryItemFileData, scanFolder } = require('../utils/scandir')
|
||||
const { groupFilesIntoLibraryItemPaths, getLibraryItemFileData, scanFolder, checkFilepathIsAudioFile } = require('../utils/scandir')
|
||||
const { comparePaths } = require('../utils/index')
|
||||
const { getIno, filePathToPOSIX } = require('../utils/fileUtils')
|
||||
const { ScanResult, LogLevel } = require('../utils/constants')
|
||||
@ -86,7 +86,7 @@ class Scanner {
|
||||
})
|
||||
this.taskManager.addTask(task)
|
||||
|
||||
const result = await this.scanLibraryItem(library.mediaType, folder, libraryItem)
|
||||
const result = await this.scanLibraryItem(library, folder, libraryItem)
|
||||
|
||||
task.setFinished(this.getScanResultDescription(result))
|
||||
this.taskManager.taskFinished(task)
|
||||
@ -94,7 +94,9 @@ class Scanner {
|
||||
return result
|
||||
}
|
||||
|
||||
async scanLibraryItem(libraryMediaType, folder, libraryItem) {
|
||||
async scanLibraryItem(library, folder, libraryItem) {
|
||||
const libraryMediaType = library.mediaType
|
||||
|
||||
// TODO: Support for single media item
|
||||
const libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, libraryItem.path, false)
|
||||
if (!libraryItemData) {
|
||||
@ -106,7 +108,7 @@ class Scanner {
|
||||
if (checkRes.updated) hasUpdated = true
|
||||
|
||||
// Sync other files first so that local images are used as cover art
|
||||
if (await libraryItem.syncFiles(this.db.serverSettings.scannerPreferOpfMetadata)) {
|
||||
if (await libraryItem.syncFiles(this.db.serverSettings.scannerPreferOpfMetadata, library.settings)) {
|
||||
hasUpdated = true
|
||||
}
|
||||
|
||||
@ -157,10 +159,10 @@ class Scanner {
|
||||
return
|
||||
}
|
||||
|
||||
var scanOptions = new ScanOptions()
|
||||
const scanOptions = new ScanOptions()
|
||||
scanOptions.setData(options, this.db.serverSettings)
|
||||
|
||||
var libraryScan = new LibraryScan()
|
||||
const libraryScan = new LibraryScan()
|
||||
libraryScan.setData(library, scanOptions)
|
||||
libraryScan.verbose = false
|
||||
this.librariesScanning.push(libraryScan.getScanEmitData)
|
||||
@ -169,7 +171,7 @@ class Scanner {
|
||||
|
||||
Logger.info(`[Scanner] Starting library scan ${libraryScan.id} for ${libraryScan.libraryName}`)
|
||||
|
||||
var canceled = await this.scanLibrary(libraryScan)
|
||||
const canceled = await this.scanLibrary(libraryScan)
|
||||
|
||||
if (canceled) {
|
||||
Logger.info(`[Scanner] Library scan canceled for "${libraryScan.libraryName}"`)
|
||||
@ -182,7 +184,7 @@ class Scanner {
|
||||
this.librariesScanning = this.librariesScanning.filter(ls => ls.id !== library.id)
|
||||
|
||||
if (canceled && !libraryScan.totalResults) {
|
||||
var emitData = libraryScan.getScanEmitData
|
||||
const emitData = libraryScan.getScanEmitData
|
||||
emitData.results = null
|
||||
SocketAuthority.emitter('scan_complete', emitData)
|
||||
return
|
||||
@ -201,7 +203,7 @@ class Scanner {
|
||||
// Scan each library
|
||||
for (let i = 0; i < libraryScan.folders.length; i++) {
|
||||
const folder = libraryScan.folders[i]
|
||||
const itemDataFoundInFolder = await scanFolder(libraryScan.libraryMediaType, folder)
|
||||
const itemDataFoundInFolder = await scanFolder(libraryScan.library, folder)
|
||||
libraryScan.addLog(LogLevel.INFO, `${itemDataFoundInFolder.length} item data found in folder "${folder.fullPath}"`)
|
||||
libraryItemDataFound = libraryItemDataFound.concat(itemDataFoundInFolder)
|
||||
}
|
||||
@ -356,7 +358,7 @@ class Scanner {
|
||||
|
||||
async scanNewLibraryItemDataChunk(newLibraryItemsData, libraryScan) {
|
||||
let newLibraryItems = await Promise.all(newLibraryItemsData.map((lid) => {
|
||||
return this.scanNewLibraryItem(lid, libraryScan.libraryMediaType, libraryScan)
|
||||
return this.scanNewLibraryItem(lid, libraryScan.library, libraryScan)
|
||||
}))
|
||||
newLibraryItems = newLibraryItems.filter(li => li) // Filter out nulls
|
||||
|
||||
@ -376,7 +378,7 @@ class Scanner {
|
||||
let hasUpdated = updated
|
||||
|
||||
// Sync other files first to use local images as cover before extracting audio file cover
|
||||
if (await libraryItem.syncFiles(libraryScan.preferOpfMetadata)) {
|
||||
if (await libraryItem.syncFiles(libraryScan.preferOpfMetadata, libraryScan.library.settings)) {
|
||||
hasUpdated = true
|
||||
}
|
||||
|
||||
@ -425,7 +427,7 @@ class Scanner {
|
||||
return hasUpdated ? libraryItem : null
|
||||
}
|
||||
|
||||
async scanNewLibraryItem(libraryItemData, libraryMediaType, libraryScan = null) {
|
||||
async scanNewLibraryItem(libraryItemData, library, libraryScan = null) {
|
||||
if (libraryScan) libraryScan.addLog(LogLevel.DEBUG, `Scanning new library item "${libraryItemData.path}"`)
|
||||
else Logger.debug(`[Scanner] Scanning new item "${libraryItemData.path}"`)
|
||||
|
||||
@ -433,14 +435,14 @@ class Scanner {
|
||||
const findCovers = libraryScan ? !!libraryScan.findCovers : !!global.ServerSettings.scannerFindCovers
|
||||
|
||||
const libraryItem = new LibraryItem()
|
||||
libraryItem.setData(libraryMediaType, libraryItemData)
|
||||
libraryItem.setData(library.mediaType, libraryItemData)
|
||||
|
||||
const mediaFiles = libraryItemData.libraryFiles.filter(lf => lf.fileType === 'audio' || lf.fileType === 'video')
|
||||
if (mediaFiles.length) {
|
||||
await MediaFileScanner.scanMediaFiles(mediaFiles, libraryItem, libraryScan)
|
||||
}
|
||||
|
||||
await libraryItem.syncFiles(preferOpfMetadata)
|
||||
await libraryItem.syncFiles(preferOpfMetadata, library.settings)
|
||||
|
||||
if (!libraryItem.hasMediaEntities) {
|
||||
Logger.warn(`[Scanner] Library item has no media files "${libraryItemData.path}"`)
|
||||
@ -457,7 +459,7 @@ class Scanner {
|
||||
}
|
||||
|
||||
// Scan for cover if enabled and has no cover
|
||||
if (libraryMediaType === 'book') {
|
||||
if (library.isBook) {
|
||||
if (libraryItem && findCovers && !libraryItem.media.coverPath && libraryItem.media.shouldSearchForCover) {
|
||||
const updatedCover = await this.searchForCover(libraryItem, libraryScan)
|
||||
libraryItem.media.updateLastCoverSearch(updatedCover)
|
||||
@ -534,7 +536,7 @@ class Scanner {
|
||||
}
|
||||
|
||||
async scanFilesChanged(fileUpdates) {
|
||||
if (!fileUpdates || !fileUpdates.length) return
|
||||
if (!fileUpdates?.length) return
|
||||
|
||||
// If already scanning files from watcher then add these updates to queue
|
||||
if (this.scanningFilesChanged) {
|
||||
@ -545,28 +547,28 @@ class Scanner {
|
||||
this.scanningFilesChanged = true
|
||||
|
||||
// files grouped by folder
|
||||
var folderGroups = this.getFileUpdatesGrouped(fileUpdates)
|
||||
const folderGroups = this.getFileUpdatesGrouped(fileUpdates)
|
||||
|
||||
for (const folderId in folderGroups) {
|
||||
var libraryId = folderGroups[folderId].libraryId
|
||||
var library = this.db.libraries.find(lib => lib.id === libraryId)
|
||||
const libraryId = folderGroups[folderId].libraryId
|
||||
const library = this.db.libraries.find(lib => lib.id === libraryId)
|
||||
if (!library) {
|
||||
Logger.error(`[Scanner] Library not found in files changed ${libraryId}`)
|
||||
continue;
|
||||
}
|
||||
var folder = library.getFolderById(folderId)
|
||||
const folder = library.getFolderById(folderId)
|
||||
if (!folder) {
|
||||
Logger.error(`[Scanner] Folder is not in library in files changed "${folderId}", Library "${library.name}"`)
|
||||
continue;
|
||||
}
|
||||
var relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath)
|
||||
var fileUpdateGroup = groupFilesIntoLibraryItemPaths(library.mediaType, relFilePaths)
|
||||
const relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath)
|
||||
const fileUpdateGroup = groupFilesIntoLibraryItemPaths(library.mediaType, relFilePaths, false)
|
||||
|
||||
if (!Object.keys(fileUpdateGroup).length) {
|
||||
Logger.info(`[Scanner] No important changes to scan for in folder "${folderId}"`)
|
||||
continue;
|
||||
}
|
||||
var folderScanResults = await this.scanFolderUpdates(library, folder, fileUpdateGroup)
|
||||
const folderScanResults = await this.scanFolderUpdates(library, folder, fileUpdateGroup)
|
||||
Logger.debug(`[Scanner] Folder scan results`, folderScanResults)
|
||||
}
|
||||
|
||||
@ -584,25 +586,25 @@ class Scanner {
|
||||
|
||||
// First pass - Remove files in parent dirs of items and remap the fileupdate group
|
||||
// Test Case: Moving audio files from library item folder to author folder should trigger a re-scan of the item
|
||||
var updateGroup = { ...fileUpdateGroup }
|
||||
const updateGroup = { ...fileUpdateGroup }
|
||||
for (const itemDir in updateGroup) {
|
||||
if (itemDir == fileUpdateGroup[itemDir]) continue; // Media in root path
|
||||
|
||||
var itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/'))
|
||||
const itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/'))
|
||||
if (!itemDirNestedFiles.length) continue;
|
||||
|
||||
var firstNest = itemDirNestedFiles[0].split('/').shift()
|
||||
var altDir = `${itemDir}/${firstNest}`
|
||||
const firstNest = itemDirNestedFiles[0].split('/').shift()
|
||||
const altDir = `${itemDir}/${firstNest}`
|
||||
|
||||
var fullPath = Path.posix.join(filePathToPOSIX(folder.fullPath), itemDir)
|
||||
var childLibraryItem = this.db.libraryItems.find(li => li.path !== fullPath && li.path.startsWith(fullPath))
|
||||
const fullPath = Path.posix.join(filePathToPOSIX(folder.fullPath), itemDir)
|
||||
const childLibraryItem = this.db.libraryItems.find(li => li.path !== fullPath && li.path.startsWith(fullPath))
|
||||
if (!childLibraryItem) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
var altFullPath = Path.posix.join(filePathToPOSIX(folder.fullPath), altDir)
|
||||
var altChildLibraryItem = this.db.libraryItems.find(li => li.path !== altFullPath && li.path.startsWith(altFullPath))
|
||||
const altFullPath = Path.posix.join(filePathToPOSIX(folder.fullPath), altDir)
|
||||
const altChildLibraryItem = this.db.libraryItems.find(li => li.path !== altFullPath && li.path.startsWith(altFullPath))
|
||||
if (altChildLibraryItem) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
delete fileUpdateGroup[itemDir]
|
||||
@ -638,14 +640,17 @@ class Scanner {
|
||||
SocketAuthority.emitter('item_updated', existingLibraryItem.toJSONExpanded())
|
||||
|
||||
itemGroupingResults[itemDir] = ScanResult.REMOVED
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Scan library item for updates
|
||||
Logger.debug(`[Scanner] Folder update for relative path "${itemDir}" is in library item "${existingLibraryItem.media.metadata.title}" - scan for updates`)
|
||||
itemGroupingResults[itemDir] = await this.scanLibraryItem(library.mediaType, folder, existingLibraryItem)
|
||||
continue;
|
||||
itemGroupingResults[itemDir] = await this.scanLibraryItem(library, folder, existingLibraryItem)
|
||||
continue
|
||||
} else if (library.settings.audiobooksOnly && !fileUpdateGroup[itemDir].some(checkFilepathIsAudioFile)) {
|
||||
Logger.debug(`[Scanner] Folder update for relative path "${itemDir}" has no audio files`)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if a library item is a subdirectory of this dir
|
||||
@ -653,12 +658,12 @@ class Scanner {
|
||||
if (childItem) {
|
||||
Logger.warn(`[Scanner] Files were modified in a parent directory of a library item "${childItem.media.metadata.title}" - ignoring`)
|
||||
itemGroupingResults[itemDir] = ScanResult.NOTHING
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
Logger.debug(`[Scanner] Folder update group must be a new item "${itemDir}" in library "${library.name}"`)
|
||||
var isSingleMediaItem = itemDir === fileUpdateGroup[itemDir]
|
||||
var newLibraryItem = await this.scanPotentialNewLibraryItem(library.mediaType, folder, fullPath, isSingleMediaItem)
|
||||
var newLibraryItem = await this.scanPotentialNewLibraryItem(library, folder, fullPath, isSingleMediaItem)
|
||||
if (newLibraryItem) {
|
||||
await this.createNewAuthorsAndSeries(newLibraryItem)
|
||||
await this.db.insertLibraryItem(newLibraryItem)
|
||||
@ -670,10 +675,10 @@ class Scanner {
|
||||
return itemGroupingResults
|
||||
}
|
||||
|
||||
async scanPotentialNewLibraryItem(libraryMediaType, folder, fullPath, isSingleMediaItem = false) {
|
||||
const libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, fullPath, isSingleMediaItem)
|
||||
async scanPotentialNewLibraryItem(library, folder, fullPath, isSingleMediaItem = false) {
|
||||
const libraryItemData = await getLibraryItemFileData(library.mediaType, folder, fullPath, isSingleMediaItem)
|
||||
if (!libraryItemData) return null
|
||||
return this.scanNewLibraryItem(libraryItemData, libraryMediaType)
|
||||
return this.scanNewLibraryItem(libraryItemData, library)
|
||||
}
|
||||
|
||||
async searchForCover(libraryItem, libraryScan = null) {
|
||||
|
Reference in New Issue
Block a user