修复已知问题,生成视频新增错误原因
This commit is contained in:
parent
9ea44ebd87
commit
7c02beed2b
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "toonflow-app",
|
"name": "toonflow-app",
|
||||||
"version": "1.0.6-dev",
|
"version": "1.0.6-dev2",
|
||||||
"description": "Toonflow 是一款 AI 短剧漫剧工具,能够利用 AI 技术将小说自动转化为剧本,并结合 AI 生成的图片和视频,实现高效的短剧创作。",
|
"description": "Toonflow 是一款 AI 短剧漫剧工具,能够利用 AI 技术将小说自动转化为剧本,并结合 AI 生成的图片和视频,实现高效的短剧创作。",
|
||||||
"author": "HBAI-Ltd <ltlctools@outlook.com>",
|
"author": "HBAI-Ltd <ltlctools@outlook.com>",
|
||||||
"homepage": "https://github.com/HBAI-Ltd/Toonflow-app#readme",
|
"homepage": "https://github.com/HBAI-Ltd/Toonflow-app#readme",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -6,6 +6,8 @@ import { z } from "zod";
|
|||||||
import type { DB } from "@/types/database";
|
import type { DB } from "@/types/database";
|
||||||
import generateImageTool from "./generateImageTool";
|
import generateImageTool from "./generateImageTool";
|
||||||
import imageSplitting from "./imageSplitting";
|
import imageSplitting from "./imageSplitting";
|
||||||
|
import path from "path";
|
||||||
|
import sharp from "sharp";
|
||||||
|
|
||||||
// ==================== 类型定义 ====================
|
// ==================== 类型定义 ====================
|
||||||
|
|
||||||
@ -240,9 +242,11 @@ ${sections.join("\n\n")}
|
|||||||
const skipped: number[] = [];
|
const skipped: number[] = [];
|
||||||
|
|
||||||
for (const item of shots) {
|
for (const item of shots) {
|
||||||
const exists = this.shots.some((f) => f.segmentId === item.segmentIndex);
|
const resultIndex = item.segmentIndex - 1;
|
||||||
|
|
||||||
|
const exists = this.shots.some((f) => f.segmentId === resultIndex);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
skipped.push(item.segmentIndex);
|
skipped.push(resultIndex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 分配独立的分镜ID
|
// 分配独立的分镜ID
|
||||||
@ -250,15 +254,15 @@ ${sections.join("\n\n")}
|
|||||||
const shotId = this.shotIdCounter;
|
const shotId = this.shotIdCounter;
|
||||||
this.shots.push({
|
this.shots.push({
|
||||||
id: shotId,
|
id: shotId,
|
||||||
segmentId: item.segmentIndex,
|
segmentId: resultIndex,
|
||||||
title: `分镜 ${shotId}`,
|
title: `分镜 ${shotId}`,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
cells: item.prompts.map((prompt) => ({ id: u.uuid(), prompt })),
|
cells: item.prompts.map((prompt) => ({ id: u.uuid(), prompt })),
|
||||||
fragmentContent: this.segments[item.segmentIndex]?.description,
|
fragmentContent: this.segments[resultIndex]?.description,
|
||||||
assetsTags: item.assetsTags,
|
assetsTags: item.assetsTags,
|
||||||
});
|
});
|
||||||
added.push({ id: shotId, segmentIndex: item.segmentIndex });
|
added.push({ id: shotId, segmentIndex: resultIndex });
|
||||||
}
|
}
|
||||||
|
|
||||||
const addedInfo = added.map((a) => `分镜${a.id}(片段${a.segmentIndex})`).join(", ");
|
const addedInfo = added.map((a) => `分镜${a.id}(片段${a.segmentIndex})`).join(", ");
|
||||||
@ -442,7 +446,6 @@ ${sections.join("\n\n")}
|
|||||||
this.scriptId,
|
this.scriptId,
|
||||||
this.projectId,
|
this.projectId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 通知前端正在分割图片
|
// 通知前端正在分割图片
|
||||||
this.emit("shotImageGenerateProgress", { shotId, status: "splitting", message: "正在分割宫格图片为单张镜头图" });
|
this.emit("shotImageGenerateProgress", { shotId, status: "splitting", message: "正在分割宫格图片为单张镜头图" });
|
||||||
|
|
||||||
|
|||||||
@ -151,6 +151,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
|
|||||||
table.text("firstFrame");
|
table.text("firstFrame");
|
||||||
table.text("storyboardImgs");
|
table.text("storyboardImgs");
|
||||||
table.text("model");
|
table.text("model");
|
||||||
|
table.text("errorReason");
|
||||||
table.integer("time");
|
table.integer("time");
|
||||||
table.integer("state");
|
table.integer("state");
|
||||||
table.integer("scriptId");
|
table.integer("scriptId");
|
||||||
|
|||||||
110
src/router.ts
110
src/router.ts
@ -1,4 +1,4 @@
|
|||||||
// @routes-hash df8fc2bdd69f6bbf900ca75396098390
|
// @routes-hash 3cfad40b3c8658b442ab766a9323d740
|
||||||
import { Express } from "express";
|
import { Express } from "express";
|
||||||
|
|
||||||
import route1 from "./routes/assets/addAssets";
|
import route1 from "./routes/assets/addAssets";
|
||||||
@ -50,32 +50,34 @@ import route46 from "./routes/setting/delModel";
|
|||||||
import route47 from "./routes/setting/getAiModelMap";
|
import route47 from "./routes/setting/getAiModelMap";
|
||||||
import route48 from "./routes/setting/getLog";
|
import route48 from "./routes/setting/getLog";
|
||||||
import route49 from "./routes/setting/getSetting";
|
import route49 from "./routes/setting/getSetting";
|
||||||
import route50 from "./routes/setting/updeteModel";
|
import route50 from "./routes/setting/getVideoModelList";
|
||||||
import route51 from "./routes/storyboard/batchSuperScoreImage";
|
import route51 from "./routes/setting/updateModel";
|
||||||
import route52 from "./routes/storyboard/chatStoryboard";
|
import route52 from "./routes/setting/updeteModel";
|
||||||
import route53 from "./routes/storyboard/generateShotImage";
|
import route53 from "./routes/storyboard/batchSuperScoreImage";
|
||||||
import route54 from "./routes/storyboard/generateStoryboardApi";
|
import route54 from "./routes/storyboard/chatStoryboard";
|
||||||
import route55 from "./routes/storyboard/generateVideoPrompt";
|
import route55 from "./routes/storyboard/generateShotImage";
|
||||||
import route56 from "./routes/storyboard/getStoryboard";
|
import route56 from "./routes/storyboard/generateStoryboardApi";
|
||||||
import route57 from "./routes/storyboard/keepStoryboard";
|
import route57 from "./routes/storyboard/generateVideoPrompt";
|
||||||
import route58 from "./routes/storyboard/saveStoryboard";
|
import route58 from "./routes/storyboard/getStoryboard";
|
||||||
import route59 from "./routes/storyboard/uploadImage";
|
import route59 from "./routes/storyboard/keepStoryboard";
|
||||||
import route60 from "./routes/task/getTaskApi";
|
import route60 from "./routes/storyboard/saveStoryboard";
|
||||||
import route61 from "./routes/task/taskDetails";
|
import route61 from "./routes/storyboard/uploadImage";
|
||||||
import route62 from "./routes/user/getUser";
|
import route62 from "./routes/task/getTaskApi";
|
||||||
import route63 from "./routes/video/addVideo";
|
import route63 from "./routes/task/taskDetails";
|
||||||
import route64 from "./routes/video/addVideoConfig";
|
import route64 from "./routes/user/getUser";
|
||||||
import route65 from "./routes/video/deleteVideoConfig";
|
import route65 from "./routes/video/addVideo";
|
||||||
import route66 from "./routes/video/generatePrompt";
|
import route66 from "./routes/video/addVideoConfig";
|
||||||
import route67 from "./routes/video/generateVideo";
|
import route67 from "./routes/video/deleteVideoConfig";
|
||||||
import route68 from "./routes/video/getManufacturer";
|
import route68 from "./routes/video/generatePrompt";
|
||||||
import route69 from "./routes/video/getVideo";
|
import route69 from "./routes/video/generateVideo";
|
||||||
import route70 from "./routes/video/getVideoConfigs";
|
import route70 from "./routes/video/getManufacturer";
|
||||||
import route71 from "./routes/video/getVideoModel";
|
import route71 from "./routes/video/getVideo";
|
||||||
import route72 from "./routes/video/getVideoStoryboards";
|
import route72 from "./routes/video/getVideoConfigs";
|
||||||
import route73 from "./routes/video/reviseVideoStoryboards";
|
import route73 from "./routes/video/getVideoModel";
|
||||||
import route74 from "./routes/video/saveVideo";
|
import route74 from "./routes/video/getVideoStoryboards";
|
||||||
import route75 from "./routes/video/upDateVideoConfig";
|
import route75 from "./routes/video/reviseVideoStoryboards";
|
||||||
|
import route76 from "./routes/video/saveVideo";
|
||||||
|
import route77 from "./routes/video/upDateVideoConfig";
|
||||||
|
|
||||||
export default async (app: Express) => {
|
export default async (app: Express) => {
|
||||||
app.use("/assets/addAssets", route1);
|
app.use("/assets/addAssets", route1);
|
||||||
@ -127,30 +129,32 @@ export default async (app: Express) => {
|
|||||||
app.use("/setting/getAiModelMap", route47);
|
app.use("/setting/getAiModelMap", route47);
|
||||||
app.use("/setting/getLog", route48);
|
app.use("/setting/getLog", route48);
|
||||||
app.use("/setting/getSetting", route49);
|
app.use("/setting/getSetting", route49);
|
||||||
app.use("/setting/updeteModel", route50);
|
app.use("/setting/getVideoModelList", route50);
|
||||||
app.use("/storyboard/batchSuperScoreImage", route51);
|
app.use("/setting/updateModel", route51);
|
||||||
app.use("/storyboard/chatStoryboard", route52);
|
app.use("/setting/updeteModel", route52);
|
||||||
app.use("/storyboard/generateShotImage", route53);
|
app.use("/storyboard/batchSuperScoreImage", route53);
|
||||||
app.use("/storyboard/generateStoryboardApi", route54);
|
app.use("/storyboard/chatStoryboard", route54);
|
||||||
app.use("/storyboard/generateVideoPrompt", route55);
|
app.use("/storyboard/generateShotImage", route55);
|
||||||
app.use("/storyboard/getStoryboard", route56);
|
app.use("/storyboard/generateStoryboardApi", route56);
|
||||||
app.use("/storyboard/keepStoryboard", route57);
|
app.use("/storyboard/generateVideoPrompt", route57);
|
||||||
app.use("/storyboard/saveStoryboard", route58);
|
app.use("/storyboard/getStoryboard", route58);
|
||||||
app.use("/storyboard/uploadImage", route59);
|
app.use("/storyboard/keepStoryboard", route59);
|
||||||
app.use("/task/getTaskApi", route60);
|
app.use("/storyboard/saveStoryboard", route60);
|
||||||
app.use("/task/taskDetails", route61);
|
app.use("/storyboard/uploadImage", route61);
|
||||||
app.use("/user/getUser", route62);
|
app.use("/task/getTaskApi", route62);
|
||||||
app.use("/video/addVideo", route63);
|
app.use("/task/taskDetails", route63);
|
||||||
app.use("/video/addVideoConfig", route64);
|
app.use("/user/getUser", route64);
|
||||||
app.use("/video/deleteVideoConfig", route65);
|
app.use("/video/addVideo", route65);
|
||||||
app.use("/video/generatePrompt", route66);
|
app.use("/video/addVideoConfig", route66);
|
||||||
app.use("/video/generateVideo", route67);
|
app.use("/video/deleteVideoConfig", route67);
|
||||||
app.use("/video/getManufacturer", route68);
|
app.use("/video/generatePrompt", route68);
|
||||||
app.use("/video/getVideo", route69);
|
app.use("/video/generateVideo", route69);
|
||||||
app.use("/video/getVideoConfigs", route70);
|
app.use("/video/getManufacturer", route70);
|
||||||
app.use("/video/getVideoModel", route71);
|
app.use("/video/getVideo", route71);
|
||||||
app.use("/video/getVideoStoryboards", route72);
|
app.use("/video/getVideoConfigs", route72);
|
||||||
app.use("/video/reviseVideoStoryboards", route73);
|
app.use("/video/getVideoModel", route73);
|
||||||
app.use("/video/saveVideo", route74);
|
app.use("/video/getVideoStoryboards", route74);
|
||||||
app.use("/video/upDateVideoConfig", route75);
|
app.use("/video/reviseVideoStoryboards", route75);
|
||||||
|
app.use("/video/saveVideo", route76);
|
||||||
|
app.use("/video/upDateVideoConfig", route77);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ const router = express.Router();
|
|||||||
|
|
||||||
export default router.post("/", async (req, res) => {
|
export default router.post("/", async (req, res) => {
|
||||||
const userId = 1;
|
const userId = 1;
|
||||||
const configData = await u.db("t_config").where("userId", userId).select("*");
|
const configData = await u.db("t_config").where("type","<>","video").where("userId", userId).select("*");
|
||||||
|
|
||||||
res.status(200).send(success(configData));
|
res.status(200).send(success(configData));
|
||||||
});
|
});
|
||||||
|
|||||||
11
src/routes/setting/getVideoModelList.ts
Normal file
11
src/routes/setting/getVideoModelList.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import express from "express";
|
||||||
|
import u from "@/utils";
|
||||||
|
import { success } from "@/lib/responseFormat";
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
export default router.post("/", async (req, res) => {
|
||||||
|
const userId = 1;
|
||||||
|
const configData = await u.db("t_config").where("type","video").where("userId", userId).select("*");
|
||||||
|
|
||||||
|
res.status(200).send(success(configData));
|
||||||
|
});
|
||||||
32
src/routes/setting/updateModel.ts
Normal file
32
src/routes/setting/updateModel.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import express from "express";
|
||||||
|
import u from "@/utils";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { success } from "@/lib/responseFormat";
|
||||||
|
import { validateFields } from "@/middleware/middleware";
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
export default router.post(
|
||||||
|
"/",
|
||||||
|
validateFields({
|
||||||
|
id: z.number(),
|
||||||
|
type: z.enum(["text", "video", "image"]),
|
||||||
|
model: z.string(),
|
||||||
|
baseUrl: z.string(),
|
||||||
|
modelType: z.string(),
|
||||||
|
apiKey: z.string(),
|
||||||
|
manufacturer: z.string(),
|
||||||
|
}),
|
||||||
|
async (req, res) => {
|
||||||
|
const { id, type, model, baseUrl, apiKey, manufacturer, modelType } = req.body;
|
||||||
|
|
||||||
|
await u.db("t_config").where("id", id).update({
|
||||||
|
type,
|
||||||
|
model,
|
||||||
|
baseUrl,
|
||||||
|
apiKey,
|
||||||
|
manufacturer,
|
||||||
|
modelType,
|
||||||
|
});
|
||||||
|
res.status(200).send(success("编辑成功"));
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -196,7 +196,10 @@ ${prompt}
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`视频生成失败 videoId=${videoId}:`, err);
|
console.error(`视频生成失败 videoId=${videoId}:`, err);
|
||||||
await u.db("t_video").where("id", videoId).update({ state: -1 });
|
await u
|
||||||
|
.db("t_video")
|
||||||
|
.where("id", videoId)
|
||||||
|
.update({ state: -1, errorReason: u.error(err).message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export default router.post(
|
|||||||
qb.whereIn("id", specifyIds);
|
qb.whereIn("id", specifyIds);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.select("id", "configId", "time", "resolution", "prompt", "firstFrame", "filePath", "storyboardImgs", "model", "scriptId", "state");
|
.select("id", "configId", "time", "resolution", "prompt", "firstFrame", "filePath", "storyboardImgs", "model", "scriptId", "state","errorReason");
|
||||||
// const videoIds: number[] = videos.map((video: any) => (typeof video.id === "string" ? parseInt(video.id) : video.id));
|
// const videoIds: number[] = videos.map((video: any) => (typeof video.id === "string" ? parseInt(video.id) : video.id));
|
||||||
|
|
||||||
// let tempAssets: TempAsset[] = await u
|
// let tempAssets: TempAsset[] = await u
|
||||||
|
|||||||
18
src/types/database.d.ts
vendored
18
src/types/database.d.ts
vendored
@ -1,6 +1,20 @@
|
|||||||
// @db-hash b175910ce89abacc2636f298095b06c3
|
// @db-hash 5de69fa13b58ac3b447664cb6faa9e8a
|
||||||
//该文件由脚本自动生成,请勿手动修改
|
//该文件由脚本自动生成,请勿手动修改
|
||||||
|
|
||||||
|
export interface _t_video_old_20260210 {
|
||||||
|
'aiConfigId'?: number | null;
|
||||||
|
'configId'?: number | null;
|
||||||
|
'filePath'?: string | null;
|
||||||
|
'firstFrame'?: string | null;
|
||||||
|
'id'?: number;
|
||||||
|
'model'?: string | null;
|
||||||
|
'prompt'?: string | null;
|
||||||
|
'resolution'?: string | null;
|
||||||
|
'scriptId'?: number | null;
|
||||||
|
'state'?: number | null;
|
||||||
|
'storyboardImgs'?: string | null;
|
||||||
|
'time'?: number | null;
|
||||||
|
}
|
||||||
export interface t_aiModelMap {
|
export interface t_aiModelMap {
|
||||||
'configId'?: number | null;
|
'configId'?: number | null;
|
||||||
'id'?: number;
|
'id'?: number;
|
||||||
@ -125,6 +139,7 @@ export interface t_user {
|
|||||||
export interface t_video {
|
export interface t_video {
|
||||||
'aiConfigId'?: number | null;
|
'aiConfigId'?: number | null;
|
||||||
'configId'?: number | null;
|
'configId'?: number | null;
|
||||||
|
'errorReason'?: string | null;
|
||||||
'filePath'?: string | null;
|
'filePath'?: string | null;
|
||||||
'firstFrame'?: string | null;
|
'firstFrame'?: string | null;
|
||||||
'id'?: number;
|
'id'?: number;
|
||||||
@ -156,6 +171,7 @@ export interface t_videoConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DB {
|
export interface DB {
|
||||||
|
"_t_video_old_20260210": _t_video_old_20260210;
|
||||||
"t_aiModelMap": t_aiModelMap;
|
"t_aiModelMap": t_aiModelMap;
|
||||||
"t_assets": t_assets;
|
"t_assets": t_assets;
|
||||||
"t_chatHistory": t_chatHistory;
|
"t_chatHistory": t_chatHistory;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import "../type";
|
import "../type";
|
||||||
import { generateImage, generateText, ModelMessage } from "ai";
|
import { generateImage, generateText, ModelMessage } from "ai";
|
||||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
export default async (input: ImageConfig, config: AIConfig): Promise<string> => {
|
export default async (input: ImageConfig, config: AIConfig): Promise<string> => {
|
||||||
if (!config.model) throw new Error("缺少Model名称");
|
if (!config.model) throw new Error("缺少Model名称");
|
||||||
@ -67,11 +68,26 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
|
|||||||
console.error(JSON.stringify(result.response, null, 2));
|
console.error(JSON.stringify(result.response, null, 2));
|
||||||
throw new Error("图片生成失败");
|
throw new Error("图片生成失败");
|
||||||
}
|
}
|
||||||
const match = result.text.match(/base64,([A-Za-z0-9+/=]+)/);
|
const mdMatch = result.text.match(/^!\[.*?\]\((.+?)\)$/);
|
||||||
const base64Str = match && match[1] ? match[1] : result.text;
|
if (mdMatch) {
|
||||||
|
const imgInfo = mdMatch[1];
|
||||||
// 返回生成的图片 base64
|
const base64InMd = imgInfo.match(/data:image\/[a-z]+;base64,(.+)/);
|
||||||
return "data:image/jpeg;base64," + base64Str!;
|
if (base64InMd) {
|
||||||
|
return imgInfo;
|
||||||
|
} else {
|
||||||
|
return await urlToBase64(imgInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const base64Match = result.text.match(/base64,([A-Za-z0-9+/=]+)/);
|
||||||
|
if (base64Match) {
|
||||||
|
return "data:image/jpeg;base64," + base64Match[1];
|
||||||
|
}
|
||||||
|
// 检查是否为图片直链 url
|
||||||
|
if (/^https?:\/\/.*\.(png|jpg|jpeg|gif|webp|bmp)$/i.test(result.text)) {
|
||||||
|
return await urlToBase64(result.text);
|
||||||
|
}
|
||||||
|
// 默认情况
|
||||||
|
return result.text;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { image } = await generateImage({
|
const { image } = await generateImage({
|
||||||
@ -87,3 +103,10 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
|
|||||||
return image.base64;
|
return image.base64;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function urlToBase64(url: string): Promise<string> {
|
||||||
|
const res = await axios.get(url, { responseType: "arraybuffer" });
|
||||||
|
const base64 = Buffer.from(res.data).toString("base64");
|
||||||
|
const mimeType = res.headers["content-type"] || "image/png";
|
||||||
|
return `data:${mimeType};base64,${base64}`;
|
||||||
|
}
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import sharp from "sharp";
|
|||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
import { pollTask, validateVideoConfig } from "@/utils/ai/utils";
|
import { pollTask, validateVideoConfig } from "@/utils/ai/utils";
|
||||||
import { createOpenAI } from "@ai-sdk/openai";
|
import { createOpenAI } from "@ai-sdk/openai";
|
||||||
import { experimental_generateVideo as generateVideo } from "ai";
|
|
||||||
export default async (input: VideoConfig, config: AIConfig) => {
|
export default async (input: VideoConfig, config: AIConfig) => {
|
||||||
if (!config.apiKey) throw new Error("缺少API Key");
|
if (!config.apiKey) throw new Error("缺少API Key");
|
||||||
if (!config.baseURL) throw new Error("缺少baseURL");
|
if (!config.baseURL) throw new Error("缺少baseURL");
|
||||||
// const { owned, images, hasTextType } = validateVideoConfig(input, config);
|
// const { owned, images, hasTextType } = validateVideoConfig(input, config);
|
||||||
|
|
||||||
const [requestUrl, queryUrl] = config.baseURL.split("|");
|
const [requestUrl, queryUrl] = config.baseURL.split("|");
|
||||||
|
|
||||||
const authorization = `Bearer ${config.apiKey}`;
|
const authorization = `Bearer ${config.apiKey}`;
|
||||||
@ -20,39 +21,30 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
|||||||
|
|
||||||
// 根据 aspectRatio 设置 size
|
// 根据 aspectRatio 设置 size
|
||||||
const sizeMap: Record<string, string> = {
|
const sizeMap: Record<string, string> = {
|
||||||
"16:9": "1280x720",
|
"16:9": "1920x1080",
|
||||||
"9:16": "720x1280",
|
"9:16": "1080x1920",
|
||||||
};
|
};
|
||||||
formData.append("size", sizeMap[input.aspectRatio] || "1920x1080");
|
formData.append("size", sizeMap[input.aspectRatio] || "1920x1080");
|
||||||
|
|
||||||
if (input.imageBase64 && input.imageBase64.length) {
|
if (input.imageBase64 && input.imageBase64.length) {
|
||||||
const base64Data = input.imageBase64[0]!.replace(/^data:image\/\w+;base64,/, "");
|
const base64Data = input.imageBase64[0]!.replace(/^data:image\/\w+;base64,/, "");
|
||||||
const buffer = Buffer.from(base64Data, "base64");
|
const buffer = Buffer.from(base64Data, "base64");
|
||||||
formData.append("input_reference", buffer, { filename: "image.jpg", contentType: "image/jpeg" });
|
formData.append("input_reference", buffer, { filename: "image.jpg", contentType: "image/jpeg" });
|
||||||
}
|
}
|
||||||
|
const { data } = await axios.post(requestUrl, formData, {
|
||||||
const body = {
|
headers: { "Content-Type": "application/json", Authorization: authorization, ...formData.getHeaders() },
|
||||||
model: "sora-2-all",
|
});
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: input.prompt,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const { data } = await axios.post(
|
|
||||||
"https://api2.aigcbest.top/v1/chat/completions",
|
|
||||||
{ ...body },
|
|
||||||
{
|
|
||||||
headers: { "Content-Type": "application/json", Authorization: authorization },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
if (data.status === "FAILED") throw new Error(`任务提交失败: ${data.errorMessage || "未知错误"}`);
|
if (data.status === "FAILED") throw new Error(`任务提交失败: ${data.errorMessage || "未知错误"}`);
|
||||||
|
const taskId = data.id;
|
||||||
|
return await pollTask(async () => {
|
||||||
|
const { data } = await axios.get(`${queryUrl.replace("{id}", taskId)}`, {
|
||||||
|
headers: { Authorization: authorization },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.status === "SUCCESS") {
|
||||||
|
return data.results?.length ? { completed: true, url: data.results[0].url } : { completed: false, error: "任务成功但未返回视频链接" };
|
||||||
|
}
|
||||||
|
if (data.status === "FAILED") return { completed: false, error: `任务失败: ${data.errorMessage || "未知错误"}` };
|
||||||
|
if (data.status === "QUEUED" || data.status === "RUNNING") return { completed: false };
|
||||||
|
return { completed: false, error: `未知状态: ${data.status}` };
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user