mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-02-03 16:59:41 +00:00
Add:Batch re-scan #1754
This commit is contained in:
parent
2fa73f7a8d
commit
b52e240025
11 changed files with 159 additions and 50 deletions
|
|
@ -75,7 +75,7 @@ class Server {
|
|||
this.audioMetadataManager = new AudioMetadataMangaer(this.db, this.taskManager)
|
||||
this.rssFeedManager = new RssFeedManager(this.db)
|
||||
|
||||
this.scanner = new Scanner(this.db, this.coverManager)
|
||||
this.scanner = new Scanner(this.db, this.coverManager, this.taskManager)
|
||||
this.cronManager = new CronManager(this.db, this.scanner, this.podcastManager)
|
||||
|
||||
// Routers
|
||||
|
|
|
|||
|
|
@ -379,20 +379,23 @@ class LibraryItemController {
|
|||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
var itemsUpdated = 0
|
||||
var itemsUnmatched = 0
|
||||
let itemsUpdated = 0
|
||||
let itemsUnmatched = 0
|
||||
|
||||
var matchData = req.body
|
||||
var options = matchData.options || {}
|
||||
var items = matchData.libraryItemIds
|
||||
if (!items || !items.length) {
|
||||
return res.sendStatus(500)
|
||||
const options = req.body.options || {}
|
||||
if (!req.body.libraryItemIds?.length) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
|
||||
const libraryItems = req.body.libraryItemIds.map(lid => this.db.getLibraryItem(lid)).filter(li => li)
|
||||
if (!libraryItems?.length) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
|
||||
res.sendStatus(200)
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
var libraryItem = this.db.libraryItems.find(_li => _li.id === items[i])
|
||||
var matchResult = await this.scanner.quickMatchLibraryItem(libraryItem, options)
|
||||
for (const libraryItem of libraryItems) {
|
||||
const matchResult = await this.scanner.quickMatchLibraryItem(libraryItem, options)
|
||||
if (matchResult.updated) {
|
||||
itemsUpdated++
|
||||
} else if (matchResult.warning) {
|
||||
|
|
@ -400,7 +403,7 @@ class LibraryItemController {
|
|||
}
|
||||
}
|
||||
|
||||
var result = {
|
||||
const result = {
|
||||
success: itemsUpdated > 0,
|
||||
updates: itemsUpdated,
|
||||
unmatched: itemsUnmatched
|
||||
|
|
@ -408,6 +411,33 @@ class LibraryItemController {
|
|||
SocketAuthority.clientEmitter(req.user.id, 'batch_quickmatch_complete', result)
|
||||
}
|
||||
|
||||
// POST: api/items/batch/scan
|
||||
async batchScan(req, res) {
|
||||
if (!req.user.isAdminOrUp) {
|
||||
Logger.warn('User other than admin attempted to batch scan library items', req.user)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
if (!req.body.libraryItemIds?.length) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
|
||||
const libraryItems = req.body.libraryItemIds.map(lid => this.db.getLibraryItem(lid)).filter(li => li)
|
||||
if (!libraryItems?.length) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
|
||||
res.sendStatus(200)
|
||||
|
||||
for (const libraryItem of libraryItems) {
|
||||
if (libraryItem.isFile) {
|
||||
Logger.warn(`[LibraryItemController] Re-scanning file library items not yet supported`)
|
||||
} else {
|
||||
await this.scanner.scanLibraryItemByRequest(libraryItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: api/items/all
|
||||
async deleteAll(req, res) {
|
||||
if (!req.user.isAdminOrUp) {
|
||||
|
|
@ -432,7 +462,7 @@ class LibraryItemController {
|
|||
return res.sendStatus(500)
|
||||
}
|
||||
|
||||
var result = await this.scanner.scanLibraryItemById(req.libraryItem.id)
|
||||
const result = await this.scanner.scanLibraryItemByRequest(req.libraryItem)
|
||||
res.json({
|
||||
result: Object.keys(ScanResult).find(key => ScanResult[key] == result)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class AbMergeManager {
|
|||
toneJsonObject: null
|
||||
}
|
||||
const taskDescription = `Encoding audiobook "${libraryItem.media.metadata.title}" into a single m4b file.`
|
||||
task.setData('encode-m4b', 'Encoding M4b', taskDescription, taskData)
|
||||
task.setData('encode-m4b', 'Encoding M4b', taskDescription, false, taskData)
|
||||
this.taskManager.addTask(task)
|
||||
Logger.info(`Start m4b encode for ${libraryItem.id} - TaskId: ${task.id}`)
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class AudioMetadataMangaer {
|
|||
}
|
||||
}
|
||||
const taskDescription = `Embedding metadata in audiobook "${libraryItem.media.metadata.title}".`
|
||||
task.setData('embed-metadata', 'Embedding Metadata', taskDescription, taskData)
|
||||
task.setData('embed-metadata', 'Embedding Metadata', taskDescription, false, taskData)
|
||||
|
||||
if (this.tasksRunning.length >= this.MAX_CONCURRENT_TASKS) {
|
||||
Logger.info(`[AudioMetadataManager] Queueing embed metadata for audiobook "${libraryItem.media.metadata.title}"`)
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class PodcastManager {
|
|||
libraryId: podcastEpisodeDownload.libraryId,
|
||||
libraryItemId: podcastEpisodeDownload.libraryItemId,
|
||||
}
|
||||
task.setData('download-podcast-episode', 'Downloading Episode', taskDescription, taskData)
|
||||
task.setData('download-podcast-episode', 'Downloading Episode', taskDescription, false, taskData)
|
||||
this.taskManager.addTask(task)
|
||||
|
||||
SocketAuthority.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class Task {
|
|||
this.title = null
|
||||
this.description = null
|
||||
this.error = null
|
||||
this.showSuccess = false // If true client side should keep the task visible after success
|
||||
|
||||
this.isFailed = false
|
||||
this.isFinished = false
|
||||
|
|
@ -25,6 +26,7 @@ class Task {
|
|||
title: this.title,
|
||||
description: this.description,
|
||||
error: this.error,
|
||||
showSuccess: this.showSuccess,
|
||||
isFailed: this.isFailed,
|
||||
isFinished: this.isFinished,
|
||||
startedAt: this.startedAt,
|
||||
|
|
@ -32,12 +34,13 @@ class Task {
|
|||
}
|
||||
}
|
||||
|
||||
setData(action, title, description, data = {}) {
|
||||
setData(action, title, description, showSuccess, data = {}) {
|
||||
this.id = getId(action)
|
||||
this.action = action
|
||||
this.data = { ...data }
|
||||
this.title = title
|
||||
this.description = description
|
||||
this.showSuccess = showSuccess
|
||||
this.startedAt = Date.now()
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +51,10 @@ class Task {
|
|||
this.setFinished()
|
||||
}
|
||||
|
||||
setFinished() {
|
||||
setFinished(newDescription = null) {
|
||||
if (newDescription) {
|
||||
this.description = newDescription
|
||||
}
|
||||
this.isFinished = true
|
||||
this.finishedAt = Date.now()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ class ApiRouter {
|
|||
//
|
||||
// Item Routes
|
||||
//
|
||||
this.router.post('/items/batch/delete', LibraryItemController.batchDelete.bind(this))
|
||||
this.router.post('/items/batch/update', LibraryItemController.batchUpdate.bind(this))
|
||||
this.router.post('/items/batch/get', LibraryItemController.batchGet.bind(this))
|
||||
this.router.post('/items/batch/quickmatch', LibraryItemController.batchQuickMatch.bind(this))
|
||||
this.router.post('/items/batch/scan', LibraryItemController.batchScan.bind(this))
|
||||
this.router.delete('/items/all', LibraryItemController.deleteAll.bind(this))
|
||||
|
||||
this.router.get('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.findOne.bind(this))
|
||||
|
|
@ -117,11 +122,6 @@ class ApiRouter {
|
|||
this.router.post('/items/:id/tone-scan/:index?', LibraryItemController.middleware.bind(this), LibraryItemController.toneScan.bind(this))
|
||||
this.router.delete('/items/:id/file/:ino', LibraryItemController.middleware.bind(this), LibraryItemController.deleteLibraryFile.bind(this))
|
||||
|
||||
this.router.post('/items/batch/delete', LibraryItemController.batchDelete.bind(this))
|
||||
this.router.post('/items/batch/update', LibraryItemController.batchUpdate.bind(this))
|
||||
this.router.post('/items/batch/get', LibraryItemController.batchGet.bind(this))
|
||||
this.router.post('/items/batch/quickmatch', LibraryItemController.batchQuickMatch.bind(this))
|
||||
|
||||
//
|
||||
// User Routes
|
||||
//
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ const ScanOptions = require('./ScanOptions')
|
|||
|
||||
const Author = require('../objects/entities/Author')
|
||||
const Series = require('../objects/entities/Series')
|
||||
const Task = require('../objects/Task')
|
||||
|
||||
class Scanner {
|
||||
constructor(db, coverManager) {
|
||||
constructor(db, coverManager, taskManager) {
|
||||
this.db = db
|
||||
this.coverManager = coverManager
|
||||
this.taskManager = taskManager
|
||||
|
||||
this.cancelLibraryScan = {}
|
||||
this.librariesScanning = []
|
||||
|
|
@ -46,12 +48,24 @@ class Scanner {
|
|||
this.cancelLibraryScan[libraryId] = true
|
||||
}
|
||||
|
||||
async scanLibraryItemById(libraryItemId) {
|
||||
const libraryItem = this.db.libraryItems.find(li => li.id === libraryItemId)
|
||||
if (!libraryItem) {
|
||||
Logger.error(`[Scanner] Scan libraryItem by id not found ${libraryItemId}`)
|
||||
return ScanResult.NOTHING
|
||||
getScanResultDescription(result) {
|
||||
switch (result) {
|
||||
case ScanResult.ADDED:
|
||||
return 'Added to library'
|
||||
case ScanResult.NOTHING:
|
||||
return 'No updates necessary'
|
||||
case ScanResult.REMOVED:
|
||||
return 'Removed from library'
|
||||
case ScanResult.UPDATED:
|
||||
return 'Item was updated'
|
||||
case ScanResult.UPTODATE:
|
||||
return 'No updates necessary'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
async scanLibraryItemByRequest(libraryItem) {
|
||||
const library = this.db.libraries.find(lib => lib.id === libraryItem.libraryId)
|
||||
if (!library) {
|
||||
Logger.error(`[Scanner] Scan libraryItem by id library not found "${libraryItem.libraryId}"`)
|
||||
|
|
@ -63,7 +77,21 @@ class Scanner {
|
|||
return ScanResult.NOTHING
|
||||
}
|
||||
Logger.info(`[Scanner] Scanning Library Item "${libraryItem.media.metadata.title}"`)
|
||||
return this.scanLibraryItem(library.mediaType, folder, libraryItem)
|
||||
|
||||
const task = new Task()
|
||||
task.setData('scan-item', `Scan ${libraryItem.media.metadata.title}`, '', true, {
|
||||
libraryItemId: libraryItem.id,
|
||||
libraryId: library.id,
|
||||
mediaType: library.mediaType
|
||||
})
|
||||
this.taskManager.addTask(task)
|
||||
|
||||
const result = await this.scanLibraryItem(library.mediaType, folder, libraryItem)
|
||||
|
||||
task.setFinished(this.getScanResultDescription(result))
|
||||
this.taskManager.taskFinished(task)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async scanLibraryItem(libraryMediaType, folder, libraryItem) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue