import fs from "fs"; import path from "path"; import color from "picocolors"; import { convert } from "./converters.js"; import { runInteractive, showSummary } from "./ui.js"; import { stopKeypress, initKeypress, onKey } from "../keyboard.js"; import { record } from "../stats.js"; const FORMAT_EXT = { collada: ".dae", x: ".x", stp: ".stp", obj: ".obj", objnomtl: ".obj", stl: ".stl", stlb: ".stl", ply: ".ply", plyb: ".ply", "3ds": ".3ds", gltf2: ".gltf", glb2: ".glb", gltf: ".gltf", glb: ".glb", assbin: ".assbin", assxml: ".assxml", x3d: ".x3d", fbx: ".fbx", fbxa: ".fbx", "3mf": ".3mf", pbrt: ".pbrt", assjson: ".json" }; function getOutputExt(format) { return FORMAT_EXT[format] || "." + format; } async function processFile(file, config) { const cwd = process.cwd(); const baseName = file.slice(0, file.lastIndexOf(".")); const outputExt = getOutputExt(config.outputFormat); const outputFile = baseName + outputExt; if (!fs.existsSync(path.join(cwd, file))) { return { ok: false, file, reason: "文件不存在" }; } await convert(file, outputFile, config.outputFormat, cwd); return { ok: true, file, output: outputFile }; } async function waitForEsc() { initKeypress(); return new Promise(resolve => { onKey((str, key) => { if (key?.name === "escape" || (key?.ctrl && key?.name === "c")) { resolve(); } }); }); } export async function run() { const result = await runInteractive(); if (!result) return "back"; const { steps, results } = result; const config = { files: results[0] || [], outputFormat: results[1] || "glb2" }; stopKeypress(); showSummary([ "源文件: " + (config.files.length ? config.files.join(", ") : "未选择"), "目标格式: " + config.outputFormat.toUpperCase() ]); if (!config.files.length) { console.log(color.yellow("未选择任何模型文件")); console.log(color.dim("\n按 Esc 返回")); await waitForEsc(); return "back"; } const total = config.files.length; let success = 0; const failed = []; console.log(color.cyan(`开始转换 ${total} 个文件...\n`)); for (let i = 0; i < config.files.length; i++) { const file = config.files[i]; const progress = `[${i + 1}/${total}]`; console.log(color.dim(`${progress} 处理中: ${file}`)); try { const result = await processFile(file, config); if (result.ok) { success++; record("convert_format", config.outputFormat); console.log(color.green(`${progress} ✓ ${file} → ${path.basename(result.output)}`)); } else { failed.push({ file, reason: result.reason }); console.log(color.yellow(`${progress} ⊘ 跳过: ${file} (${result.reason})`)); } } catch (err) { failed.push({ file, reason: err?.message || String(err) }); console.log(color.red(`${progress} ✖ 失败: ${file}`)); console.log(color.dim(" " + String(err?.message || err))); } } console.log("\n" + color.bgGreen(color.black(" 转换完成 "))); if (success) { console.log(color.green(`成功: ${success} 个`)); } if (failed.length) { console.log(color.yellow(`失败: ${failed.length} 个`)); } console.log(color.dim("\n按 Esc 返回")); await waitForEsc(); return "back"; }