100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
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: 图片别名(如@图8),value: 图片ID(如456)
|
||
* "@图10": "123" // key: 图片别名(如@图10),value: 图片ID(如123)
|
||
* },
|
||
* "将@图10中圈起来的部分换成@图8"
|
||
* );
|
||
*/
|
||
export default async (images: Record<string, string>, directive: string, projectId: number) => {
|
||
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: "16:9",
|
||
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;
|
||
};
|