mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-11 08:18:17 +02:00
Add:Batch re-scan #1754
This commit is contained in:
parent
2fa73f7a8d
commit
b52e240025
@ -15,8 +15,6 @@
|
|||||||
<controls-global-search v-if="currentLibrary" class="mr-1 sm:mr-0" />
|
<controls-global-search v-if="currentLibrary" class="mr-1 sm:mr-0" />
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
|
|
||||||
<widgets-notification-widget class="hidden md:block" />
|
|
||||||
|
|
||||||
<ui-tooltip v-if="isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center">
|
<ui-tooltip v-if="isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center">
|
||||||
<span class="material-icons-outlined text-2xl text-warning text-opacity-50"> cast </span>
|
<span class="material-icons-outlined text-2xl text-warning text-opacity-50"> cast </span>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
@ -24,6 +22,8 @@
|
|||||||
<google-cast-launcher></google-cast-launcher>
|
<google-cast-launcher></google-cast-launcher>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<widgets-notification-widget class="hidden md:block" />
|
||||||
|
|
||||||
<nuxt-link v-if="currentLibrary" to="/config/stats" class="hover:text-gray-200 cursor-pointer w-8 h-8 hidden sm:flex items-center justify-center mx-1">
|
<nuxt-link v-if="currentLibrary" to="/config/stats" class="hover:text-gray-200 cursor-pointer w-8 h-8 hidden sm:flex items-center justify-center mx-1">
|
||||||
<ui-tooltip :text="$strings.HeaderYourStats" direction="bottom" class="flex items-center">
|
<ui-tooltip :text="$strings.HeaderYourStats" direction="bottom" class="flex items-center">
|
||||||
<span class="material-icons text-2xl" aria-label="User Stats" role="button">equalizer</span>
|
<span class="material-icons text-2xl" aria-label="User Stats" role="button">equalizer</span>
|
||||||
@ -178,6 +178,11 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.push({
|
||||||
|
text: 'Re-Scan',
|
||||||
|
action: 'rescan'
|
||||||
|
})
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -211,8 +216,34 @@ export default {
|
|||||||
this.requestBatchQuickEmbed()
|
this.requestBatchQuickEmbed()
|
||||||
} else if (action === 'quick-match') {
|
} else if (action === 'quick-match') {
|
||||||
this.batchAutoMatchClick()
|
this.batchAutoMatchClick()
|
||||||
|
} else if (action === 'rescan') {
|
||||||
|
this.batchRescan()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async batchRescan() {
|
||||||
|
const payload = {
|
||||||
|
message: `Are you sure you want to re-scan ${this.selectedMediaItems.length} items?`,
|
||||||
|
callback: (confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/items/batch/scan`, {
|
||||||
|
libraryItemIds: this.selectedMediaItems.map((i) => i.id)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log('Batch Re-Scan started')
|
||||||
|
this.cancelSelectionMode()
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Batch Re-Scan failed', error)
|
||||||
|
const errorMsg = error.response.data || 'Failed to batch re-scan'
|
||||||
|
this.$toast.error(errorMsg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'yesNo'
|
||||||
|
}
|
||||||
|
this.$store.commit('globals/setConfirmPrompt', payload)
|
||||||
|
},
|
||||||
async playSelectedItems() {
|
async playSelectedItems() {
|
||||||
this.$store.commit('setProcessingBatch', true)
|
this.$store.commit('setProcessingBatch', true)
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center h-full px-1 overflow-hidden">
|
<div class="flex items-center px-1 overflow-hidden">
|
||||||
<div class="h-5 w-5 min-w-5 text-lg mr-1.5 flex items-center justify-center">
|
<div class="w-8 flex items-center justify-center">
|
||||||
|
<!-- <div class="text-lg"> -->
|
||||||
<span v-if="isFinished" :class="taskIconStatus" class="material-icons text-base">{{ actionIcon }}</span>
|
<span v-if="isFinished" :class="taskIconStatus" class="material-icons text-base">{{ actionIcon }}</span>
|
||||||
<widgets-loading-spinner v-else />
|
<widgets-loading-spinner v-else />
|
||||||
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow px-2 taskRunningCardContent">
|
<div class="flex-grow px-2 taskRunningCardContent">
|
||||||
<p class="truncate text-sm">{{ title }}</p>
|
<p class="truncate text-sm">{{ title }}</p>
|
||||||
@ -36,10 +38,13 @@ export default {
|
|||||||
return this.task.details || 'Unknown'
|
return this.task.details || 'Unknown'
|
||||||
},
|
},
|
||||||
isFinished() {
|
isFinished() {
|
||||||
return this.task.isFinished || false
|
return !!this.task.isFinished
|
||||||
},
|
},
|
||||||
isFailed() {
|
isFailed() {
|
||||||
return this.task.isFailed || false
|
return !!this.task.isFailed
|
||||||
|
},
|
||||||
|
isSuccess() {
|
||||||
|
return this.isFinished && !this.isFailed
|
||||||
},
|
},
|
||||||
failedMessage() {
|
failedMessage() {
|
||||||
return this.task.error || ''
|
return this.task.error || ''
|
||||||
@ -48,6 +53,11 @@ export default {
|
|||||||
return this.task.action || ''
|
return this.task.action || ''
|
||||||
},
|
},
|
||||||
actionIcon() {
|
actionIcon() {
|
||||||
|
if (this.isFailed) {
|
||||||
|
return 'error'
|
||||||
|
} else if (this.isSuccess) {
|
||||||
|
return 'done'
|
||||||
|
}
|
||||||
switch (this.action) {
|
switch (this.action) {
|
||||||
case 'download-podcast-episode':
|
case 'download-podcast-episode':
|
||||||
return 'cloud_download'
|
return 'cloud_download'
|
||||||
@ -68,16 +78,15 @@ export default {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {},
|
||||||
},
|
|
||||||
mounted() {}
|
mounted() {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.taskRunningCardContent {
|
.taskRunningCardContent {
|
||||||
width: calc(100% - 80px);
|
width: calc(100% - 84px);
|
||||||
height: 75px;
|
height: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="tasksRunning" class="w-4 h-4 mx-3 relative" v-click-outside="clickOutsideObj">
|
<div v-if="tasksToShow.length" class="w-4 h-4 mx-3 relative" v-click-outside="clickOutsideObj">
|
||||||
<button type="button" :disabled="disabled" class="w-10 sm:w-full relative h-full cursor-pointer" aria-haspopup="listbox" :aria-expanded="showMenu" @click.stop.prevent="clickShowMenu">
|
<button type="button" :disabled="disabled" class="w-10 sm:w-full relative h-full cursor-pointer" aria-haspopup="listbox" :aria-expanded="showMenu" @click.stop.prevent="clickShowMenu">
|
||||||
<div class="flex h-full items-center justify-center">
|
<div class="flex h-full items-center justify-center">
|
||||||
<ui-tooltip text="Tasks running" direction="bottom" class="flex items-center">
|
<ui-tooltip v-if="tasksRunning" :text="$strings.LabelTasks" direction="bottom" class="flex items-center">
|
||||||
<widgets-loading-spinner />
|
<widgets-loading-spinner />
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
<ui-tooltip v-else text="Activities" direction="bottom" class="flex items-center">
|
||||||
|
<span class="material-icons text-1.5xl" aria-label="Activities" role="button">notifications</span>
|
||||||
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<transition name="menu">
|
<transition name="menu">
|
||||||
<div class="sm:w-80 w-full relative">
|
<div class="sm:w-80 w-full relative">
|
||||||
<div v-show="showMenu" class="absolute z-40 -mt-px w-40 sm:w-full bg-bg border border-black-200 shadow-lg rounded-md py-1 px-2 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm globalTaskRunningMenu">
|
<div v-show="showMenu" class="absolute z-40 -mt-px w-40 sm:w-full bg-bg border border-black-200 shadow-lg rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm globalTaskRunningMenu">
|
||||||
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
|
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
|
||||||
<template v-if="tasksRunningOrFailed.length">
|
<template v-if="tasksToShow.length">
|
||||||
<p class="uppercase text-xs text-gray-400 my-1 px-1 font-semibold">{{ $strings.LabelTasks }}</p>
|
<template v-for="task in tasksToShow">
|
||||||
<template v-for="task in tasksRunningOrFailed">
|
|
||||||
<nuxt-link :key="task.id" v-if="actionLink(task)" :to="actionLink(task)">
|
<nuxt-link :key="task.id" v-if="actionLink(task)" :to="actionLink(task)">
|
||||||
<li class="text-gray-50 select-none relative hover:bg-black-400 py-1 cursor-pointer">
|
<li class="text-gray-50 select-none relative hover:bg-black-400 py-1 cursor-pointer">
|
||||||
<cards-item-task-running-card :task="task" />
|
<cards-item-task-running-card :task="task" />
|
||||||
@ -54,9 +56,10 @@ export default {
|
|||||||
tasksRunning() {
|
tasksRunning() {
|
||||||
return this.tasks.some((t) => !t.isFinished)
|
return this.tasks.some((t) => !t.isFinished)
|
||||||
},
|
},
|
||||||
tasksRunningOrFailed() {
|
tasksToShow() {
|
||||||
// return just the tasks that are running or failed in the last 1 minute
|
// return just the tasks that are running or failed (or show success) in the last 1 minute
|
||||||
return this.tasks.filter((t) => !t.isFinished || (t.isFailed && t.finishedAt > new Date().getTime() - 1000 * 60)) || []
|
const tasks = this.tasks.filter((t) => !t.isFinished || ((t.isFailed || t.showSuccess) && t.finishedAt > new Date().getTime() - 1000 * 60)) || []
|
||||||
|
return tasks.sort((a, b) => b.startedAt - a.startedAt)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -75,6 +78,8 @@ export default {
|
|||||||
return `/audiobook/${task.data.libraryItemId}/manage?tool=m4b`
|
return `/audiobook/${task.data.libraryItemId}/manage?tool=m4b`
|
||||||
case 'embed-metadata':
|
case 'embed-metadata':
|
||||||
return `/audiobook/${task.data.libraryItemId}/manage?tool=embed`
|
return `/audiobook/${task.data.libraryItemId}/manage?tool=embed`
|
||||||
|
case 'scan-item':
|
||||||
|
return `/item/${task.data.libraryItemId}`
|
||||||
default:
|
default:
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ class Server {
|
|||||||
this.audioMetadataManager = new AudioMetadataMangaer(this.db, this.taskManager)
|
this.audioMetadataManager = new AudioMetadataMangaer(this.db, this.taskManager)
|
||||||
this.rssFeedManager = new RssFeedManager(this.db)
|
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)
|
this.cronManager = new CronManager(this.db, this.scanner, this.podcastManager)
|
||||||
|
|
||||||
// Routers
|
// Routers
|
||||||
|
@ -379,20 +379,23 @@ class LibraryItemController {
|
|||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemsUpdated = 0
|
let itemsUpdated = 0
|
||||||
var itemsUnmatched = 0
|
let itemsUnmatched = 0
|
||||||
|
|
||||||
var matchData = req.body
|
const options = req.body.options || {}
|
||||||
var options = matchData.options || {}
|
if (!req.body.libraryItemIds?.length) {
|
||||||
var items = matchData.libraryItemIds
|
return res.sendStatus(400)
|
||||||
if (!items || !items.length) {
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const libraryItems = req.body.libraryItemIds.map(lid => this.db.getLibraryItem(lid)).filter(li => li)
|
||||||
|
if (!libraryItems?.length) {
|
||||||
|
return res.sendStatus(400)
|
||||||
|
}
|
||||||
|
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (const libraryItem of libraryItems) {
|
||||||
var libraryItem = this.db.libraryItems.find(_li => _li.id === items[i])
|
const matchResult = await this.scanner.quickMatchLibraryItem(libraryItem, options)
|
||||||
var matchResult = await this.scanner.quickMatchLibraryItem(libraryItem, options)
|
|
||||||
if (matchResult.updated) {
|
if (matchResult.updated) {
|
||||||
itemsUpdated++
|
itemsUpdated++
|
||||||
} else if (matchResult.warning) {
|
} else if (matchResult.warning) {
|
||||||
@ -400,7 +403,7 @@ class LibraryItemController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = {
|
const result = {
|
||||||
success: itemsUpdated > 0,
|
success: itemsUpdated > 0,
|
||||||
updates: itemsUpdated,
|
updates: itemsUpdated,
|
||||||
unmatched: itemsUnmatched
|
unmatched: itemsUnmatched
|
||||||
@ -408,6 +411,33 @@ class LibraryItemController {
|
|||||||
SocketAuthority.clientEmitter(req.user.id, 'batch_quickmatch_complete', result)
|
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
|
// DELETE: api/items/all
|
||||||
async deleteAll(req, res) {
|
async deleteAll(req, res) {
|
||||||
if (!req.user.isAdminOrUp) {
|
if (!req.user.isAdminOrUp) {
|
||||||
@ -432,7 +462,7 @@ class LibraryItemController {
|
|||||||
return res.sendStatus(500)
|
return res.sendStatus(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await this.scanner.scanLibraryItemById(req.libraryItem.id)
|
const result = await this.scanner.scanLibraryItemByRequest(req.libraryItem)
|
||||||
res.json({
|
res.json({
|
||||||
result: Object.keys(ScanResult).find(key => ScanResult[key] == result)
|
result: Object.keys(ScanResult).find(key => ScanResult[key] == result)
|
||||||
})
|
})
|
||||||
|
@ -46,7 +46,7 @@ class AbMergeManager {
|
|||||||
toneJsonObject: null
|
toneJsonObject: null
|
||||||
}
|
}
|
||||||
const taskDescription = `Encoding audiobook "${libraryItem.media.metadata.title}" into a single m4b file.`
|
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)
|
this.taskManager.addTask(task)
|
||||||
Logger.info(`Start m4b encode for ${libraryItem.id} - TaskId: ${task.id}`)
|
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}".`
|
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) {
|
if (this.tasksRunning.length >= this.MAX_CONCURRENT_TASKS) {
|
||||||
Logger.info(`[AudioMetadataManager] Queueing embed metadata for audiobook "${libraryItem.media.metadata.title}"`)
|
Logger.info(`[AudioMetadataManager] Queueing embed metadata for audiobook "${libraryItem.media.metadata.title}"`)
|
||||||
|
@ -78,7 +78,7 @@ class PodcastManager {
|
|||||||
libraryId: podcastEpisodeDownload.libraryId,
|
libraryId: podcastEpisodeDownload.libraryId,
|
||||||
libraryItemId: podcastEpisodeDownload.libraryItemId,
|
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)
|
this.taskManager.addTask(task)
|
||||||
|
|
||||||
SocketAuthority.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
|
SocketAuthority.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
|
||||||
|
@ -9,6 +9,7 @@ class Task {
|
|||||||
this.title = null
|
this.title = null
|
||||||
this.description = null
|
this.description = null
|
||||||
this.error = null
|
this.error = null
|
||||||
|
this.showSuccess = false // If true client side should keep the task visible after success
|
||||||
|
|
||||||
this.isFailed = false
|
this.isFailed = false
|
||||||
this.isFinished = false
|
this.isFinished = false
|
||||||
@ -25,6 +26,7 @@ class Task {
|
|||||||
title: this.title,
|
title: this.title,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
error: this.error,
|
error: this.error,
|
||||||
|
showSuccess: this.showSuccess,
|
||||||
isFailed: this.isFailed,
|
isFailed: this.isFailed,
|
||||||
isFinished: this.isFinished,
|
isFinished: this.isFinished,
|
||||||
startedAt: this.startedAt,
|
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.id = getId(action)
|
||||||
this.action = action
|
this.action = action
|
||||||
this.data = { ...data }
|
this.data = { ...data }
|
||||||
this.title = title
|
this.title = title
|
||||||
this.description = description
|
this.description = description
|
||||||
|
this.showSuccess = showSuccess
|
||||||
this.startedAt = Date.now()
|
this.startedAt = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +51,10 @@ class Task {
|
|||||||
this.setFinished()
|
this.setFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
setFinished() {
|
setFinished(newDescription = null) {
|
||||||
|
if (newDescription) {
|
||||||
|
this.description = newDescription
|
||||||
|
}
|
||||||
this.isFinished = true
|
this.isFinished = true
|
||||||
this.finishedAt = Date.now()
|
this.finishedAt = Date.now()
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,11 @@ class ApiRouter {
|
|||||||
//
|
//
|
||||||
// Item Routes
|
// 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.delete('/items/all', LibraryItemController.deleteAll.bind(this))
|
||||||
|
|
||||||
this.router.get('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.findOne.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.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.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
|
// User Routes
|
||||||
//
|
//
|
||||||
|
@ -19,11 +19,13 @@ const ScanOptions = require('./ScanOptions')
|
|||||||
|
|
||||||
const Author = require('../objects/entities/Author')
|
const Author = require('../objects/entities/Author')
|
||||||
const Series = require('../objects/entities/Series')
|
const Series = require('../objects/entities/Series')
|
||||||
|
const Task = require('../objects/Task')
|
||||||
|
|
||||||
class Scanner {
|
class Scanner {
|
||||||
constructor(db, coverManager) {
|
constructor(db, coverManager, taskManager) {
|
||||||
this.db = db
|
this.db = db
|
||||||
this.coverManager = coverManager
|
this.coverManager = coverManager
|
||||||
|
this.taskManager = taskManager
|
||||||
|
|
||||||
this.cancelLibraryScan = {}
|
this.cancelLibraryScan = {}
|
||||||
this.librariesScanning = []
|
this.librariesScanning = []
|
||||||
@ -46,12 +48,24 @@ class Scanner {
|
|||||||
this.cancelLibraryScan[libraryId] = true
|
this.cancelLibraryScan[libraryId] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async scanLibraryItemById(libraryItemId) {
|
getScanResultDescription(result) {
|
||||||
const libraryItem = this.db.libraryItems.find(li => li.id === libraryItemId)
|
switch (result) {
|
||||||
if (!libraryItem) {
|
case ScanResult.ADDED:
|
||||||
Logger.error(`[Scanner] Scan libraryItem by id not found ${libraryItemId}`)
|
return 'Added to library'
|
||||||
return ScanResult.NOTHING
|
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)
|
const library = this.db.libraries.find(lib => lib.id === libraryItem.libraryId)
|
||||||
if (!library) {
|
if (!library) {
|
||||||
Logger.error(`[Scanner] Scan libraryItem by id library not found "${libraryItem.libraryId}"`)
|
Logger.error(`[Scanner] Scan libraryItem by id library not found "${libraryItem.libraryId}"`)
|
||||||
@ -63,7 +77,21 @@ class Scanner {
|
|||||||
return ScanResult.NOTHING
|
return ScanResult.NOTHING
|
||||||
}
|
}
|
||||||
Logger.info(`[Scanner] Scanning Library Item "${libraryItem.media.metadata.title}"`)
|
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) {
|
async scanLibraryItem(libraryMediaType, folder, libraryItem) {
|
||||||
|
Loading…
Reference in New Issue
Block a user