重构成功
This commit is contained in:
@ -1,9 +1,11 @@
|
||||
import fs from "fs";
|
||||
import { getImportExtensions, getExportFormats } from "./converters.js";
|
||||
import { getImportExtensions, getExportFormats } from "./service.js";
|
||||
import { sortByUsage } from "../../stats.js";
|
||||
|
||||
const EXCLUDED_FORMATS = ["x", "glb", "gltf"];
|
||||
|
||||
export const title = "格式转换工具";
|
||||
|
||||
export function listConvertibleFiles() {
|
||||
const cwd = process.cwd();
|
||||
const exts = getImportExtensions();
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
import { spawnSync } from "child_process";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ASSIMP_PATH = path.join(__dirname, "../../../bin/model_converter.exe");
|
||||
|
||||
let importExts = null;
|
||||
let exportFormats = null;
|
||||
|
||||
export function getImportExtensions() {
|
||||
if (!importExts) {
|
||||
const r = spawnSync(ASSIMP_PATH, ["listext"], { encoding: "utf8" });
|
||||
if (!r.stdout) throw new Error("无法获取支持的导入格式,请检查 model_converter.exe 是否存在");
|
||||
importExts = r.stdout.trim().split(";").map(e => e.replace("*", "").toLowerCase());
|
||||
}
|
||||
return importExts;
|
||||
}
|
||||
|
||||
export function getExportFormats() {
|
||||
if (!exportFormats) {
|
||||
const r = spawnSync(ASSIMP_PATH, ["listexport"], { encoding: "utf8" });
|
||||
if (!r.stdout) throw new Error("无法获取支持的导出格式,请检查 model_converter.exe 是否存在");
|
||||
exportFormats = r.stdout.trim().split(/\r?\n/).filter(Boolean);
|
||||
}
|
||||
return exportFormats;
|
||||
}
|
||||
|
||||
export async function convert(inputFile, outputFile, format, cwd = process.cwd()) {
|
||||
const r = spawnSync(ASSIMP_PATH, ["export", inputFile, outputFile, `-f${format}`], { encoding: "utf8", cwd });
|
||||
if (r.status !== 0) throw new Error(r.stderr || r.stdout || "转换失败");
|
||||
return outputFile;
|
||||
}
|
||||
@ -1,61 +1,30 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import color from "picocolors";
|
||||
import { convert } from "./converters.js";
|
||||
import { runInteractive, showSummary } from "./ui.js";
|
||||
import { createStepUI } from "../../utils/stepui.js";
|
||||
import { title, getSteps } from "./config.js";
|
||||
import { stopKeypress, waitForKey } from "../../keyboard.js";
|
||||
import { record } from "../../stats.js";
|
||||
import { processFile } from "./service.js";
|
||||
|
||||
const FORMAT_EXT = {
|
||||
collada: ".dae",stp: ".stp", obj: ".obj", objnomtl: ".obj",
|
||||
stl: ".stl", stlb: ".stl", ply: ".ply", plyb: ".ply", "3ds": ".3ds",
|
||||
gltf2: ".gltf", glb2: ".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 { dir, name } = path.parse(file);
|
||||
const outputExt = getOutputExt(config.outputFormat);
|
||||
const outputFile = path.join(dir || "", `${name}${outputExt}`);
|
||||
const absolutePath = path.join(cwd, file);
|
||||
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
return { ok: false, file, reason: "文件不存在" };
|
||||
}
|
||||
|
||||
await convert(file, outputFile, config.outputFormat, cwd);
|
||||
return { ok: true, file, output: outputFile };
|
||||
}
|
||||
|
||||
async function waitForEsc() {
|
||||
return waitForKey(color.dim("按 Esc 返回"), key => key?.name === "escape" || (key?.ctrl && key?.name === "c"));
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const result = await runInteractive();
|
||||
const run = async () => {
|
||||
const ui = createStepUI({ title, getSteps });
|
||||
const result = await ui.runInteractive();
|
||||
if (!result) return "back";
|
||||
|
||||
const { results } = result;
|
||||
const config = {
|
||||
files: results[0] || [],
|
||||
outputFormat: results[1] || "glb2"
|
||||
files: result.results[0] || [],
|
||||
outputFormat: result.results[1] || "glb2"
|
||||
};
|
||||
|
||||
stopKeypress();
|
||||
showSummary([
|
||||
ui.showSummary([
|
||||
"源文件: " + (config.files.length ? config.files.join(", ") : "未选择"),
|
||||
"目标格式: " + config.outputFormat.toUpperCase()
|
||||
]);
|
||||
|
||||
if (!config.files.length) {
|
||||
console.log(color.yellow("未选择任何模型文件"));
|
||||
await waitForEsc();
|
||||
await waitForKey(color.dim("按 Esc 返回"), key => key?.name === "escape" || (key?.ctrl && key?.name === "c"));
|
||||
return "back";
|
||||
}
|
||||
|
||||
@ -88,16 +57,12 @@ async function run() {
|
||||
}
|
||||
|
||||
console.log("\n" + color.bgGreen(color.black(" 转换完成 ")));
|
||||
if (success) {
|
||||
console.log(color.green(`成功: ${success} 个`));
|
||||
}
|
||||
if (failed.length) {
|
||||
console.log(color.yellow(`失败: ${failed.length} 个`));
|
||||
}
|
||||
if (success) console.log(color.green(`成功: ${success} 个`));
|
||||
if (failed.length) console.log(color.yellow(`失败: ${failed.length} 个`));
|
||||
|
||||
await waitForEsc();
|
||||
await waitForKey(color.dim("按 Esc 返回"), key => key?.name === "escape" || (key?.ctrl && key?.name === "c"));
|
||||
return "back";
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
id: "convert",
|
||||
|
||||
65
lib/plugins/convert/service.js
Normal file
65
lib/plugins/convert/service.js
Normal file
@ -0,0 +1,65 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { spawnSync } from "child_process";
|
||||
import { MODEL_CONVERTER } from "../../paths.js";
|
||||
|
||||
// 格式映射
|
||||
const FORMAT_EXT = {
|
||||
collada: ".dae", stp: ".stp", obj: ".obj", objnomtl: ".obj",
|
||||
stl: ".stl", stlb: ".stl", ply: ".ply", plyb: ".ply", "3ds": ".3ds",
|
||||
gltf2: ".gltf", glb2: ".glb",
|
||||
assbin: ".assbin", assxml: ".assxml", x3d: ".x3d",
|
||||
fbx: ".fbx", fbxa: ".fbx", "3mf": ".3mf", pbrt: ".pbrt", assjson: ".json"
|
||||
};
|
||||
|
||||
// 缓存
|
||||
let importExts = null;
|
||||
let exportFormats = null;
|
||||
|
||||
// 获取支持的导入格式
|
||||
export const getImportExtensions = () => {
|
||||
if (!importExts) {
|
||||
const r = spawnSync(MODEL_CONVERTER, ["listext"], { encoding: "utf8" });
|
||||
if (!r.stdout) throw new Error("无法获取支持的导入格式,请检查 model_converter.exe 是否存在");
|
||||
importExts = r.stdout.trim().split(";").map(e => e.replace("*", "").toLowerCase());
|
||||
}
|
||||
return importExts;
|
||||
};
|
||||
|
||||
// 获取支持的导出格式
|
||||
export const getExportFormats = () => {
|
||||
if (!exportFormats) {
|
||||
const r = spawnSync(MODEL_CONVERTER, ["listexport"], { encoding: "utf8" });
|
||||
if (!r.stdout) throw new Error("无法获取支持的导出格式,请检查 model_converter.exe 是否存在");
|
||||
exportFormats = r.stdout.trim().split(/\r?\n/).filter(Boolean);
|
||||
}
|
||||
return exportFormats;
|
||||
};
|
||||
|
||||
// 转换文件
|
||||
export const convert = async (inputFile, outputFile, format, cwd = process.cwd()) => {
|
||||
const r = spawnSync(MODEL_CONVERTER, ["export", inputFile, outputFile, `-f${format}`], { encoding: "utf8", cwd });
|
||||
if (r.status !== 0) throw new Error(r.stderr || r.stdout || "转换失败");
|
||||
return outputFile;
|
||||
};
|
||||
|
||||
// 获取输出文件扩展名
|
||||
const getOutputExt = (format) => {
|
||||
return FORMAT_EXT[format] || "." + format;
|
||||
};
|
||||
|
||||
// 处理单个文件
|
||||
export const processFile = async (file, config) => {
|
||||
const cwd = process.cwd();
|
||||
const { dir, name } = path.parse(file);
|
||||
const outputExt = getOutputExt(config.outputFormat);
|
||||
const outputFile = path.join(dir || "", `${name}${outputExt}`);
|
||||
const absolutePath = path.join(cwd, file);
|
||||
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
return { ok: false, file, reason: "文件不存在" };
|
||||
}
|
||||
|
||||
await convert(file, outputFile, config.outputFormat, cwd);
|
||||
return { ok: true, file, output: outputFile };
|
||||
};
|
||||
@ -1,9 +0,0 @@
|
||||
import { createStepUI } from "../../utils/stepui.js";
|
||||
import { getSteps } from "./config.js";
|
||||
|
||||
const ui = createStepUI({
|
||||
title: "格式转换工具",
|
||||
getSteps
|
||||
});
|
||||
|
||||
export const { runInteractive, showSummary } = ui;
|
||||
Reference in New Issue
Block a user