91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
import express from "express";
|
||
import u from "@/utils";
|
||
import { success } from "@/lib/responseFormat";
|
||
import fs from "fs";
|
||
import path from "path";
|
||
const router = express.Router();
|
||
|
||
// 字段映射表
|
||
const DATA_MAP: { label: string; value: string; subDir?: string }[] = [
|
||
{ label: "README", value: "README" },
|
||
{ label: "前缀", value: "prefix" },
|
||
{ label: "角色", value: "art_character", subDir: "art_prompt" },
|
||
{ label: "角色衍生", value: "art_character_derivative", subDir: "art_prompt" },
|
||
{ label: "道具", value: "art_prop", subDir: "art_prompt" },
|
||
{ label: "道具衍生", value: "art_prop_derivative", subDir: "art_prompt" },
|
||
{ label: "场景", value: "art_scene", subDir: "art_prompt" },
|
||
{ label: "场景衍生", value: "art_scene_derivative", subDir: "art_prompt" },
|
||
{ label: "分镜", value: "art_storyboard", subDir: "art_prompt" },
|
||
{ label: "分镜视频", value: "art_storyboard_video", subDir: "art_prompt" },
|
||
{ label: "技法-导演规划", value: "director_planning", subDir: "driector_skills" },
|
||
{ label: "技法-分镜表设计", value: "director_storyboard_table", subDir: "driector_skills" },
|
||
];
|
||
|
||
// 读取 md 文件内容,文件不存在时返回空字符串
|
||
function readMd(filePath: string): string {
|
||
try {
|
||
return fs.readFileSync(filePath, "utf-8");
|
||
} catch {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
// 获取 images 文件夹第一张图片的 base64,无图片返回空字符串
|
||
function readFirstImage(imagesDir: string): string {
|
||
try {
|
||
const files = fs.readdirSync(imagesDir);
|
||
const imgFile = files.find((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
|
||
if (!imgFile) return "";
|
||
const imgPath = path.join(imagesDir, imgFile);
|
||
const ext = path.extname(imgFile).slice(1).toLowerCase();
|
||
const mimeType = ext === "jpg" ? "jpeg" : ext;
|
||
const base64 = fs.readFileSync(imgPath).toString("base64");
|
||
return `data:image/${mimeType};base64,${base64}`;
|
||
} catch {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
// 获取视觉手册
|
||
export default router.post("/", async (req, res) => {
|
||
try {
|
||
const artPromptsDir = u.getPath(["skills", "art_prompts"]);
|
||
|
||
// 读取所有风格文件夹
|
||
const styleDirs = fs
|
||
.readdirSync(artPromptsDir, { withFileTypes: true })
|
||
.filter((d) => d.isDirectory())
|
||
.map((d) => d.name);
|
||
|
||
const result = styleDirs.map((styleName) => {
|
||
const styleDir = path.join(artPromptsDir, styleName);
|
||
const imagesDir = path.join(styleDir, "images");
|
||
|
||
const image = readFirstImage(imagesDir);
|
||
|
||
const data = DATA_MAP.map(({ label, value, subDir }) => {
|
||
let mdPath: string;
|
||
if (subDir) {
|
||
mdPath = path.join(styleDir, subDir, `${value}.md`);
|
||
} else {
|
||
mdPath = path.join(styleDir, `${value}.md`);
|
||
}
|
||
return {
|
||
label,
|
||
value,
|
||
data: readMd(mdPath),
|
||
};
|
||
});
|
||
|
||
return {
|
||
name: styleName,
|
||
image,
|
||
data,
|
||
};
|
||
});
|
||
res.status(200).send(success(result));
|
||
} catch (err) {
|
||
res.status(500).send({ error: String(err) });
|
||
}
|
||
});
|