153 lines
4.9 KiB
JavaScript
153 lines
4.9 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
const { execSync } = require('child_process');
|
||
const {
|
||
question,
|
||
getImageDimensions,
|
||
detectImageType,
|
||
getTargetSize,
|
||
getOutputExtension,
|
||
IMAGE_TYPES,
|
||
IMAGE_TYPE_LABELS,
|
||
} = require('./utils');
|
||
|
||
// 处理单个图片
|
||
function processImage(inputPath, outputPath) {
|
||
const filename = path.basename(inputPath);
|
||
const dimensions = getImageDimensions(inputPath);
|
||
const { type, 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);
|
||
|
||
if (fs.existsSync(finalOutputPath)) {
|
||
console.log(`⏭️ 跳过已存在的文件: ${newFilename}`);
|
||
return false;
|
||
}
|
||
|
||
console.log(`\n📸 处理: ${filename}`);
|
||
console.log(` 类型: ${IMAGE_TYPE_LABELS[type]} (${reason})`);
|
||
console.log(` 尺寸: ${dimensions.width}x${dimensions.height}`);
|
||
|
||
try {
|
||
if (type === IMAGE_TYPES.SCREENSHOT) {
|
||
execSync(
|
||
`magick "${inputPath}" -gravity center -quality 80 "${finalOutputPath}"`,
|
||
{ stdio: 'inherit' }
|
||
);
|
||
} else {
|
||
const target = getTargetSize(type, dimensions.width, dimensions.height);
|
||
console.log(` 目标尺寸: ${target.size}${target.crop ? '' : ' (保持比例)'}`);
|
||
const resizeArgs = target.crop
|
||
? `-gravity center -resize "${target.size}^" -extent "${target.size}"`
|
||
: `-resize "${target.size}"`;
|
||
execSync(
|
||
`magick "${inputPath}" ${resizeArgs} -quality 80 "${finalOutputPath}"`,
|
||
{ stdio: 'inherit' }
|
||
);
|
||
}
|
||
console.log(` ✅ 图片转换完成 → ${outputExt}`);
|
||
} catch (error) {
|
||
console.error(` ❌ 图片转换失败`);
|
||
throw error;
|
||
}
|
||
|
||
try {
|
||
execSync(
|
||
`exiftool -overwrite_original -gps:all= -artist="奇趣保罗" -CreatorTool= "${finalOutputPath}"`,
|
||
{ stdio: 'inherit' }
|
||
);
|
||
console.log(` ✅ 元数据处理完成`);
|
||
} catch (error) {
|
||
console.error(` ❌ 元数据处理失败`);
|
||
throw error;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 格式转换主函数
|
||
async function runFormatTool(rl) {
|
||
console.log('\n🎨 图片格式转换工具');
|
||
console.log('='.repeat(50));
|
||
console.log('支持类型: iPhone (4:3 JPG) · 索尼相机 (3:2 JPG) · 截图 (WebP) · 小米手机 (4:3 JPG)');
|
||
|
||
const inputDir = await question(rl, '\n📁 请输入图片所在目录 (留空使用当前目录): ');
|
||
const sourceDir = inputDir.trim() || process.cwd();
|
||
|
||
if (!fs.existsSync(sourceDir)) {
|
||
console.error(`❌ 目录不存在: ${sourceDir}`);
|
||
return;
|
||
}
|
||
|
||
const outputDir = await question(rl, '📁 请输入输出目录 (留空使用 output): ');
|
||
const targetDir = outputDir.trim() || path.join(sourceDir, 'output');
|
||
|
||
if (!fs.existsSync(targetDir)) {
|
||
fs.mkdirSync(targetDir, { recursive: true });
|
||
console.log(`✅ 已创建输出目录: ${targetDir}`);
|
||
}
|
||
|
||
const extensions = ['.heic', '.heif', '.png', '.jpg', '.jpeg', '.HEIC', '.HEIF', '.PNG', '.JPG', '.JPEG'];
|
||
const files = fs.readdirSync(sourceDir).filter(file => {
|
||
const ext = path.extname(file);
|
||
const fullPath = path.join(sourceDir, file);
|
||
const isFile = fs.statSync(fullPath).isFile();
|
||
const hasValidExt = extensions.includes(ext);
|
||
const notModified = !file.includes('MODIFIED') && !file.includes('MERGED');
|
||
return isFile && hasValidExt && notModified;
|
||
});
|
||
|
||
if (files.length === 0) {
|
||
console.log('\n⚠️ 未找到符合条件的图片文件');
|
||
return;
|
||
}
|
||
|
||
console.log(`\n📋 找到 ${files.length} 个文件:`);
|
||
files.forEach((file, index) => {
|
||
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})`);
|
||
});
|
||
|
||
const confirm = await question(rl, '\n❓ 是否开始处理? (y/n): ');
|
||
if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
|
||
console.log('❌ 已取消');
|
||
return;
|
||
}
|
||
|
||
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);
|
||
|
||
console.log(`\n[${i + 1}/${files.length}]`);
|
||
try {
|
||
const result = processImage(inputPath, outputPath);
|
||
if (result) {
|
||
processed++;
|
||
} else {
|
||
skipped++;
|
||
}
|
||
} catch (error) {
|
||
console.error(`❌ 处理失败: ${file}`);
|
||
failed++;
|
||
}
|
||
}
|
||
|
||
console.log('\n' + '='.repeat(50));
|
||
console.log('📊 处理完成!');
|
||
console.log(` ✅ 成功: ${processed} 个`);
|
||
console.log(` ⏭️ 跳过: ${skipped} 个`);
|
||
console.log(` ❌ 失败: ${failed} 个`);
|
||
console.log(` 📁 输出目录: ${targetDir}`);
|
||
}
|
||
|
||
module.exports = { runFormatTool };
|