视觉手册图片放置问题,添加生成视频时长
This commit is contained in:
parent
2b38e46d74
commit
0717c4c884
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
27
src/app.ts
27
src/app.ts
@ -12,6 +12,7 @@ import fs from "fs";
|
|||||||
import u from "@/utils";
|
import u from "@/utils";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import socketInit from "@/socket/index";
|
import socketInit from "@/socket/index";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
@ -29,14 +30,28 @@ export default async function startServe(randomPort: Boolean = false) {
|
|||||||
app.use(express.json({ limit: "100mb" }));
|
app.use(express.json({ limit: "100mb" }));
|
||||||
app.use(express.urlencoded({ extended: true, limit: "100mb" }));
|
app.use(express.urlencoded({ extended: true, limit: "100mb" }));
|
||||||
|
|
||||||
|
|
||||||
// oss 静态资源
|
// oss 静态资源
|
||||||
const rootDir = u.getPath("oss");
|
const ossDir = u.getPath("oss");
|
||||||
if (!fs.existsSync(rootDir)) {
|
if (!fs.existsSync(ossDir)) {
|
||||||
fs.mkdirSync(rootDir, { recursive: true });
|
fs.mkdirSync(ossDir, { recursive: true });
|
||||||
}
|
}
|
||||||
console.log("文件目录:", rootDir);
|
console.log("文件目录:", ossDir);
|
||||||
app.use(express.static(rootDir));
|
app.use("/oss", express.static(ossDir));
|
||||||
|
// skills 静态资源
|
||||||
|
|
||||||
|
const skillsDir = u.getPath("skills");
|
||||||
|
if (!fs.existsSync(skillsDir)) {
|
||||||
|
fs.mkdirSync(skillsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
console.log("文件目录:", skillsDir);
|
||||||
|
// 只允许图片文件访问
|
||||||
|
app.use(
|
||||||
|
"/skills",
|
||||||
|
(req, res, next) => {
|
||||||
|
/\.(jpe?g|png|gif|webp|svg|ico|bmp)$/i.test(req.path) ? next() : res.status(403).end();
|
||||||
|
},
|
||||||
|
express.static(skillsDir),
|
||||||
|
);
|
||||||
|
|
||||||
// data/web 静态网站
|
// data/web 静态网站
|
||||||
const webDir = u.getPath("web");
|
const webDir = u.getPath("web");
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import path from "path";
|
|||||||
|
|
||||||
// 默认环境变量(当 env 文件不存在时自动创建)
|
// 默认环境变量(当 env 文件不存在时自动创建)
|
||||||
const defaultEnvValues: Record<string, string> = {
|
const defaultEnvValues: Record<string, string> = {
|
||||||
dev: `NODE_ENV=dev\nPORT=10588\nOSSURL=http://127.0.0.1:10588/`,
|
dev: `NODE_ENV=dev\nPORT=10588\nOSSURL=http://127.0.0.1:10588/oss/`,
|
||||||
prod: `NODE_ENV=prod\nPORT=10588\nOSSURL=http://127.0.0.1:10588/`,
|
prod: `NODE_ENV=prod\nPORT=10588\nOSSURL=http://127.0.0.1:10588/oss/`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 判断是否为打包后的 Electron 环境
|
// 判断是否为打包后的 Electron 环境
|
||||||
|
|||||||
@ -539,6 +539,7 @@ description: 专注于从剧本内容中提取所使用的资产(角色、场
|
|||||||
table.text("reason");
|
table.text("reason");
|
||||||
table.text("prompt");
|
table.text("prompt");
|
||||||
table.integer("selectVideoId");
|
table.integer("selectVideoId");
|
||||||
|
table.integer("duration");
|
||||||
table.primary(["id"]);
|
table.primary(["id"]);
|
||||||
table.unique(["id"]);
|
table.unique(["id"]);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -83,21 +83,47 @@ export default router.post(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
const result: ResultItem[] = Object.values(itemMap);
|
const result: ResultItem[] = Object.values(itemMap);
|
||||||
|
|
||||||
const typeConfig: Record<string, { promptKey: string; itemType: ItemType; label: string; nameLabel: string; visualManual: string }> = {
|
|
||||||
role: { promptKey: "role-polish", itemType: "characters", label: "角色标准四视图", nameLabel: "角色", visualManual: "art_character" },
|
|
||||||
scene: { promptKey: "scene-polish", itemType: "scenes", label: "场景图", nameLabel: "场景", visualManual: "art_scene" },
|
|
||||||
tool: { promptKey: "tool-polish", itemType: "props", label: "道具图", nameLabel: "道具", visualManual: "art_prop" },
|
|
||||||
};
|
|
||||||
// 批量更新所有 item 状态为生成中
|
// 批量更新所有 item 状态为生成中
|
||||||
const assetsIds = items.map((item: { assetsId: number }) => item.assetsId);
|
const assetsIds = items.map((item: { assetsId: number }) => item.assetsId);
|
||||||
await u.db("o_assets").whereIn("id", assetsIds).update({ promptState: "生成中" });
|
await u.db("o_assets").whereIn("id", assetsIds).update({ promptState: "生成中" });
|
||||||
|
//查询所有资产,用于判断每个资产是否是衍生资产
|
||||||
|
const assetsDataList = await u.db("o_assets").whereIn("id", assetsIds).select("id", "assetsId");
|
||||||
|
if (!assetsDataList || assetsDataList.length === 0) return res.status(500).send(error("资产不存在"));
|
||||||
|
const assetsDataMap = new Map(assetsDataList.map((a: any) => [a.id, a]));
|
||||||
|
|
||||||
|
const getTypeConfig = (
|
||||||
|
isDerivative: boolean,
|
||||||
|
): Record<string, { promptKey: string; itemType: ItemType; label: string; nameLabel: string; visualManual: string }> => ({
|
||||||
|
role: {
|
||||||
|
promptKey: "role-polish",
|
||||||
|
itemType: "characters",
|
||||||
|
label: "角色标准四视图",
|
||||||
|
nameLabel: "角色",
|
||||||
|
visualManual: isDerivative ? "art_character_derivative" : "art_character",
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
promptKey: "scene-polish",
|
||||||
|
itemType: "scenes",
|
||||||
|
label: "场景图",
|
||||||
|
nameLabel: "场景",
|
||||||
|
visualManual: isDerivative ? "art_scene_derivative" : "art_scene",
|
||||||
|
},
|
||||||
|
tool: {
|
||||||
|
promptKey: "tool-polish",
|
||||||
|
itemType: "props",
|
||||||
|
label: "道具图",
|
||||||
|
nameLabel: "道具",
|
||||||
|
visualManual: isDerivative ? "art_prop_derivative" : "art_prop",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 后台异步并发生成,不阻塞响应
|
// 后台异步并发生成,不阻塞响应
|
||||||
const limit = pLimit(concurrentCount ?? 1);
|
const limit = pLimit(concurrentCount ?? 1);
|
||||||
|
|
||||||
const tasks = items.map((item: { assetsId: number; type: string; name: string; describe: string }) =>
|
const tasks = items.map((item: { assetsId: number; type: string; name: string; describe: string }) =>
|
||||||
limit(async () => {
|
limit(async () => {
|
||||||
|
const assetData = assetsDataMap.get(item.assetsId);
|
||||||
|
if (!assetData) return;
|
||||||
|
const typeConfig = getTypeConfig(!!assetData.assetsId);
|
||||||
const config = typeConfig[item.type];
|
const config = typeConfig[item.type];
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
//获取到视觉手册
|
//获取到视觉手册
|
||||||
|
|||||||
@ -76,11 +76,31 @@ export default router.post(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const result: ResultItem[] = Object.values(itemMap);
|
const result: ResultItem[] = Object.values(itemMap);
|
||||||
|
//查询资产是否是衍生资产
|
||||||
|
const assetsData = await u.db("o_assets").where("id", assetsId).select("assetsId").first();
|
||||||
|
if (!assetsData) return { code: 500, message: "资产不存在" };
|
||||||
const typeConfig: Record<string, { promptKey: string; itemType: ItemType; label: string; nameLabel: string; visualManual: string }> = {
|
const typeConfig: Record<string, { promptKey: string; itemType: ItemType; label: string; nameLabel: string; visualManual: string }> = {
|
||||||
role: { promptKey: "role-polish", itemType: "characters", label: "角色标准四视图", nameLabel: "角色", visualManual: "art_character" },
|
role: {
|
||||||
scene: { promptKey: "scene-polish", itemType: "scenes", label: "场景图", nameLabel: "场景", visualManual: "art_scene" },
|
promptKey: "role-polish",
|
||||||
tool: { promptKey: "tool-polish", itemType: "props", label: "道具图", nameLabel: "道具", visualManual: "art_prop" },
|
itemType: "characters",
|
||||||
|
label: "角色标准四视图",
|
||||||
|
nameLabel: "角色",
|
||||||
|
visualManual: assetsData.assetsId ? "art_character_derivative" : "art_character",
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
promptKey: "scene-polish",
|
||||||
|
itemType: "scenes",
|
||||||
|
label: "场景图",
|
||||||
|
nameLabel: "场景",
|
||||||
|
visualManual: assetsData.assetsId ? "art_scene_derivative" : "art_scene",
|
||||||
|
},
|
||||||
|
tool: {
|
||||||
|
promptKey: "tool-polish",
|
||||||
|
itemType: "props",
|
||||||
|
label: "道具图",
|
||||||
|
nameLabel: "道具",
|
||||||
|
visualManual: assetsData.assetsId ? "art_prop_derivative" : "art_prop",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = typeConfig[type];
|
const config = typeConfig[type];
|
||||||
|
|||||||
@ -23,6 +23,7 @@ interface TrackItem {
|
|||||||
prompt: string;
|
prompt: string;
|
||||||
state: "未生成" | "生成中" | "已完成" | "生成失败";
|
state: "未生成" | "生成中" | "已完成" | "生成失败";
|
||||||
reason?: string;
|
reason?: string;
|
||||||
|
duration?: number;
|
||||||
selectVideoId?: number;
|
selectVideoId?: number;
|
||||||
medias: TrackMedia[];
|
medias: TrackMedia[];
|
||||||
videoList: VideoItem[];
|
videoList: VideoItem[];
|
||||||
@ -53,6 +54,7 @@ export default router.post(
|
|||||||
const item = trackData.find((t) => t.id === trackId);
|
const item = trackData.find((t) => t.id === trackId);
|
||||||
trackList.push({
|
trackList.push({
|
||||||
id: trackId,
|
id: trackId,
|
||||||
|
duration: item?.duration ?? 0,
|
||||||
prompt: item?.prompt || "",
|
prompt: item?.prompt || "",
|
||||||
state: (item?.state as "未生成" | "生成中" | "已完成" | "生成失败") ?? "未生成",
|
state: (item?.state as "未生成" | "生成中" | "已完成" | "生成失败") ?? "未生成",
|
||||||
reason: item?.reason ?? "",
|
reason: item?.reason ?? "",
|
||||||
|
|||||||
@ -76,11 +76,11 @@ export default router.post(
|
|||||||
}
|
}
|
||||||
fs.writeFileSync(filePath, item.data, "utf-8");
|
fs.writeFileSync(filePath, item.data, "utf-8");
|
||||||
}
|
}
|
||||||
const ossImagesDir = u.getPath(["oss", stylePath]);
|
const imagesDir = path.join(mainPath, "images");
|
||||||
|
|
||||||
let existingFiles: string[] = [];
|
let existingFiles: string[] = [];
|
||||||
try {
|
try {
|
||||||
const allFiles = fs.readdirSync(ossImagesDir);
|
const allFiles = fs.readdirSync(imagesDir);
|
||||||
existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
|
existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
@ -88,12 +88,22 @@ export default router.post(
|
|||||||
|
|
||||||
for (const file of existingFiles) {
|
for (const file of existingFiles) {
|
||||||
if (!retainedFileNames.has(file)) {
|
if (!retainedFileNames.has(file)) {
|
||||||
await u.oss.deleteFile(`${stylePath}/${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) {
|
for (const item of images) {
|
||||||
if (!item.startsWith("http")) await u.oss.writeFile(`${stylePath}/${u.uuid()}.jpg`, item);
|
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());
|
res.status(200).send(success());
|
||||||
|
|||||||
@ -33,14 +33,6 @@ export default router.post(
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[删除视觉手册] 删除失败:", artPromptsDir, e);
|
console.error("[删除视觉手册] 删除失败:", artPromptsDir, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 删除 oss 下的同名文件夹(存放图片),独立于 art_prompts 目录
|
|
||||||
try {
|
|
||||||
await u.oss.deleteDirectory(name);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("[删除视觉手册] oss 目录删除失败:", name, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).send(success({ message: "删除成功" }));
|
res.status(200).send(success({ message: "删除成功" }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.status(500).send(error(u.error(err).message || "删除失败"));
|
res.status(500).send(error(u.error(err).message || "删除失败"));
|
||||||
|
|||||||
@ -77,11 +77,11 @@ export default router.post(
|
|||||||
const content = item.value === "README" ? `${name}\n${item.data}` : item.data;
|
const content = item.value === "README" ? `${name}\n${item.data}` : item.data;
|
||||||
fs.writeFileSync(filePath, content, "utf-8");
|
fs.writeFileSync(filePath, content, "utf-8");
|
||||||
}
|
}
|
||||||
const ossImagesDir = u.getPath(["oss", stylePath]);
|
const imagesDir = path.join(mainPath, "images");
|
||||||
|
|
||||||
let existingFiles: string[] = [];
|
let existingFiles: string[] = [];
|
||||||
try {
|
try {
|
||||||
const allFiles = fs.readdirSync(ossImagesDir);
|
const allFiles = fs.readdirSync(imagesDir);
|
||||||
existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
|
existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
@ -89,12 +89,22 @@ export default router.post(
|
|||||||
|
|
||||||
for (const file of existingFiles) {
|
for (const file of existingFiles) {
|
||||||
if (!retainedFileNames.has(file)) {
|
if (!retainedFileNames.has(file)) {
|
||||||
await u.oss.deleteFile(`${stylePath}/${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) {
|
for (const item of images) {
|
||||||
if (!item.startsWith("http")) await u.oss.writeFile(`${stylePath}/${u.uuid()}.jpg`, item);
|
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());
|
res.status(200).send(success());
|
||||||
|
|||||||
@ -33,11 +33,11 @@ function readMd(filePath: string): string {
|
|||||||
// 获取 images 文件夹下所有图片文件路径列表
|
// 获取 images 文件夹下所有图片文件路径列表
|
||||||
async function readAllImages(imagesDir: string) {
|
async function readAllImages(imagesDir: string) {
|
||||||
try {
|
try {
|
||||||
const ossPath = u.getPath(["oss", imagesDir]);
|
const ossPath = u.getPath(path.join("skills", "art_prompts", imagesDir, "images"));
|
||||||
const files = fs.readdirSync(ossPath);
|
const files = fs.readdirSync(ossPath);
|
||||||
const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join(imagesDir, f));
|
const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("art_prompts", imagesDir, "images", f));
|
||||||
if (images.length) {
|
if (images.length) {
|
||||||
return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i)));
|
return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i, "skills")));
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -60,7 +60,6 @@ export default router.post("/", async (req, res) => {
|
|||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
styleDirs.map(async (styleName) => {
|
styleDirs.map(async (styleName) => {
|
||||||
const styleDir = path.join(artPromptsDir, styleName);
|
const styleDir = path.join(artPromptsDir, styleName);
|
||||||
|
|
||||||
const images = await readAllImages(styleName);
|
const images = await readAllImages(styleName);
|
||||||
const readmePath = path.join(styleDir, "README.md");
|
const readmePath = path.join(styleDir, "README.md");
|
||||||
const readmeContent = fs.readFileSync(readmePath, "utf-8");
|
const readmeContent = fs.readFileSync(readmePath, "utf-8");
|
||||||
|
|||||||
34
src/types/database.d.ts
vendored
34
src/types/database.d.ts
vendored
@ -1,6 +1,34 @@
|
|||||||
// @db-hash c0d74bd27b3a41b397705c93d1737a3b
|
// @db-hash a6017ee44d67db4a339664cfe7bacb76
|
||||||
//该文件由脚本自动生成,请勿手动修改
|
//该文件由脚本自动生成,请勿手动修改
|
||||||
|
|
||||||
|
export interface _o_vendorConfig_old_20260401 {
|
||||||
|
'author'?: string | null;
|
||||||
|
'code'?: string | null;
|
||||||
|
'createTime'?: number | null;
|
||||||
|
'description'?: string | null;
|
||||||
|
'icon'?: string | null;
|
||||||
|
'id'?: string;
|
||||||
|
'inputs'?: string | null;
|
||||||
|
'inputValues'?: string | null;
|
||||||
|
'models'?: string | null;
|
||||||
|
'name'?: string | null;
|
||||||
|
}
|
||||||
|
export interface _o_videoTrack_old_20260401 {
|
||||||
|
'id'?: number;
|
||||||
|
'projectId'?: number | null;
|
||||||
|
'scriptId'?: number | null;
|
||||||
|
'videoId'?: number | null;
|
||||||
|
}
|
||||||
|
export interface _o_videoTrack_old_20260401_1 {
|
||||||
|
'id'?: number;
|
||||||
|
'projectId'?: number | null;
|
||||||
|
'prompt'?: string | null;
|
||||||
|
'reason'?: string | null;
|
||||||
|
'scriptId'?: number | null;
|
||||||
|
'selectVideoId'?: number | null;
|
||||||
|
'state'?: string | null;
|
||||||
|
'videoId'?: number | null;
|
||||||
|
}
|
||||||
export interface memories {
|
export interface memories {
|
||||||
'content': string;
|
'content': string;
|
||||||
'createTime': number;
|
'createTime': number;
|
||||||
@ -216,6 +244,7 @@ export interface o_video {
|
|||||||
'videoTrackId'?: number | null;
|
'videoTrackId'?: number | null;
|
||||||
}
|
}
|
||||||
export interface o_videoTrack {
|
export interface o_videoTrack {
|
||||||
|
'duration'?: number | null;
|
||||||
'id'?: number;
|
'id'?: number;
|
||||||
'projectId'?: number | null;
|
'projectId'?: number | null;
|
||||||
'prompt'?: string | null;
|
'prompt'?: string | null;
|
||||||
@ -227,6 +256,9 @@ export interface o_videoTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DB {
|
export interface DB {
|
||||||
|
"_o_vendorConfig_old_20260401": _o_vendorConfig_old_20260401;
|
||||||
|
"_o_videoTrack_old_20260401": _o_videoTrack_old_20260401;
|
||||||
|
"_o_videoTrack_old_20260401_1": _o_videoTrack_old_20260401_1;
|
||||||
"memories": memories;
|
"memories": memories;
|
||||||
"o_agentDeploy": o_agentDeploy;
|
"o_agentDeploy": o_agentDeploy;
|
||||||
"o_agentWorkData": o_agentWorkData;
|
"o_agentWorkData": o_agentWorkData;
|
||||||
|
|||||||
@ -45,12 +45,13 @@ class OSS {
|
|||||||
* @param userRelPath 用户传入的相对文件路径(使用 / 作为分隔符)
|
* @param userRelPath 用户传入的相对文件路径(使用 / 作为分隔符)
|
||||||
* @returns 文件的 http 链接(本地服务地址)
|
* @returns 文件的 http 链接(本地服务地址)
|
||||||
*/
|
*/
|
||||||
async getFileUrl(userRelPath: string): Promise<string> {
|
async getFileUrl(userRelPath: string, prefix?: string): Promise<string> {
|
||||||
|
if (!prefix) prefix = "oss";
|
||||||
await this.ensureInit();
|
await this.ensureInit();
|
||||||
const safePath = normalizeUserPath(userRelPath);
|
const safePath = normalizeUserPath(userRelPath);
|
||||||
// URL 始终使用 /,所以这里需要将系统分隔符转回 /
|
// URL 始终使用 /,所以这里需要将系统分隔符转回 /
|
||||||
let url = process.env.OSSURL || `http://127.0.0.1:10588/`;
|
let url = `${process.env.OSSURL}${prefix}/` || `http://127.0.0.1:10588/${prefix}/`;
|
||||||
if (isEletron()) url = `http://localhost:${process.env.PORT}/`;
|
if (isEletron()) url = `http://localhost:${process.env.PORT}/${prefix}/`;
|
||||||
return `${url}${safePath.split(path.sep).join("/")}`;
|
return `${url}${safePath.split(path.sep).join("/")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,10 +147,7 @@ class OSS {
|
|||||||
await fs.mkdir(path.dirname(absPath), { recursive: true });
|
await fs.mkdir(path.dirname(absPath), { recursive: true });
|
||||||
// 如果 data 是 string,则视为 base64 编码,先解码再写入
|
// 如果 data 是 string,则视为 base64 编码,先解码再写入
|
||||||
// 自动去除可能存在的 Data URL 前缀(如 "data:image/png;base64,")
|
// 自动去除可能存在的 Data URL 前缀(如 "data:image/png;base64,")
|
||||||
const buffer =
|
const buffer = typeof data === "string" ? Buffer.from(data.replace(/^data:[^;]+;base64,/, ""), "base64") : data;
|
||||||
typeof data === "string"
|
|
||||||
? Buffer.from(data.replace(/^data:[^;]+;base64,/, ""), "base64")
|
|
||||||
: data;
|
|
||||||
await fs.writeFile(absPath, buffer);
|
await fs.writeFile(absPath, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user