video-flow-toon/src/utils/editImage.ts
2026-02-28 16:36:38 +08:00

100 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import u from "@/utils";
import axios from "axios";
import { v4 as uuid } from "uuid";
async function getImageBase64ForId(imageId: string | number) {
const imagePath = await u
.db("t_assets")
.select("filePath")
.where({ id: Number(imageId) })
.first();
if (!imagePath || !imagePath.filePath) return ""; // 未找到图片路径
const url = await u.oss.getFileUrl(imagePath.filePath);
return await urlToBase64(url);
}
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}`;
}
// 将图片ID和指令转换为base64数组和替换后的指令
async function convertDirectiveAndImages(images: Record<string, string>, directive: string) {
// step1: 列出所有别名
const aliasList = Object.keys(images);
// step2: 在指令中提取所有 @别名出现
const aliasRegex = /@[\u4e00-\u9fa5\w]+/g;
const referencedAliases = directive.match(aliasRegex) || [];
// step3: 检查别名
for (const alias of referencedAliases) {
if (!(alias in images)) {
throw new Error(`您引用了不存在的图片:${alias}`);
}
}
// step4: 构建别名与顺序编号映射
const aliasToIndex: Record<string, number> = {};
aliasList.forEach((alias, i) => {
aliasToIndex[alias] = i + 1;
});
// step5: 替换指令中的别名为"图N"
let prompt = directive;
for (const [alias, idx] of Object.entries(aliasToIndex)) {
// 转义alias可能含特殊字符
const reg = new RegExp(alias.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1"), "g");
prompt = prompt.replace(reg, `${idx}`);
}
// step6: 依次获取图片 base64 内容区分id或者本身就是base64
const base64Images: string[] = [];
for (const imageVal of Object.values(images)) {
// 判断是否为base64串
const isBase64 = typeof imageVal === "string" && /^data:image\//.test(imageVal);
if (isBase64) {
base64Images.push(imageVal);
} else if (typeof imageVal === "number") {
const base64 = await getImageBase64ForId(imageVal);
base64Images.push(base64);
} else if (imageVal.includes("http")) {
const base64 = await urlToBase64(imageVal);
base64Images.push(base64);
}
}
return {
prompt,
images: base64Images,
};
}
/**
* 示例用法:
*
* editImages(
* {
* "@图8": "456", // key: 图片别名(如@图8value: 图片ID如456
* "@图10": "123" // key: 图片别名(如@图10value: 图片ID如123
* },
* "将@图10中圈起来的部分换成@图8"
* );
*/
export default async (images: Record<string, string>, directive: string, projectId: number, aspectRatio: string | null) => {
const { prompt, images: base64Images } = await convertDirectiveAndImages(images, directive);
const apiConfig = await u.getPromptAi("editImage");
const contentStr = await u.ai.image(
{
systemPrompt: "根据用户提供的具体修改指令,对上传的图片进行智能编辑。",
prompt: prompt,
imageBase64: base64Images,
aspectRatio: aspectRatio!,
size: "1K",
},
apiConfig,
);
const match = contentStr.match(/base64,([A-Za-z0-9+/=]+)/);
const buffer = Buffer.from(match && match.length >= 1 ? match[1]! : contentStr, "base64");
const filePath = `/${projectId}/storyboard/${uuid()}.jpg`;
await u.oss.writeFile(filePath, buffer);
return filePath;
};