From 168fc28d48692a750aa8eabb0167cc35850c69b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=87=E8=B6=A3=E4=BF=9D=E7=BD=97?= Date: Mon, 7 Sep 2026 18:02:51 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=88=E6=A3=80=E6=9F=A5=20output=20?= =?UTF-8?q?=E8=B7=B3=E8=BF=87=E5=B7=B2=E5=A4=84=E7=90=86=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=86=8D=E5=BC=80=E5=A7=8B=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- photo-toolkit/format.js | 61 +++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/photo-toolkit/format.js b/photo-toolkit/format.js index 2df2d4c..133d5c9 100644 --- a/photo-toolkit/format.js +++ b/photo-toolkit/format.js @@ -11,11 +11,20 @@ const { IMAGE_TYPE_LABELS, } = require('./utils'); -// 处理单个图片 -function processImage(inputPath, outputPath) { +// 输出可能是 .jpg 或 .webp,先按文件名探测是否已处理,避免读 EXIF +function findExistingOutput(targetDir, inputFilename) { + const base = path.basename(inputFilename, path.extname(inputFilename)); + const candidates = ['.jpg', '.webp'].map((ext) => path.join(targetDir, base + ext)); + return candidates.find((p) => fs.existsSync(p)) || null; +} + +// 处理单个图片(可传入预检测结果,避免重复 identify / exiftool) +function processImage(inputPath, outputPath, predetect) { const filename = path.basename(inputPath); - const dimensions = getImageDimensions(inputPath); - const { type, reason } = detectImageType(inputPath, dimensions); + const dimensions = predetect?.dimensions ?? getImageDimensions(inputPath); + const { type, reason } = predetect + ? { type: predetect.type, reason: predetect.reason } + : detectImageType(inputPath, dimensions); const outputExt = getOutputExtension(type); const newFilename = path.basename(outputPath, path.extname(outputPath)) + outputExt; const finalOutputPath = path.join(path.dirname(outputPath), newFilename); @@ -103,12 +112,36 @@ async function runFormatTool(rl) { return; } - console.log(`\n📋 找到 ${files.length} 个文件:`); - files.forEach((file, index) => { + console.log(`\n📋 源目录共 ${files.length} 个文件,先检查 output 中已存在的结果...`); + + const pendingFiles = []; + let skipped = 0; + for (const file of files) { + const existing = findExistingOutput(targetDir, file); + if (existing) { + skipped++; + console.log(` ⏭️ ${file} → 已存在 ${path.basename(existing)}`); + } else { + pendingFiles.push(file); + } + } + + if (pendingFiles.length === 0) { + console.log(`\n✅ 全部 ${files.length} 个文件已在输出目录中,无需处理`); + return; + } + + console.log(`\n🔍 分析剩余 ${pendingFiles.length} 个文件的相机/类型(已跳过 ${skipped} 个)...`); + const jobs = pendingFiles.map((file) => { const inputPath = path.join(sourceDir, file); const dimensions = getImageDimensions(inputPath); const { type, reason } = detectImageType(inputPath, dimensions); - console.log(` ${index + 1}. ${file} → ${IMAGE_TYPE_LABELS[type]} (${reason})`); + return { file, inputPath, dimensions, type, reason }; + }); + + console.log(`\n📋 待处理 ${jobs.length} 个文件:`); + jobs.forEach((job, index) => { + console.log(` ${index + 1}. ${job.file} → ${IMAGE_TYPE_LABELS[job.type]} (${job.reason})`); }); const confirm = await question(rl, '\n❓ 是否开始处理? (y/n): '); @@ -119,24 +152,22 @@ async function runFormatTool(rl) { console.log('\n🚀 开始处理...\n'); let processed = 0; - let skipped = 0; let failed = 0; - for (let i = 0; i < files.length; i++) { - const file = files[i]; - const inputPath = path.join(sourceDir, file); - const outputPath = path.join(targetDir, file); + for (let i = 0; i < jobs.length; i++) { + const job = jobs[i]; + const outputPath = path.join(targetDir, job.file); - console.log(`\n[${i + 1}/${files.length}]`); + console.log(`\n[${i + 1}/${jobs.length}]`); try { - const result = processImage(inputPath, outputPath); + const result = processImage(job.inputPath, outputPath, job); if (result) { processed++; } else { skipped++; } } catch (error) { - console.error(`❌ 处理失败: ${file}`); + console.error(`❌ 处理失败: ${job.file}`); failed++; } }