fix blocking during scans
This commit is contained in:
@@ -3,7 +3,8 @@ import crypto from 'crypto'
|
||||
import type { Library, ImportedTag, TagMapping } from '@/types'
|
||||
import { getDb } from './db'
|
||||
import { resolveLibraryRoot } from './libraries'
|
||||
import { parseComicInfo } from './comic-info'
|
||||
import { parseComicInfoAsync } from './comic-info'
|
||||
import { mapConcurrent } from './zip-utils'
|
||||
|
||||
// ─── Metadata Import ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -13,7 +14,7 @@ import { parseComicInfo } from './comic-info'
|
||||
* - For each tag: if a mapping exists, assigns the real tag; otherwise creates
|
||||
* an imported tag entry.
|
||||
*/
|
||||
export function importComicMetadata(library: Library): void {
|
||||
export async function importComicMetadata(library: Library): Promise<void> {
|
||||
const db = getDb()
|
||||
const libraryRoot = resolveLibraryRoot(library)
|
||||
|
||||
@@ -56,53 +57,65 @@ export function importComicMetadata(library: Library): void {
|
||||
|
||||
let importedCount = 0
|
||||
|
||||
db.transaction(() => {
|
||||
for (const issue of issues) {
|
||||
const absPath = path.join(libraryRoot, issue.file_path)
|
||||
const info = parseComicInfo(absPath)
|
||||
if (!info) continue
|
||||
// Process in batches: async file reads (10 concurrent) followed by batch DB writes,
|
||||
// with an event-loop yield between batches to keep the app responsive.
|
||||
const BATCH_SIZE = 50
|
||||
for (let i = 0; i < issues.length; i += BATCH_SIZE) {
|
||||
const batch = issues.slice(i, i + BATCH_SIZE)
|
||||
|
||||
// Merge with existing metadata JSON (preserve pageCount, coverUrl, etc.)
|
||||
const existingMeta = issue.metadata ? JSON.parse(issue.metadata) : {}
|
||||
const mergedMeta = {
|
||||
...existingMeta,
|
||||
writer: info.writer,
|
||||
publisher: info.publisher,
|
||||
translator: info.translator,
|
||||
web: info.web,
|
||||
month: info.month,
|
||||
day: info.day,
|
||||
}
|
||||
// Async: read ComicInfo.xml from each archive concurrently (10 at a time).
|
||||
// Uses async ZIP central-directory reader — no full-file reads.
|
||||
const infos = await mapConcurrent(batch, 10, (issue) =>
|
||||
parseComicInfoAsync(path.join(libraryRoot, issue.file_path))
|
||||
)
|
||||
|
||||
updateItem.run({
|
||||
item_key: issue.item_key,
|
||||
title: info.title ?? existingMeta.title ?? null,
|
||||
year: info.year,
|
||||
genres: info.genre,
|
||||
metadata: JSON.stringify(mergedMeta),
|
||||
})
|
||||
// Sync: write this batch to the DB in one transaction.
|
||||
db.transaction(() => {
|
||||
for (let j = 0; j < batch.length; j++) {
|
||||
const issue = batch[j]
|
||||
const info = infos[j]
|
||||
if (!info) continue
|
||||
|
||||
// Process tags
|
||||
for (const tagName of info.tags) {
|
||||
const mappedTagId = mappings.get(tagName)
|
||||
if (mappedTagId) {
|
||||
// Mapping exists — assign the real tag
|
||||
addMediaTag.run(issue.item_key, mappedTagId)
|
||||
} else {
|
||||
// No mapping — create imported tag
|
||||
const importedTagId = crypto.randomUUID()
|
||||
const row = upsertImportedTag.get({
|
||||
id: importedTagId,
|
||||
library_id: library.id,
|
||||
name: tagName,
|
||||
}) as { id: string }
|
||||
addItemImportedTag.run(issue.item_key, row.id)
|
||||
const existingMeta = issue.metadata ? JSON.parse(issue.metadata) : {}
|
||||
const mergedMeta = {
|
||||
...existingMeta,
|
||||
writer: info.writer,
|
||||
publisher: info.publisher,
|
||||
translator: info.translator,
|
||||
web: info.web,
|
||||
month: info.month,
|
||||
day: info.day,
|
||||
}
|
||||
}
|
||||
|
||||
importedCount++
|
||||
}
|
||||
})()
|
||||
updateItem.run({
|
||||
item_key: issue.item_key,
|
||||
title: info.title ?? existingMeta.title ?? null,
|
||||
year: info.year,
|
||||
genres: info.genre,
|
||||
metadata: JSON.stringify(mergedMeta),
|
||||
})
|
||||
|
||||
for (const tagName of info.tags) {
|
||||
const mappedTagId = mappings.get(tagName)
|
||||
if (mappedTagId) {
|
||||
addMediaTag.run(issue.item_key, mappedTagId)
|
||||
} else {
|
||||
const importedTagId = crypto.randomUUID()
|
||||
const row = upsertImportedTag.get({
|
||||
id: importedTagId,
|
||||
library_id: library.id,
|
||||
name: tagName,
|
||||
}) as { id: string }
|
||||
addItemImportedTag.run(issue.item_key, row.id)
|
||||
}
|
||||
}
|
||||
|
||||
importedCount++
|
||||
}
|
||||
})()
|
||||
|
||||
await new Promise<void>((r) => setImmediate(r))
|
||||
}
|
||||
|
||||
console.log(`[comic-metadata] Imported metadata for ${importedCount}/${issues.length} issues in "${library.name}"`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user