UNPKG

taglib-wasm

Version:

TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps

90 lines (89 loc) 3.25 kB
import { applyTagsToFile } from "../simple/index.js"; import { writeFileData } from "../utils/write.js"; import { processBatch } from "./file-processors.js"; import { scanFolder } from "./scan-operations.js"; import { EMPTY_TAG } from "./types.js"; async function updateFolderTags(updates, options = {}) { const startTime = Date.now(); const { continueOnError = true, concurrency = 4, signal } = options; const items = []; const batchSize = concurrency * 10; for (let i = 0; i < updates.length; i += batchSize) { signal?.throwIfAborted(); const batch = updates.slice(i, Math.min(i + batchSize, updates.length)); const updateMap = new Map(batch.map((u) => [u.path, u])); await processBatch( batch.map((u) => u.path), async (path) => { const update = updateMap.get(path); try { await applyTagsToFile(update.path, update.tags); items.push({ status: "ok", path: update.path }); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); if (continueOnError) { items.push({ status: "error", path: update.path, error: err }); } else { throw err; } } return { path, tags: EMPTY_TAG }; }, concurrency ); } return { items, duration: Date.now() - startTime }; } function buildCriteriaKey(tags, criteria) { const record = {}; for (const field of criteria) { const val = tags[field]; const strVal = Array.isArray(val) ? val.join(", ") : String(val ?? ""); if (strVal) record[field] = strVal; } if (Object.keys(record).length === 0) return null; const key = criteria.map((f) => record[f] ?? "").join("\0"); return { record, key }; } async function findDuplicates(folderPath, options) { const { criteria = ["artist", "title"], ...scanOptions } = options ?? {}; const result = await scanFolder(folderPath, scanOptions); scanOptions.signal?.throwIfAborted(); const groupMap = /* @__PURE__ */ new Map(); for (const item of result.items) { if (item.status !== "ok") continue; const entry = buildCriteriaKey(item.tags, criteria); if (!entry) continue; const existing = groupMap.get(entry.key); if (existing) { existing.files.push(item); } else { groupMap.set(entry.key, { criteria: entry.record, files: [item] }); } } return Array.from(groupMap.values()).filter((g) => g.files.length >= 2); } async function exportFolderMetadata(folderPath, outputPath, options) { const result = await scanFolder(folderPath, options); const okItems = result.items.filter((item) => item.status === "ok"); const errorItems = result.items.filter((item) => item.status === "error"); const data = { folder: folderPath, scanDate: (/* @__PURE__ */ new Date()).toISOString(), summary: { totalFiles: result.items.length, processedFiles: okItems.length, errors: errorItems.length, duration: result.duration }, files: okItems, errors: errorItems }; const jsonBytes = new TextEncoder().encode(JSON.stringify(data, null, 2)); await writeFileData(outputPath, jsonBytes); } export { exportFolderMetadata, findDuplicates, updateFolderTags };