const path = require('path'); const { execSync } = require('child_process'); const readline = require('readline'); const IMAGE_TYPES = { IPHONE: 'iphone', SONY: 'sony', XIAOMI: 'xiaomi', SCREENSHOT: 'screenshot', }; const IMAGE_TYPE_LABELS = { [IMAGE_TYPES.IPHONE]: '📱 iPhone (4:3)', [IMAGE_TYPES.SONY]: '📷 索尼相机 (3:2)', [IMAGE_TYPES.XIAOMI]: '📱 小米手机 (4:3)', [IMAGE_TYPES.SCREENSHOT]: '🖼️ 截图 → WebP', }; // 创建 readline 接口 function createInterface() { return readline.createInterface({ input: process.stdin, output: process.stdout }); } // 提问函数 function question(rl, prompt) { return new Promise((resolve) => { rl.question(prompt, resolve); }); } // 获取图片尺寸 function getImageDimensions(filePath) { try { const width = execSync(`identify -format "%w" "${filePath}"`, { encoding: 'utf-8' }).trim(); const height = execSync(`identify -format "%h" "${filePath}"`, { encoding: 'utf-8' }).trim(); return { width: parseInt(width), height: parseInt(height) }; } catch (error) { console.error(`获取图片尺寸失败: ${filePath}`); throw error; } } // 读取 EXIF 中的 Make / Model function getExifMakeModel(filePath) { try { const output = execSync(`exiftool -Make -Model -s3 "${filePath}"`, { encoding: 'utf-8' }).trim(); const [make = '', model = ''] = output.split('\n'); return { make: make.trim(), model: model.trim() }; } catch { return { make: '', model: '' }; } } // 根据宽高比推断 iPhone (4:3) 或索尼 (3:2) function detectTypeFromAspectRatio(width, height) { const ratio = Math.max(width, height) / Math.min(width, height); const diff43 = Math.abs(ratio - 4 / 3); const diff32 = Math.abs(ratio - 3 / 2); return diff43 <= diff32 ? IMAGE_TYPES.IPHONE : IMAGE_TYPES.SONY; } // 检测图片类型:iPhone / 索尼相机 / 截图 function detectImageType(filePath, dimensions) { const ext = path.extname(filePath).toLowerCase(); if (ext === '.png') { return { type: IMAGE_TYPES.SCREENSHOT, reason: 'PNG 格式' }; } const { make, model } = getExifMakeModel(filePath); const makeUpper = make.toUpperCase(); const modelUpper = model.toUpperCase(); // 我的 iPhone if (makeUpper === 'APPLE' || modelUpper.includes('IPHONE')) { return { type: IMAGE_TYPES.IPHONE, reason: `EXIF: ${make} ${model}`.trim() }; } // 老妈小米 11 青春版 if (makeUpper === 'XIAOMI' || modelUpper.includes('M2101K9C')) { return { type: IMAGE_TYPES.XIAOMI, reason: `EXIF: ${make} ${model}`.trim() }; } // 索尼相机 if (makeUpper.includes('SONY') || modelUpper.includes('ILCE') || modelUpper.startsWith('DSC-')) { return { type: IMAGE_TYPES.SONY, reason: `EXIF: ${make} ${model}`.trim() }; } // iPhone if (['.heic', '.heif'].includes(ext)) { return { type: IMAGE_TYPES.IPHONE, reason: 'HEIC/HEIF 格式' }; } const { width, height } = dimensions; const type = detectTypeFromAspectRatio(width, height); return { type, reason: `宽高比推断 (${width}x${height})` }; } // 根据类型和方向获取目标尺寸,截图为 null(不裁剪) // 索尼返回 ImageMagick 缩放规格(如 2250x),按原图宽高比缩放、不裁剪 function getTargetSize(type, width, height) { const landscape = width >= height; switch (type) { case IMAGE_TYPES.IPHONE: return { size: landscape ? '2000x1500' : '1500x2000', crop: true }; case IMAGE_TYPES.SONY: return { size: landscape ? '2250x' : 'x2250', crop: false }; case IMAGE_TYPES.SCREENSHOT: return null; default: return { size: landscape ? '2250x1500' : '1500x2250', crop: true }; } } // 获取输出扩展名 function getOutputExtension(type) { return type === IMAGE_TYPES.SCREENSHOT ? '.webp' : '.jpg'; } // 规范化文件名(去掉 -Modified 或 _Modified 后缀) function normalizeBasename(name) { // 匹配 -Modified 或 _Modified(不区分大小写) const match = name.match(/^(.*?)[-_]?modified$/i); if (match && match[1]) { return match[1]; } return name; } // 显示菜单并获取选择 async function showMenu(rl, title, options) { console.log(`\n${title}`); console.log('='.repeat(50)); options.forEach((option, index) => { console.log(`${index + 1}. ${option}`); }); const choice = await question(rl, '\n请选择 (输入数字): '); const index = parseInt(choice) - 1; if (index >= 0 && index < options.length) { return index; } return -1; } module.exports = { createInterface, question, getImageDimensions, getExifMakeModel, detectImageType, detectTypeFromAspectRatio, getTargetSize, getOutputExtension, normalizeBasename, showMenu, IMAGE_TYPES, IMAGE_TYPE_LABELS, };