✅ {{ t('library.status.completed') }} ({{ completedFiles.length }})
+
📋 {{ t('library.status.registered') }} ({{ registeredFiles.length }})
+
@@ -1069,6 +1075,7 @@ async function playVideo(f: any) {
const registering = ref(false)
const processing = ref(false)
const unregistering = ref(false)
+const batchProcessing = ref(false)
const reprocessing = ref(false)
const confirmDialog = ref(false)
const confirmTarget = ref<{ mode: 'register' | 'process' | 'reprocess'; file: any } | null>(null)
@@ -1871,6 +1878,84 @@ async function batchRegister() {
setTimeout(() => { actionMsg.value = '' }, 8000)
}
+async function batchDeepScan() {
+ if (batchProcessing.value) return
+ if (selectedRegisteredFiles.value.length === 0) return
+
+ const filesToProcess = selectedRegisteredFiles.value
+
+ if (!confirm(`Deep scan ${filesToProcess.length} files?\n\nThis will process all selected files with default settings.`)) return
+
+ batchProcessing.value = true
+ actionMsg.value = `Starting deep scan for ${filesToProcess.length} files...`
+
+ const maxConcurrent = 3
+ const limit = pLimit(maxConcurrent)
+
+ let successCount = 0
+ let failCount = 0
+ let queueCount = 0
+
+ const tasks = filesToProcess.map((f, index) => {
+ return limit(async () => {
+ const fileUuid = f.file_uuid
+ if (!fileUuid) {
+ failCount++
+ return
+ }
+
+ const position = index + 1
+ if (position > maxConcurrent) {
+ queueCount++
+ actionMsg.value = `Processing ${successCount + 1}/${filesToProcess.length} (${queueCount} in queue)...`
+ } else {
+ actionMsg.value = `Processing ${position}/${filesToProcess.length}...`
+ }
+
+ try {
+ const file = filesCache.value.find((x: any) => x.file_uuid === fileUuid)
+ if (!file) {
+ failCount++
+ return
+ }
+
+ const result: any = await apiCall('re_scan_file', {
+ fileUuid: fileUuid,
+ autoPipeline: true
+ })
+
+ if (result.success) {
+ successCount++
+ if (file) {
+ file.status = 'scanning'
+ fileStatusCache.value[fileUuid] = 'scanning'
+ await syncFileStatus(fileUuid)
+ }
+ } else {
+ failCount++
+ }
+ } catch (e: any) {
+ failCount++
+ console.error('[batchDeepScan] Failed:', f.file_name, e)
+ }
+ })
+ })
+
+ await Promise.all(tasks)
+
+ selectedFiles.value = []
+ batchProcessing.value = false
+
+ if (successCount > 0) {
+ actionMsg.value = `Deep scan started for ${successCount} files${failCount > 0 ? `, ${failCount} failed` : ''}`
+ } else {
+ actionMsg.value = `Failed to start deep scan for all ${failCount} files`
+ }
+
+ await loadFiles()
+ setTimeout(() => { actionMsg.value = '' }, 8000)
+}
+
async function batchUnregister() {
if (unregistering.value) return
if (selectedRegisteredFiles.value.length === 0) return
@@ -2248,7 +2333,9 @@ function resetFilters() {
.ms-fm-section-title { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 12px; padding-left: 4px; display: flex; align-items: center; gap: 12px; }
.ms-batch-register-btn { margin-left: auto; padding: 4px 12px; font-size: 12px; font-weight: 600; background: var(--primary-color); color: white; border: none; border-radius: 6px; cursor: pointer; }
.ms-batch-register-btn:hover { background: var(--primary-hover); }
-.ms-batch-unregister-btn { margin-left: auto; padding: 4px 12px; font-size: 12px; font-weight: 600; background: var(--danger-color, #dc3545); color: white; border: none; border-radius: 6px; cursor: pointer; }
+.ms-batch-scan-btn { margin-left: 8px; padding: 4px 12px; font-size: 12px; font-weight: 600; background: var(--info-color, #17a2b8); color: white; border: none; border-radius: 6px; cursor: pointer; }
+.ms-batch-scan-btn:hover { background: var(--info-hover, #138496); }
+.ms-batch-unregister-btn { margin-left: 8px; padding: 4px 12px; font-size: 12px; font-weight: 600; background: var(--danger-color, #dc3545); color: white; border: none; border-radius: 6px; cursor: pointer; }
.ms-batch-unregister-btn:hover { background: var(--danger-hover, #c82333); }
.ms-fm-card-checkbox { position: absolute; bottom: 8px; right: 8px; width: 24px; height: 24px; border: 2px solid var(--border-color); border-radius: 4px; background: var(--card-background); cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 14px; color: var(--primary-color); z-index: 10; }
.ms-fm-card-checkbox:hover { border-color: var(--primary-color); }