101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import express from "express";
|
|
import u from "@/utils";
|
|
import { error, success } from "@/lib/responseFormat";
|
|
import { validateFields } from "@/middleware/middleware";
|
|
import { z } from "zod";
|
|
import { v4 } from "uuid";
|
|
import axios from "axios";
|
|
|
|
const router = express.Router();
|
|
|
|
// url转base64
|
|
async function urlToBase64(imageUrl: string): Promise<string> {
|
|
const response = await axios.get(imageUrl, { responseType: "arraybuffer" });
|
|
const contentType = response.headers["content-type"] || "image/png";
|
|
const base64 = Buffer.from(response.data, "binary").toString("base64");
|
|
return `data:${contentType};base64,${base64}`;
|
|
}
|
|
|
|
// 超分并保存到 oss
|
|
async function superResolutionAndSave(
|
|
src: string,
|
|
projectId: number,
|
|
videoRatio: string,
|
|
): Promise<{ ossPath: string; base64: string }> {
|
|
const contentStr = await u.ai.image({
|
|
aspectRatio: videoRatio,
|
|
size: "1K",
|
|
resType: "b64",
|
|
systemPrompt: "你的核心任务是将所给的图片超分到 1K ,不改变图片任何内容,仅改变分辨率",
|
|
prompt: "你的核心任务是将所给的图片超分到 1K ,不改变图片任何内容,仅改变分辨率",
|
|
imageBase64: [await urlToBase64(src)],
|
|
});
|
|
const match = contentStr.match(/base64,([A-Za-z0-9+/=]+)/);
|
|
const base64Str = match ? match[1] : contentStr;
|
|
const buffer = Buffer.from(base64Str, "base64");
|
|
const ossPath = `/${projectId}/chat/${v4()}.jpg`;
|
|
await u.oss.writeFile(ossPath, buffer);
|
|
return { ossPath, base64: `data:image/jpg;base64,${base64Str}` };
|
|
}
|
|
|
|
export default router.post(
|
|
"/",
|
|
validateFields({
|
|
projectId: z.number(),
|
|
scriptId: z.number().nullable(),
|
|
imageList: z.array(
|
|
z.object({
|
|
cells: z.array(
|
|
z.object({
|
|
id: z.string(),
|
|
prompt: z.string().optional(),
|
|
src: z.string(),
|
|
})
|
|
),
|
|
})
|
|
),
|
|
}),
|
|
async (req, res) => {
|
|
const { projectId, scriptId, imageList } = req.body;
|
|
const scriptData = await u.db("t_script").where("id", scriptId).select("content").first();
|
|
if (!scriptData) return res.status(500).send(error("剧本不存在"));
|
|
const projectData = await u.db("t_project").where({ id: +projectId }).select("artStyle", "videoRatio").first();
|
|
if (!projectData) return res.status(500).send(error("项目不存在"));
|
|
|
|
// 遍历处理每个分镜段
|
|
const processSegment = async (
|
|
segment: { cells: { id: string; src: string }[] }
|
|
) => {
|
|
// 超分所有 cell
|
|
const cellsWithSuperscore = await Promise.all(
|
|
segment.cells.map(async (cell) => {
|
|
const { ossPath } = await superResolutionAndSave(cell.src, projectId, projectData.videoRatio!);
|
|
return {
|
|
id: cell.id,
|
|
projectId,
|
|
scriptId,
|
|
filePath: ossPath, // oss 路径(未签名)
|
|
src: cell.src,
|
|
type: "分镜"
|
|
};
|
|
})
|
|
);
|
|
return cellsWithSuperscore;
|
|
};
|
|
|
|
// 处理每个段
|
|
const results = await Promise.allSettled(imageList.map(processSegment));
|
|
|
|
// 展开放回并签名 filePath
|
|
const flatList = await Promise.all(
|
|
results.flatMap((item: any) =>
|
|
(item.value as any[]).map(async (cell) => ({
|
|
...cell,
|
|
filePath: await u.oss.getFileUrl(cell.filePath ?? ""),
|
|
}))
|
|
)
|
|
);
|
|
res.status(200).send(success(flatList));
|
|
}
|
|
);
|