添加导演手册

This commit is contained in:
小帅 2026-04-02 15:28:36 +08:00
parent 3e45c854aa
commit b8882c84cb
33 changed files with 475 additions and 198 deletions

View File

@ -0,0 +1,2 @@
12第三方第三方
12是多少

View File

@ -0,0 +1 @@
12

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

View File

@ -37,6 +37,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
table.text("intro");
table.text("type");
table.text("artStyle");
table.text("directorManual");
table.text("mode");
table.text("videoRatio");
table.integer("createTime");

View File

@ -1,4 +1,4 @@
// @routes-hash 6b77c26005a9993d80cda7ab95d26702
// @routes-hash 0bd9383c5e26c63f175c07ee431ea8a9
import { Express } from "express";
import route1 from "./routes/agents/clearMemory";
@ -78,59 +78,62 @@ import route74 from "./routes/production/workbench/getGenerateData";
import route75 from "./routes/production/workbench/getVideoList";
import route76 from "./routes/production/workbench/getVideoModelDetail";
import route77 from "./routes/production/workbench/selectVideo";
import route78 from "./routes/project/addProject";
import route79 from "./routes/project/addVisual";
import route78 from "./routes/project/addDirectorManual";
import route79 from "./routes/project/addProject";
import route80 from "./routes/project/addVisualManual";
import route81 from "./routes/project/deleteVisualManual";
import route82 from "./routes/project/delProject";
import route83 from "./routes/project/editProject";
import route84 from "./routes/project/editVisualManual";
import route85 from "./routes/project/getProject";
import route86 from "./routes/project/getVisualManual";
import route87 from "./routes/project/visualManual";
import route88 from "./routes/script/addScript";
import route89 from "./routes/script/delScript";
import route90 from "./routes/script/exportScript";
import route91 from "./routes/script/extractAssets";
import route92 from "./routes/script/getScrptApi";
import route93 from "./routes/script/pollScriptAssets";
import route94 from "./routes/script/updateScript";
import route95 from "./routes/scriptAgent/getPlanData";
import route96 from "./routes/scriptAgent/setPlanData";
import route97 from "./routes/scriptAgent/updateData";
import route98 from "./routes/setting/about/checkUpdate";
import route99 from "./routes/setting/about/downloadApp";
import route100 from "./routes/setting/agentDeploy/agentSetKey";
import route101 from "./routes/setting/agentDeploy/deployAgentModel";
import route102 from "./routes/setting/agentDeploy/getAgentDeploy";
import route103 from "./routes/setting/dbConfig/clearData";
import route104 from "./routes/setting/dev/getSwitchAiDevTool";
import route105 from "./routes/setting/dev/updateSwitchAiDevTool";
import route106 from "./routes/setting/fileManagement/openFolder";
import route107 from "./routes/setting/getTextModel";
import route108 from "./routes/setting/loginConfig/getUser";
import route109 from "./routes/setting/loginConfig/updateUserPwd";
import route110 from "./routes/setting/memoryConfig/delAllMemory";
import route111 from "./routes/setting/memoryConfig/getMemory";
import route112 from "./routes/setting/memoryConfig/sureMemory";
import route113 from "./routes/setting/promptManage/getPrompt";
import route114 from "./routes/setting/promptManage/updatePrompt";
import route115 from "./routes/setting/skillManagement/getSkillContent";
import route116 from "./routes/setting/skillManagement/getSkillList";
import route117 from "./routes/setting/skillManagement/saveSkillContent";
import route118 from "./routes/setting/vendorConfig/addVendor";
import route119 from "./routes/setting/vendorConfig/deleteVendor";
import route120 from "./routes/setting/vendorConfig/enableEnglishVendor";
import route121 from "./routes/setting/vendorConfig/getCodeByLink";
import route122 from "./routes/setting/vendorConfig/getVendorList";
import route123 from "./routes/setting/vendorConfig/modelTest";
import route124 from "./routes/setting/vendorConfig/updateCode";
import route125 from "./routes/setting/vendorConfig/updateVendor";
import route126 from "./routes/task/getProject";
import route127 from "./routes/task/getTaskApi";
import route128 from "./routes/task/getTaskCategories";
import route129 from "./routes/task/taskDetails";
import route130 from "./routes/test/test";
import route81 from "./routes/project/deleteDirectorManual";
import route82 from "./routes/project/deleteVisualManual";
import route83 from "./routes/project/delProject";
import route84 from "./routes/project/editDirectorlManual";
import route85 from "./routes/project/editProject";
import route86 from "./routes/project/editVisualManual";
import route87 from "./routes/project/getProject";
import route88 from "./routes/project/getVisualManual";
import route89 from "./routes/project/queryDirectorManual";
import route90 from "./routes/project/visualManual";
import route91 from "./routes/script/addScript";
import route92 from "./routes/script/delScript";
import route93 from "./routes/script/exportScript";
import route94 from "./routes/script/extractAssets";
import route95 from "./routes/script/getScrptApi";
import route96 from "./routes/script/pollScriptAssets";
import route97 from "./routes/script/updateScript";
import route98 from "./routes/scriptAgent/getPlanData";
import route99 from "./routes/scriptAgent/setPlanData";
import route100 from "./routes/scriptAgent/updateData";
import route101 from "./routes/setting/about/checkUpdate";
import route102 from "./routes/setting/about/downloadApp";
import route103 from "./routes/setting/agentDeploy/agentSetKey";
import route104 from "./routes/setting/agentDeploy/deployAgentModel";
import route105 from "./routes/setting/agentDeploy/getAgentDeploy";
import route106 from "./routes/setting/dbConfig/clearData";
import route107 from "./routes/setting/dev/getSwitchAiDevTool";
import route108 from "./routes/setting/dev/updateSwitchAiDevTool";
import route109 from "./routes/setting/fileManagement/openFolder";
import route110 from "./routes/setting/getTextModel";
import route111 from "./routes/setting/loginConfig/getUser";
import route112 from "./routes/setting/loginConfig/updateUserPwd";
import route113 from "./routes/setting/memoryConfig/delAllMemory";
import route114 from "./routes/setting/memoryConfig/getMemory";
import route115 from "./routes/setting/memoryConfig/sureMemory";
import route116 from "./routes/setting/promptManage/getPrompt";
import route117 from "./routes/setting/promptManage/updatePrompt";
import route118 from "./routes/setting/skillManagement/getSkillContent";
import route119 from "./routes/setting/skillManagement/getSkillList";
import route120 from "./routes/setting/skillManagement/saveSkillContent";
import route121 from "./routes/setting/vendorConfig/addVendor";
import route122 from "./routes/setting/vendorConfig/deleteVendor";
import route123 from "./routes/setting/vendorConfig/enableEnglishVendor";
import route124 from "./routes/setting/vendorConfig/getCodeByLink";
import route125 from "./routes/setting/vendorConfig/getVendorList";
import route126 from "./routes/setting/vendorConfig/modelTest";
import route127 from "./routes/setting/vendorConfig/updateCode";
import route128 from "./routes/setting/vendorConfig/updateVendor";
import route129 from "./routes/task/getProject";
import route130 from "./routes/task/getTaskApi";
import route131 from "./routes/task/getTaskCategories";
import route132 from "./routes/task/taskDetails";
import route133 from "./routes/test/test";
export default async (app: Express) => {
app.use("/api/agents/clearMemory", route1);
@ -210,57 +213,60 @@ export default async (app: Express) => {
app.use("/api/production/workbench/getVideoList", route75);
app.use("/api/production/workbench/getVideoModelDetail", route76);
app.use("/api/production/workbench/selectVideo", route77);
app.use("/api/project/addProject", route78);
app.use("/api/project/addVisual", route79);
app.use("/api/project/addDirectorManual", route78);
app.use("/api/project/addProject", route79);
app.use("/api/project/addVisualManual", route80);
app.use("/api/project/deleteVisualManual", route81);
app.use("/api/project/delProject", route82);
app.use("/api/project/editProject", route83);
app.use("/api/project/editVisualManual", route84);
app.use("/api/project/getProject", route85);
app.use("/api/project/getVisualManual", route86);
app.use("/api/project/visualManual", route87);
app.use("/api/script/addScript", route88);
app.use("/api/script/delScript", route89);
app.use("/api/script/exportScript", route90);
app.use("/api/script/extractAssets", route91);
app.use("/api/script/getScrptApi", route92);
app.use("/api/script/pollScriptAssets", route93);
app.use("/api/script/updateScript", route94);
app.use("/api/scriptAgent/getPlanData", route95);
app.use("/api/scriptAgent/setPlanData", route96);
app.use("/api/scriptAgent/updateData", route97);
app.use("/api/setting/about/checkUpdate", route98);
app.use("/api/setting/about/downloadApp", route99);
app.use("/api/setting/agentDeploy/agentSetKey", route100);
app.use("/api/setting/agentDeploy/deployAgentModel", route101);
app.use("/api/setting/agentDeploy/getAgentDeploy", route102);
app.use("/api/setting/dbConfig/clearData", route103);
app.use("/api/setting/dev/getSwitchAiDevTool", route104);
app.use("/api/setting/dev/updateSwitchAiDevTool", route105);
app.use("/api/setting/fileManagement/openFolder", route106);
app.use("/api/setting/getTextModel", route107);
app.use("/api/setting/loginConfig/getUser", route108);
app.use("/api/setting/loginConfig/updateUserPwd", route109);
app.use("/api/setting/memoryConfig/delAllMemory", route110);
app.use("/api/setting/memoryConfig/getMemory", route111);
app.use("/api/setting/memoryConfig/sureMemory", route112);
app.use("/api/setting/promptManage/getPrompt", route113);
app.use("/api/setting/promptManage/updatePrompt", route114);
app.use("/api/setting/skillManagement/getSkillContent", route115);
app.use("/api/setting/skillManagement/getSkillList", route116);
app.use("/api/setting/skillManagement/saveSkillContent", route117);
app.use("/api/setting/vendorConfig/addVendor", route118);
app.use("/api/setting/vendorConfig/deleteVendor", route119);
app.use("/api/setting/vendorConfig/enableEnglishVendor", route120);
app.use("/api/setting/vendorConfig/getCodeByLink", route121);
app.use("/api/setting/vendorConfig/getVendorList", route122);
app.use("/api/setting/vendorConfig/modelTest", route123);
app.use("/api/setting/vendorConfig/updateCode", route124);
app.use("/api/setting/vendorConfig/updateVendor", route125);
app.use("/api/task/getProject", route126);
app.use("/api/task/getTaskApi", route127);
app.use("/api/task/getTaskCategories", route128);
app.use("/api/task/taskDetails", route129);
app.use("/api/test/test", route130);
app.use("/api/project/deleteDirectorManual", route81);
app.use("/api/project/deleteVisualManual", route82);
app.use("/api/project/delProject", route83);
app.use("/api/project/editDirectorlManual", route84);
app.use("/api/project/editProject", route85);
app.use("/api/project/editVisualManual", route86);
app.use("/api/project/getProject", route87);
app.use("/api/project/getVisualManual", route88);
app.use("/api/project/queryDirectorManual", route89);
app.use("/api/project/visualManual", route90);
app.use("/api/script/addScript", route91);
app.use("/api/script/delScript", route92);
app.use("/api/script/exportScript", route93);
app.use("/api/script/extractAssets", route94);
app.use("/api/script/getScrptApi", route95);
app.use("/api/script/pollScriptAssets", route96);
app.use("/api/script/updateScript", route97);
app.use("/api/scriptAgent/getPlanData", route98);
app.use("/api/scriptAgent/setPlanData", route99);
app.use("/api/scriptAgent/updateData", route100);
app.use("/api/setting/about/checkUpdate", route101);
app.use("/api/setting/about/downloadApp", route102);
app.use("/api/setting/agentDeploy/agentSetKey", route103);
app.use("/api/setting/agentDeploy/deployAgentModel", route104);
app.use("/api/setting/agentDeploy/getAgentDeploy", route105);
app.use("/api/setting/dbConfig/clearData", route106);
app.use("/api/setting/dev/getSwitchAiDevTool", route107);
app.use("/api/setting/dev/updateSwitchAiDevTool", route108);
app.use("/api/setting/fileManagement/openFolder", route109);
app.use("/api/setting/getTextModel", route110);
app.use("/api/setting/loginConfig/getUser", route111);
app.use("/api/setting/loginConfig/updateUserPwd", route112);
app.use("/api/setting/memoryConfig/delAllMemory", route113);
app.use("/api/setting/memoryConfig/getMemory", route114);
app.use("/api/setting/memoryConfig/sureMemory", route115);
app.use("/api/setting/promptManage/getPrompt", route116);
app.use("/api/setting/promptManage/updatePrompt", route117);
app.use("/api/setting/skillManagement/getSkillContent", route118);
app.use("/api/setting/skillManagement/getSkillList", route119);
app.use("/api/setting/skillManagement/saveSkillContent", route120);
app.use("/api/setting/vendorConfig/addVendor", route121);
app.use("/api/setting/vendorConfig/deleteVendor", route122);
app.use("/api/setting/vendorConfig/enableEnglishVendor", route123);
app.use("/api/setting/vendorConfig/getCodeByLink", route124);
app.use("/api/setting/vendorConfig/getVendorList", route125);
app.use("/api/setting/vendorConfig/modelTest", route126);
app.use("/api/setting/vendorConfig/updateCode", route127);
app.use("/api/setting/vendorConfig/updateVendor", route128);
app.use("/api/task/getProject", route129);
app.use("/api/task/getTaskApi", route130);
app.use("/api/task/getTaskCategories", route131);
app.use("/api/task/taskDetails", route132);
app.use("/api/test/test", route133);
}

View File

@ -0,0 +1,104 @@
import express from "express";
import u from "@/utils";
import { error, success } from "@/lib/responseFormat";
import fs from "fs";
import path from "path";
import { validateFields } from "@/middleware/middleware";
import { z } from "zod";
const router = express.Router();
// 新增导演手册
export default router.post(
"/",
validateFields({
name: z.string(),
images: z.array(z.string()),
directorManual: z.string(),
data: z.array(
z.object({
label: z.string(),
value: z.string(),
data: z.string(),
}),
),
}),
async (req, res) => {
try {
const { name, images, data, directorManual } = req.body as {
name: string;
images: string[];
data: { label: string; value: string; data: string }[];
directorManual: string;
};
if (/^\d+$/.test(directorManual)) {
res.status(400).send(error("文件名称不能为纯数字"));
return;
}
const mainPath = u.getPath(["skills", "story_skills", directorManual]);
if (fs.existsSync(mainPath)) {
return res.status(400).send(error("请勿填写重复名称的视觉手册"));
}
// 字段映射表(与 getVisualManual 保持一致)
const DATA_MAP: { value: string; subDir?: string }[] = [
{ value: "README" },
{ value: "art_character", subDir: "art_prompt" },
{ value: "art_character_derivative", subDir: "art_prompt" },
];
// 根据 DATA_MAP 构建 value -> subDir 的映射
const SUB_DIR_MAP = new Map(DATA_MAP.map(({ value, subDir }) => [value, subDir ?? ""]));
// 合法的 value 值集合,用于校验
const VALID_KEYS = new Set(DATA_MAP.map(({ value }) => value));
for (const item of data) {
if (!VALID_KEYS.has(item.value)) continue;
const subDir = SUB_DIR_MAP.get(item.value)!;
const dirArr = subDir ? [mainPath, subDir] : [mainPath];
const filePath = u.getPath([...dirArr, `${item.value}.md`]);
const fileDir = path.dirname(filePath);
// 目录不存在时递归创建
if (!fs.existsSync(fileDir)) {
fs.mkdirSync(fileDir, { recursive: true });
}
fs.writeFileSync(filePath, item.data, "utf-8");
}
const imagesDir = path.join(mainPath, "images");
let existingFiles: string[] = [];
try {
const allFiles = fs.readdirSync(imagesDir);
existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
} catch {}
const retainedFileNames = new Set(images.filter((item) => item.startsWith("http")).map((url) => path.basename(new URL(url).pathname)));
for (const file of existingFiles) {
if (!retainedFileNames.has(file)) {
const filePath = path.join(imagesDir, file);
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
}
}
if (!fs.existsSync(imagesDir)) {
fs.mkdirSync(imagesDir, { recursive: true });
}
for (const item of images) {
if (!item.startsWith("http")) {
const fileName = `${u.uuid()}.jpg`;
const targetPath = path.join(imagesDir, fileName);
const buffer = Buffer.from(item.replace(/^data:[^;]+;base64,/, ""), "base64");
fs.writeFileSync(targetPath, buffer);
}
}
res.status(200).send(success());
} catch (err) {
res.status(500).send({ error: String(err) });
}
},
);

View File

@ -1,85 +0,0 @@
import express from "express";
import u from "@/utils";
import { success } from "@/lib/responseFormat";
import fs from "fs";
import path from "path";
import { validateFields } from "@/middleware/middleware";
import { z } from "zod";
const router = express.Router();
// 新增视觉手册
export default router.post(
"/",
validateFields({
name: z.string(),
image: z.string(),
data: z.array(
z.object({
label: z.string(),
value: z.string(),
data: z.string(),
}),
),
}),
async (req, res) => {
try {
const { name, image, data } = req.body as {
name: string;
image: string;
data: { label: string; value: string; data: string }[];
};
const mainPath = u.getPath(["skills", "art_prompts", name]);
// 将 image 写入 mainPath/images/image 文件(无后缀)
if (image) {
const imagesDir = path.join(mainPath, "images");
if (!fs.existsSync(imagesDir)) {
fs.mkdirSync(imagesDir, { recursive: true });
}
fs.writeFileSync(path.join(imagesDir, "image"), image, "utf-8");
}
// 字段映射表(与 getVisualManual 保持一致)
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" },
];
// 根据 DATA_MAP 构建 value -> subDir 的映射
const SUB_DIR_MAP = new Map(DATA_MAP.map(({ value, subDir }) => [value, subDir ?? ""]));
// 合法的 value 值集合,用于校验
const VALID_KEYS = new Set(DATA_MAP.map(({ value }) => value));
for (const item of data) {
if (!VALID_KEYS.has(item.value)) continue;
const subDir = SUB_DIR_MAP.get(item.value)!;
const dirArr = subDir ? [mainPath, subDir] : [mainPath];
const filePath = u.getPath([...dirArr, `${item.value}.md`]);
const fileDir = path.dirname(filePath);
// 目录不存在时递归创建
if (!fs.existsSync(fileDir)) {
fs.mkdirSync(fileDir, { recursive: true });
}
fs.writeFileSync(filePath, item.data, "utf-8");
}
res.status(200).send(success());
} catch (err) {
res.status(500).send({ error: String(err) });
}
},
);

View File

@ -7,7 +7,7 @@ import { validateFields } from "@/middleware/middleware";
import { z } from "zod";
const router = express.Router();
// 编辑视觉手册
// 新增视觉手册
export default router.post(
"/",
validateFields({
@ -36,7 +36,7 @@ export default router.post(
return;
}
const mainPath = u.getPath(["skills", "art_prompts", stylePath]);
const mainPath = u.getPath(["skills", "art_skills", stylePath]);
if (fs.existsSync(mainPath)) {
return res.status(400).send(error("请勿填写重复名称的视觉手册"));
}

View File

@ -0,0 +1,41 @@
import express from "express";
import u from "@/utils";
import fs from "node:fs/promises";
import { z } from "zod";
import { error, success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
const router = express.Router();
// 删除导演手册
export default router.post(
"/",
validateFields({
name: z.string(),
}),
async (req, res) => {
try {
const { name } = req.body as { name: string };
// 安全校验:不允许包含路径分隔符、纯数字,防止越级删除或误删项目目录
if (name.includes("/") || name.includes("\\") || name === "." || name === ".." || /^\d+$/.test(name)) {
res.status(400).send(error("名称不能包含路径分隔符或为纯数字"));
return;
}
const artPromptsDir = u.getPath(["skills", "story_skills", name]);
try {
const stat = await fs.stat(artPromptsDir);
if (!stat.isDirectory()) {
throw new Error(`${artPromptsDir} 不是文件夹`);
}
await fs.rm(artPromptsDir, { recursive: true, force: true });
} catch (e) {
console.error("[删除视觉手册] 删除失败:", artPromptsDir, e);
}
res.status(200).send(success({ message: "删除成功" }));
} catch (err) {
res.status(500).send(error(u.error(err).message || "删除失败"));
}
},
);

View File

@ -22,7 +22,7 @@ export default router.post(
return;
}
const artPromptsDir = u.getPath(["skills", "art_prompts", name]);
const artPromptsDir = u.getPath(["skills", "art_skills", name]);
try {
const stat = await fs.stat(artPromptsDir);

View File

@ -0,0 +1,105 @@
import express from "express";
import u from "@/utils";
import { error, success } from "@/lib/responseFormat";
import fs from "fs";
import path from "path";
import { validateFields } from "@/middleware/middleware";
import { z } from "zod";
const router = express.Router();
// 编辑导演手册
export default router.post(
"/",
validateFields({
name: z.string(),
directorManual: z.string(),
images: z.array(z.string()),
data: z.array(
z.object({
label: z.string(),
value: z.string(),
data: z.string(),
}),
),
}),
async (req, res) => {
try {
const { name, directorManual, images, data } = req.body as {
name: string;
directorManual: string;
images: string[];
data: { label: string; value: string; data: string }[];
};
if (/^\d+$/.test(directorManual)) {
res.status(400).send(error("名称不能为纯数字"));
return;
}
const mainPath = u.getPath(["skills", "story_skills", directorManual]);
if (!fs.existsSync(mainPath)) {
return res.status(400).send(error("导演手册不存在"));
}
// 字段映射表(与 getVisualManual 保持一致)
const DATA_MAP: { value: string; subDir?: string }[] = [
{ value: "README" },
{ value: "art_character", subDir: "art_prompt" },
{ value: "art_character_derivative", subDir: "art_prompt" },
];
// 根据 DATA_MAP 构建 value -> subDir 的映射
const SUB_DIR_MAP = new Map(DATA_MAP.map(({ value, subDir }) => [value, subDir ?? ""]));
// 合法的 value 值集合,用于校验
const VALID_KEYS = new Set(DATA_MAP.map(({ value }) => value));
for (const item of data) {
if (!VALID_KEYS.has(item.value)) continue;
const subDir = SUB_DIR_MAP.get(item.value)!;
const dirArr = subDir ? [mainPath, subDir] : [mainPath];
const filePath = u.getPath([...dirArr, `${item.value}.md`]);
const fileDir = path.dirname(filePath);
// 目录不存在时递归创建
if (!fs.existsSync(fileDir)) {
fs.mkdirSync(fileDir, { recursive: true });
}
const content = item.value === "README" ? `${name}\n${item.data}` : item.data;
fs.writeFileSync(filePath, content, "utf-8");
}
const imagesDir = path.join(mainPath, "images");
let existingFiles: string[] = [];
try {
const allFiles = fs.readdirSync(imagesDir);
existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
} catch {}
const retainedFileNames = new Set(images.filter((item) => item.startsWith("http")).map((url) => path.basename(new URL(url).pathname)));
for (const file of existingFiles) {
if (!retainedFileNames.has(file)) {
const filePath = path.join(imagesDir, file);
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
}
}
if (!fs.existsSync(imagesDir)) {
fs.mkdirSync(imagesDir, { recursive: true });
}
for (const item of images) {
if (!item.startsWith("http")) {
const fileName = `${u.uuid()}.jpg`;
const targetPath = path.join(imagesDir, fileName);
const buffer = Buffer.from(item.replace(/^data:[^;]+;base64,/, ""), "base64");
fs.writeFileSync(targetPath, buffer);
}
}
res.status(200).send(success());
} catch (err) {
res.status(500).send({ error: String(err) });
}
},
);

View File

@ -36,7 +36,7 @@ export default router.post(
return;
}
const mainPath = u.getPath(["skills", "art_prompts", stylePath]);
const mainPath = u.getPath(["skills", "art_skills", stylePath]);
if (!fs.existsSync(mainPath)) {
return res.status(400).send(error("视觉手册不存在"));
}

View File

@ -33,9 +33,9 @@ function readMd(filePath: string): string {
// 获取 images 文件夹下所有图片文件路径列表
async function readAllImages(imagesDir: string) {
try {
const ossPath = u.getPath(path.join("skills", "art_prompts", imagesDir, "images"));
const ossPath = u.getPath(path.join("skills", "art_skills", imagesDir, "images"));
const files = fs.readdirSync(ossPath);
const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("art_prompts", imagesDir, "images", f));
const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("art_skills", imagesDir, "images", f));
if (images.length) {
return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i, "skills")));
} else {
@ -49,7 +49,7 @@ async function readAllImages(imagesDir: string) {
// 获取视觉手册
export default router.post("/", async (req, res) => {
try {
const artPromptsDir = u.getPath(["skills", "art_prompts"]);
const artPromptsDir = u.getPath(["skills", "art_skills"]);
// 读取所有风格文件夹
const styleDirs = fs

View File

@ -0,0 +1,84 @@
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: "art_character", subDir: "art_prompt" },
{ label: "角色", value: "art_character_derivative", subDir: "art_prompt" },
];
// 读取 md 文件内容,文件不存在时返回空字符串
function readMd(filePath: string): string {
try {
return fs.readFileSync(filePath, "utf-8");
} catch {
return "";
}
}
// 获取 images 文件夹下所有图片文件路径列表
async function readAllImages(imagesDir: string) {
try {
const ossPath = u.getPath(path.join("skills", "story_skills", imagesDir, "images"));
const files = fs.readdirSync(ossPath);
const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("story_skills", imagesDir, "images", f));
if (images.length) {
return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i, "skills")));
} else {
return [];
}
} catch {
return [];
}
}
// 获取导演手册
export default router.post("/", async (req, res) => {
try {
const artPromptsDir = u.getPath(["skills", "story_skills"]);
// 读取所有风格文件夹
const styleDirs = fs
.readdirSync(artPromptsDir, { withFileTypes: true })
.filter((d) => d.isDirectory())
.map((d) => d.name);
const result = await Promise.all(
styleDirs.map(async (directorManual) => {
const styleDir = path.join(artPromptsDir, directorManual);
const images = await readAllImages(directorManual);
const readmePath = path.join(styleDir, "README.md");
const readmeContent = fs.readFileSync(readmePath, "utf-8");
const firstLine = readmeContent.split("\n")[0].replace(/--/g, "");
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: firstLine,
image: images,
directorManual: directorManual,
data,
};
}),
);
res.status(200).send(success(result));
} catch (err) {
res.status(500).send({ error: String(err) });
}
});

View File

@ -1,6 +1,21 @@
// @db-hash 7af86e2bafe5cab7d175eb68cf76ed7a
// @db-hash 35cf00f711e9d4df398703de70511684
//该文件由脚本自动生成,请勿手动修改
export interface _o_project_old_20260402 {
'artStyle'?: string | null;
'createTime'?: number | null;
'id'?: number | null;
'imageModel'?: string | null;
'imageQuality'?: string | null;
'intro'?: string | null;
'mode'?: string | null;
'name'?: string | null;
'projectType'?: string | null;
'type'?: string | null;
'userId'?: number | null;
'videoModel'?: string | null;
'videoRatio'?: string | null;
}
export interface _o_storyboard_old_20260402 {
'createTime'?: number | null;
'duration'?: string | null;
@ -165,6 +180,7 @@ export interface o_outlineNovel {
export interface o_project {
'artStyle'?: string | null;
'createTime'?: number | null;
'directorManual'?: string | null;
'id'?: number | null;
'imageModel'?: string | null;
'imageQuality'?: string | null;
@ -285,6 +301,7 @@ export interface o_videoTrack {
}
export interface DB {
"_o_project_old_20260402": _o_project_old_20260402;
"_o_storyboard_old_20260402": _o_storyboard_old_20260402;
"_o_storyboard_old_20260402_1": _o_storyboard_old_20260402_1;
"_o_vendorConfig_old_20260401": _o_vendorConfig_old_20260401;