feat: 重构 NodeJS 版本图片处理工具
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
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 newSize = getTargetSize(type, dimensions.width, dimensions.height);
|
||||
console.log(` 目标尺寸: ${newSize}`);
|
||||
execSync(
|
||||
`magick "${inputPath}" -gravity center -resize "${newSize}^" -extent "${newSize}" -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)');
|
||||
|
||||
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 };
|
||||
Reference in New Issue
Block a user