This commit is contained in:
ACT丶流星雨 2026-03-16 23:07:26 +08:00
commit c80abaa694
56 changed files with 7953 additions and 1798 deletions

1411
output.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@
"@ai-sdk/anthropic": "^3.0.35", "@ai-sdk/anthropic": "^3.0.35",
"@ai-sdk/deepseek": "^2.0.17", "@ai-sdk/deepseek": "^2.0.17",
"@ai-sdk/devtools": "^0.0.11", "@ai-sdk/devtools": "^0.0.11",
"@ai-sdk/google": "^3.0.20", "@ai-sdk/google": "^3.0.43",
"@ai-sdk/openai": "^3.0.25", "@ai-sdk/openai": "^3.0.25",
"@ai-sdk/openai-compatible": "^2.0.27", "@ai-sdk/openai-compatible": "^2.0.27",
"@ai-sdk/xai": "^3.0.47", "@ai-sdk/xai": "^3.0.47",

File diff suppressed because one or more lines are too long

View File

@ -176,13 +176,19 @@ async function processImages(images: ImageInfo[]): Promise<Buffer[]> {
if (images.length <= maxImages) { if (images.length <= maxImages) {
const buffers = await Promise.all(images.map((img) => u.oss.getFile(img.filePath))); const buffers = await Promise.all(images.map((img) => u.oss.getFile(img.filePath)));
processedBuffers = await Promise.all(buffers.map((buffer) => compressImage(buffer))); processedBuffers = await Promise.all(buffers.map((buffer) => compressImage(buffer)));
} else { } else {
const mergeStartIndex = maxImages - 1; const mergeStartIndex = maxImages - 1;
const firstBuffers = await Promise.all(images.slice(0, mergeStartIndex).map((img) => u.oss.getFile(img.filePath))); const firstBuffers = await Promise.all(images.slice(0, mergeStartIndex).map((img) => u.oss.getFile(img.filePath)));
const compressedFirstImages = await Promise.all(firstBuffers.map((buffer) => compressImage(buffer))); const compressedFirstImages = await Promise.all(firstBuffers.map((buffer) => compressImage(buffer)));
const imagesToMergeList = images.slice(mergeStartIndex).map((img) => img.filePath); const imagesToMergeList = images.slice(mergeStartIndex).map((img) => img.filePath);
const mergedImage = await mergeImages(imagesToMergeList); const mergedImage = await mergeImages(imagesToMergeList);
processedBuffers = [...compressedFirstImages, mergedImage]; processedBuffers = [...compressedFirstImages, mergedImage];
} }
@ -290,7 +296,7 @@ export default async (cells: { prompt: string }[], scriptId: number, projectId:
const filteredImages = await filterRelevantAssets(cellPrompts, resources, allImages); const filteredImages = await filterRelevantAssets(cellPrompts, resources, allImages);
const resourcesMapPrompts = buildResourcesMapPrompts(filteredImages); const resourcesMapPrompts = buildResourcesMapPrompts(filteredImages);
console.log("====润色前:", cellPrompts);
const promptsData = await generateImagePromptsTool({ const promptsData = await generateImagePromptsTool({
prompts: cellPrompts, prompts: cellPrompts,
style: `类型:${projectInfo?.type!},风格:${projectInfo?.artStyle!}`, style: `类型:${projectInfo?.type!},风格:${projectInfo?.artStyle!}`,
@ -305,7 +311,6 @@ export default async (cells: { prompt: string }[], scriptId: number, projectId:
// 注意:请严格按照提示词内容生成图片,确保人物样貌、艺术风格、色调光影一致。 // 注意:请严格按照提示词内容生成图片,确保人物样貌、艺术风格、色调光影一致。
// `; // `;
const prompts = promptsData.prompt; const prompts = promptsData.prompt;
console.log("====润色后:", prompts);
const processedImages = await processImages(filteredImages); const processedImages = await processImages(filteredImages);
const apiConfig = await u.getPromptAi("storyboardImage"); const apiConfig = await u.getPromptAi("storyboardImage");
@ -317,10 +322,13 @@ export default async (cells: { prompt: string }[], scriptId: number, projectId:
size: "4K", size: "4K",
aspectRatio: projectInfo?.videoRatio ? (projectInfo.videoRatio as any) : "16:9", aspectRatio: projectInfo?.videoRatio ? (projectInfo.videoRatio as any) : "16:9",
imageBase64: processedImages.map((buf) => buf.toString("base64")), imageBase64: processedImages.map((buf) => buf.toString("base64")),
taskClass: "分镜图生成",
name: `分镜图-${outline?.title || "未知剧集"}`,
describe: prompts,
projectId,
}, },
apiConfig, apiConfig,
); );
const match = contentStr.match(/base64,([A-Za-z0-9+/=]+)/); const match = contentStr.match(/base64,([A-Za-z0-9+/=]+)/);
const base64Str = match?.[1] ?? contentStr; const base64Str = match?.[1] ?? contentStr;
const buffer = Buffer.from(base64Str, "base64"); const buffer = Buffer.from(base64Str, "base64");

View File

@ -98,14 +98,13 @@ export default class Storyboard {
private log(action: string, detail?: string) { private log(action: string, detail?: string) {
const msg = detail ? `${action}: ${detail}` : action; const msg = detail ? `${action}: ${detail}` : action;
console.log(`\n[${new Date().toLocaleTimeString()}] ${msg}\n`);
} }
// ==================== 剧本相关操作 ==================== // ==================== 剧本相关操作 ====================
getScript = tool({ getScript = tool({
title: "getScript", title: "getScript",
description: "获取剧本内容", description: "用于获取剧本内容",
inputSchema: z.object({}), inputSchema: z.object({}),
execute: async () => { execute: async () => {
this.log("获取剧本", `scriptId: ${this.scriptId}`); this.log("获取剧本", `scriptId: ${this.scriptId}`);
@ -242,7 +241,6 @@ ${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 exists = this.shots.some((f) => f.segmentId === item.segmentIndex);
if (exists) { if (exists) {
skipped.push(item.segmentIndex); skipped.push(item.segmentIndex);
@ -445,6 +443,7 @@ ${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: "正在分割宫格图片为单张镜头图" });
@ -460,8 +459,10 @@ ${sections.join("\n\n")}
for (let i = 0; i < imageBuffers.length; i++) { for (let i = 0; i < imageBuffers.length; i++) {
const fileName = `${this.projectId}/chat/${this.scriptId}/storyboard/shot_${shotId}_take_${i}_${timestamp}.png`; const fileName = `${this.projectId}/chat/${this.scriptId}/storyboard/shot_${shotId}_take_${i}_${timestamp}.png`;
await u.oss.writeFile(fileName, imageBuffers[i]); await u.oss.writeFile(fileName, imageBuffers[i]);
const imageUrl = await u.oss.getFileUrl(fileName); const imageUrl = await u.oss.getFileUrl(fileName);
imagePaths.push(imageUrl); imagePaths.push(imageUrl);
// 每保存一张镜头图片通知进度 // 每保存一张镜头图片通知进度
@ -661,7 +662,9 @@ ${task}
inputSchema: z.object({ inputSchema: z.object({
taskDescription: z.string().describe("具体的任务描述,包含章节范围、修改要求等详细信息"), taskDescription: z.string().describe("具体的任务描述,包含章节范围、修改要求等详细信息"),
}), }),
execute: async ({ taskDescription }) => this.invokeSubAgent(agentType, taskDescription), execute: async ({ taskDescription }) => {
return this.invokeSubAgent(agentType, taskDescription);
},
}); });
} }
@ -671,7 +674,7 @@ ${task}
return { return {
segmentAgent: this.createSubAgentTool( segmentAgent: this.createSubAgentTool(
"segmentAgent", "segmentAgent",
"调用片段师。负责根据剧本生成片段,会自行调用 getScript 获取剧本内容,并调用 updateSegments 保存片段结果。", "调用片段师。负责根据剧本生成片段,必须调用 getScript工具 获取剧本内容,并调用 updateSegments 保存片段结果。",
), ),
shotAgent: this.createSubAgentTool( shotAgent: this.createSubAgentTool(
"shotAgent", "shotAgent",

1411
src/lib/artStyle.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,6 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import { artStyle } from "./artStyle";
interface TableSchema { interface TableSchema {
name: string; name: string;
builder: (table: Knex.CreateTableBuilder) => void; builder: (table: Knex.CreateTableBuilder) => void;
@ -96,6 +97,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
name: "t_project", name: "t_project",
builder: (table) => { builder: (table) => {
table.integer("id"); table.integer("id");
// table.string("projectType");
table.text("name"); table.text("name");
table.text("intro"); table.text("intro");
table.text("type"); table.text("type");
@ -160,20 +162,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
table.unique(["id"]); table.unique(["id"]);
}, },
}, },
{
name: "t_taskList",
builder: (table) => {
table.integer("id").notNullable();
table.integer("projectName");
table.text("name");
table.text("prompt");
table.text("state");
table.text("startTime");
table.text("endTime");
table.primary(["id"]);
table.unique(["id"]);
},
},
{ {
name: "t_image", name: "t_image",
builder: (table) => { builder: (table) => {
@ -207,6 +195,36 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
}, },
initData: async (knex) => {}, initData: async (knex) => {},
}, },
// {
// name: "t_myTasks",
// builder: (table) => {
// table.integer("id").notNullable();
// table.integer("projectId");
// table.string("taskClass");
// table.string("relatedObjects");
// table.string("model");
// table.text("describe");
// table.string("state");
// table.integer("startTime");
// table.text("reason");
// table.primary(["id"]);
// table.unique(["id"]);
// },
// initData: async (knex) => {},
// },
{
name: "t_artStyle",
builder: (table) => {
table.integer("id").notNullable();
table.string("name");
table.text("styles");
table.primary(["id"]);
table.unique(["id"]);
},
initData: async (knex) => {
await knex("t_artStyle").insert(artStyle.map((item, index) => ({ id: index + 1, name: item.name, styles: JSON.stringify(item.styles) })));
},
},
{ {
name: "t_videoConfig", name: "t_videoConfig",
builder: (table) => { builder: (table) => {
@ -573,54 +591,38 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
{ manufacturer: "deepSeek", model: "deepseek-chat", responseFormat: "schema", image: 0, think: 0, tool: 1 }, { manufacturer: "deepSeek", model: "deepseek-chat", responseFormat: "schema", image: 0, think: 0, tool: 1 },
{ manufacturer: "deepSeek", model: "deepseek-reasoner", responseFormat: "schema", image: 0, think: 1, tool: 1 }, { manufacturer: "deepSeek", model: "deepseek-reasoner", responseFormat: "schema", image: 0, think: 1, tool: 1 },
{ manufacturer: "volcengine", model: "doubao-seed-2-0-pro-260215", responseFormat: "object", image: 1, think: 1, tool: 1 }, { manufacturer: "volcengine", model: "doubao-seed-2-0-pro-260215", responseFormat: "object", image: 1, think: 1, tool: 1 },
// { manufacturer: "volcengine", model: "doubao-seed-2-0-lite-260215", responseFormat: "object", image: 1, think: 1, tool: 1 },
// { manufacturer: "volcengine", model: "doubao-seed-2-0-mini-260215", responseFormat: "object", image: 1, think: 1, tool: 1 },
{ manufacturer: "volcengine", model: "doubao-seed-1-8-251228", responseFormat: "schema", image: 1, think: 1, tool: 1 }, { manufacturer: "volcengine", model: "doubao-seed-1-8-251228", responseFormat: "schema", image: 1, think: 1, tool: 1 },
// { manufacturer: "volcengine", model: "doubao-seed-1-6-251015", responseFormat: "schema", image: 1, think: 1, tool: 1 },
// { manufacturer: "volcengine", model: "doubao-seed-1-6-lite-251015", responseFormat: "schema", image: 1, think: 1, tool: 1 },
// { manufacturer: "volcengine", model: "doubao-seed-1-6-flash-250828", responseFormat: "schema", image: 1, think: 1, tool: 1 },
{ manufacturer: "zhipu", model: "glm-4.7", responseFormat: "object", image: 0, think: 0, tool: 1 }, { manufacturer: "zhipu", model: "glm-4.7", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4.7-flashx", responseFormat: "object", image: 0, think: 0, tool: 1 },
{ manufacturer: "zhipu", model: "glm-4.6", responseFormat: "object", image: 0, think: 0, tool: 1 }, { manufacturer: "zhipu", model: "glm-4.6", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4.5-air", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4.5-airx", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4-long", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4-flashx-250414", responseFormat: "object", image: 0, think: 0, tool: 1 },
{ manufacturer: "zhipu", model: "glm-4.7-flash", responseFormat: "object", image: 0, think: 0, tool: 1 }, { manufacturer: "zhipu", model: "glm-4.7-flash", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4.5-flash", responseFormat: "object", image: 0, think: 1, tool: 1 },
// { manufacturer: "zhipu", model: "glm-4-flash-250414", responseFormat: "object", image: 0, think: 0, tool: 1 },
{ manufacturer: "zhipu", model: "glm-4.6v", responseFormat: "object", image: 1, think: 1, tool: 1 }, { manufacturer: "zhipu", model: "glm-4.6v", responseFormat: "object", image: 1, think: 1, tool: 1 },
{ manufacturer: "qwen", model: "qwen-vl-max", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "qwen", model: "qwen-vl-max", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "qwen", model: "qwen-plus-latest", responseFormat: "schema", image: 0, think: 0, tool: 1 }, { manufacturer: "qwen", model: "qwen-plus-latest", responseFormat: "schema", image: 0, think: 0, tool: 1 },
{ manufacturer: "qwen", model: "qwen-max", responseFormat: "schema", image: 0, think: 0, tool: 1 }, { manufacturer: "qwen", model: "qwen-max", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "qwen", model: "qwen2.5-72b-instruct", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "qwen", model: "qwen2.5-14b-instruct-1m", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "qwen", model: "qwen2.5-vl-72b-instruct", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "openai", model: "gpt-4o", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "openai", model: "gpt-4o-mini", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "openai", model: "gpt-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "openai", model: "gpt-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "openai", model: "gpt-5.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "openai", model: "gpt-5.2", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "openai", model: "gpt-5.2", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "gemini", model: "gemini-3-pro-preview", responseFormat: "schema", image: 1, think: 1, tool: 1 }, { manufacturer: "gemini", model: "gemini-3-pro-preview", responseFormat: "schema", image: 1, think: 1, tool: 1 },
{ manufacturer: "gemini", model: "gemini-2.5-pro", responseFormat: "schema", image: 1, think: 1, tool: 1 }, { manufacturer: "gemini", model: "gemini-2.5-pro", responseFormat: "schema", image: 1, think: 1, tool: 1 },
// { manufacturer: "gemini", model: "gemini-2.5-flash", responseFormat: "schema", image: 1, think: 1, tool: 1 },
// { manufacturer: "gemini", model: "gemini-2.0-flash", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "gemini", model: "gemini-2.0-flash-lite", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "gemini", model: "gemini-1.5-pro", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "gemini", model: "gemini-1.5-flash", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "anthropic", model: "claude-opus-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "anthropic", model: "claude-opus-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "anthropic", model: "claude-haiku-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "anthropic", model: "claude-haiku-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "anthropic", model: "claude-sonnet-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "anthropic", model: "claude-sonnet-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "anthropic", model: "claude-opus-4-1", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "anthropic", model: "claude-opus-4-1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "anthropic", model: "claude-opus-4-0", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "anthropic", model: "claude-sonnet-4-0", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "anthropic", model: "claude-3-7-sonnet-latest", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "anthropic", model: "claude-3-5-haiku-latest", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "xai", model: "grok-3", responseFormat: "schema", image: 0, think: 0, tool: 1 }, { manufacturer: "xai", model: "grok-3", responseFormat: "schema", image: 0, think: 0, tool: 1 },
{ manufacturer: "xai", model: "grok-4", responseFormat: "schema", image: 0, think: 0, tool: 1 }, { manufacturer: "xai", model: "grok-4", responseFormat: "schema", image: 0, think: 0, tool: 1 },
{ manufacturer: "xai", model: "grok-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 }, { manufacturer: "xai", model: "grok-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
{ manufacturer: "other", model: "", responseFormat: "object", image: 1, think: 0, tool: 1 }, { manufacturer: "other", model: "", responseFormat: "object", image: 1, think: 0, tool: 1 },
{ manufacturer: "modelScope", model: "deepseek-ai/DeepSeek-V3.2", responseFormat: "object", image: 0, think: 0, tool: 1 }, { manufacturer: "modelScope", model: "deepseek-ai/DeepSeek-V3.2", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "formal", model: "gpt-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "formal", model: "doubao-seed-2-0-pro-260215", responseFormat: "object", image: 0, think: 0, tool: 1 },
// { manufacturer: "formal", model: "doubao-seed-1-8-251215", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "formal", model: "gpt-5.2", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "formal", model: "gpt-5.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "formal", model: "claude-sonnet-4-6", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "formal", model: "claude-sonnet-4-5-20250929", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "formal", model: "claude-opus-4-5-20251101", responseFormat: "schema", image: 0, think: 0, tool: 1 },
// { manufacturer: "formal", model: "qwen3.5-plus", responseFormat: "schema", image: 1, think: 0, tool: 1 },
// { manufacturer: "formal", model: "qwen3-max-2026-01-23", responseFormat: "schema", image: 1, think: 0, tool: 1 },
]); ]);
}, },
}, },
@ -638,7 +640,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
await knex("t_imageModel").insert([ await knex("t_imageModel").insert([
{ manufacturer: "volcengine", model: "doubao-seedream-5-0-260128", grid: 1, type: "ti2i" }, { manufacturer: "volcengine", model: "doubao-seedream-5-0-260128", grid: 1, type: "ti2i" },
{ manufacturer: "volcengine", model: "doubao-seedream-4-5-251128", grid: 0, type: "ti2i" }, { manufacturer: "volcengine", model: "doubao-seedream-4-5-251128", grid: 0, type: "ti2i" },
// { manufacturer: "volcengine", model: "doubao-seedream-4-0-250828", grid: 0, type: "ti2i" },
{ manufacturer: "kling", model: "kling-image-o1", grid: 0, type: "ti2i" }, { manufacturer: "kling", model: "kling-image-o1", grid: 0, type: "ti2i" },
{ manufacturer: "gemini", model: "gemini-2.5-flash-image", grid: 1, type: "ti2i" }, { manufacturer: "gemini", model: "gemini-2.5-flash-image", grid: 1, type: "ti2i" },
{ manufacturer: "gemini", model: "gemini-3-pro-image-preview", grid: 1, type: "ti2i" }, { manufacturer: "gemini", model: "gemini-3-pro-image-preview", grid: 1, type: "ti2i" },
@ -650,6 +651,10 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
{ manufacturer: "grsai", model: "nano-banana-pro", grid: 1, type: "ti2i" }, { manufacturer: "grsai", model: "nano-banana-pro", grid: 1, type: "ti2i" },
{ manufacturer: "grsai", model: "nano-banana", grid: 1, type: "ti2i" }, { manufacturer: "grsai", model: "nano-banana", grid: 1, type: "ti2i" },
{ manufacturer: "grsai", model: "nano-banana-2", grid: 1, type: "ti2i" }, { manufacturer: "grsai", model: "nano-banana-2", grid: 1, type: "ti2i" },
// { manufacturer: "formal", model: "Doubao-Seedream-5.0-Lite", grid: 1, type: "ti2i" },
// { manufacturer: "formal", model: "doubao-seedream-4-5-251128", grid: 1, type: "ti2i" },
// { manufacturer: "formal", model: "doubao-seedream-4-0-250828", grid: 1, type: "ti2i" },
]); ]);
}, },
}, },
@ -668,7 +673,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
initData: async (knex) => { initData: async (knex) => {
await knex("t_videoModel").insert([ await knex("t_videoModel").insert([
{ {
id: 1,
manufacturer: "volcengine", manufacturer: "volcengine",
model: "doubao-seedance-1-5-pro-251215", model: "doubao-seedance-1-5-pro-251215",
durationResolutionMap: JSON.stringify([{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
@ -677,7 +681,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "endFrameOptional"]), type: JSON.stringify(["text", "endFrameOptional"]),
}, },
{ {
id: 2,
manufacturer: "volcengine", manufacturer: "volcengine",
model: "doubao-seedance-1-0-pro-250528", model: "doubao-seedance-1-0-pro-250528",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
@ -686,7 +689,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "endFrameOptional"]), type: JSON.stringify(["text", "endFrameOptional"]),
}, },
{ {
id: 3,
manufacturer: "volcengine", manufacturer: "volcengine",
model: "doubao-seedance-1-0-pro-fast-251015", model: "doubao-seedance-1-0-pro-fast-251015",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
@ -695,7 +697,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "singleImage"]), type: JSON.stringify(["text", "singleImage"]),
}, },
{ {
id: 4,
manufacturer: "volcengine", manufacturer: "volcengine",
model: "doubao-seedance-1-0-lite-i2v-250428", model: "doubao-seedance-1-0-lite-i2v-250428",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
@ -704,106 +705,54 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["endFrameOptional", "reference"]), type: JSON.stringify(["endFrameOptional", "reference"]),
}, },
{ {
id: 5,
manufacturer: "volcengine", manufacturer: "volcengine",
model: "doubao-seedance-1-0-lite-t2v-250428", model: "doubao-seedance-2-0-260128",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "480p"] }]),
aspectRatio: JSON.stringify(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"]), aspectRatio: JSON.stringify(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"]),
audio: 0, audio: 1,
type: JSON.stringify(["text"]), type: JSON.stringify(["endFrameOptional", "multiImage"]),
}, },
{ {
id: 6,
manufacturer: "kling", manufacturer: "kling",
model: "kling-v1(STD)", model: "kling-v1(STD)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["720p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["720p"] }]),
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]), aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["text"]), type: JSON.stringify(["text", "startEndRequired"]),
}, },
{ {
id: 7,
manufacturer: "kling",
model: "kling-v1(STD)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["720p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["startEndRequired"]),
},
{
id: 8,
manufacturer: "kling", manufacturer: "kling",
model: "kling-v1(PRO)", model: "kling-v1(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]), aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["text"]), type: JSON.stringify(["text", "startEndRequired"]),
}, },
{ {
id: 9,
manufacturer: "kling",
model: "kling-v1(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["startEndRequired"]),
},
{
id: 10,
manufacturer: "kling", manufacturer: "kling",
model: "kling-v1-6(PRO)", model: "kling-v1-6(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]), aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["text"]), type: JSON.stringify(["text", "startEndRequired"]),
}, },
{ {
id: 11,
manufacturer: "kling",
model: "kling-v1-6(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["startEndRequired"]),
},
{
id: 12,
manufacturer: "kling", manufacturer: "kling",
model: "kling-v2-5-turbo(PRO)", model: "kling-v2-5-turbo(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]), aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["text"]), type: JSON.stringify(["text", "startEndRequired"]),
}, },
{ {
id: 13,
manufacturer: "kling",
model: "kling-v2-5-turbo(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["startEndRequired"]),
},
{
id: 14,
manufacturer: "kling", manufacturer: "kling",
model: "kling-v2-6(PRO)", model: "kling-v2-6(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]), aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["text"]), type: JSON.stringify(["text", "startEndRequired"]),
}, },
{ {
id: 15,
manufacturer: "kling",
model: "kling-v2-6(PRO)",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["startEndRequired"]),
},
{
id: 16,
manufacturer: "vidu", manufacturer: "vidu",
model: "viduq3-pro", model: "viduq3-pro",
durationResolutionMap: JSON.stringify([ durationResolutionMap: JSON.stringify([
@ -811,21 +760,9 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
]), ]),
aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]), aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
audio: 1, audio: 1,
type: JSON.stringify(["text"]), type: JSON.stringify(["text", "singleImage"]),
}, },
{ {
id: 17,
manufacturer: "vidu",
model: "viduq3-pro",
durationResolutionMap: JSON.stringify([
{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], resolution: ["540p", "720p", "1080p"] },
]),
aspectRatio: JSON.stringify([]),
audio: 1,
type: JSON.stringify(["singleImage"]),
},
{
id: 18,
manufacturer: "vidu", manufacturer: "vidu",
model: "viduq2-pro-fast", model: "viduq2-pro-fast",
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["720p", "1080p"] }]),
@ -834,70 +771,22 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage", "startEndRequired"]), type: JSON.stringify(["singleImage", "startEndRequired"]),
}, },
{ {
id: 19,
manufacturer: "vidu",
model: "viduq2-pro",
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
audio: 0,
type: JSON.stringify(["text"]),
},
{
id: 20,
manufacturer: "vidu", manufacturer: "vidu",
model: "viduq2-pro", model: "viduq2-pro",
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
aspectRatio: JSON.stringify([]), aspectRatio: JSON.stringify([]),
audio: 0, audio: 0,
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]), type: JSON.stringify(["singleImage", "reference", "startEndRequired", "text"]),
}, },
{ {
id: 21,
manufacturer: "vidu",
model: "viduq2-turbo",
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
audio: 0,
type: JSON.stringify(["text"]),
},
{
id: 22,
manufacturer: "vidu", manufacturer: "vidu",
model: "viduq2-turbo", model: "viduq2-turbo",
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
aspectRatio: JSON.stringify([]), aspectRatio: JSON.stringify([]),
audio: 0, audio: 0,
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]), type: JSON.stringify(["singleImage", "reference", "startEndRequired", "text"]),
}, },
{ {
id: 23,
manufacturer: "vidu",
model: "viduq1",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1"]),
audio: 0,
type: JSON.stringify(["text"]),
},
{
id: 24,
manufacturer: "vidu",
model: "viduq1",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
},
{
id: 25,
manufacturer: "vidu",
model: "viduq1-classic",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["1080p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["singleImage", "startEndRequired"]),
},
{
id: 26,
manufacturer: "vidu", manufacturer: "vidu",
model: "vidu2.0", model: "vidu2.0",
durationResolutionMap: JSON.stringify([ durationResolutionMap: JSON.stringify([
@ -909,7 +798,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]), type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
}, },
{ {
id: 27,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.6-t2v", model: "wan2.6-t2v",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]),
@ -918,7 +806,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text"]), type: JSON.stringify(["text"]),
}, },
{ {
id: 28,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.5-t2v-preview", model: "wan2.5-t2v-preview",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["480p", "720p", "1080p"] }]),
@ -927,7 +814,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text"]), type: JSON.stringify(["text"]),
}, },
{ {
id: 29,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.2-t2v-plus", model: "wan2.2-t2v-plus",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "1080p"] }]),
@ -936,7 +822,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text"]), type: JSON.stringify(["text"]),
}, },
{ {
id: 30,
manufacturer: "wan", manufacturer: "wan",
model: "wanx2.1-t2v-turbo", model: "wanx2.1-t2v-turbo",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p"] }]),
@ -945,7 +830,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text"]), type: JSON.stringify(["text"]),
}, },
{ {
id: 31,
manufacturer: "wan", manufacturer: "wan",
model: "wanx2.1-t2v-plus", model: "wanx2.1-t2v-plus",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]),
@ -954,7 +838,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text"]), type: JSON.stringify(["text"]),
}, },
{ {
id: 32,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.6-i2v-flash", model: "wan2.6-i2v-flash",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]),
@ -963,7 +846,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage"]), type: JSON.stringify(["singleImage"]),
}, },
{ {
id: 33,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.6-i2v", model: "wan2.6-i2v",
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]),
@ -972,7 +854,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage"]), type: JSON.stringify(["singleImage"]),
}, },
{ {
id: 34,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.5-i2v-preview", model: "wan2.5-i2v-preview",
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["480p", "720p", "1080p"] }]),
@ -981,7 +862,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage"]), type: JSON.stringify(["singleImage"]),
}, },
{ {
id: 35,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.2-i2v-flash", model: "wan2.2-i2v-flash",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p", "1080p"] }]),
@ -990,7 +870,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage"]), type: JSON.stringify(["singleImage"]),
}, },
{ {
id: 36,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.2-i2v-plus", model: "wan2.2-i2v-plus",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "1080p"] }]),
@ -999,25 +878,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["singleImage"]), type: JSON.stringify(["singleImage"]),
}, },
{ {
id: 37,
manufacturer: "wan",
model: "wanx2.1-i2v-plus",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["singleImage"]),
},
{
id: 38,
manufacturer: "wan",
model: "wanx2.1-i2v-turbo",
durationResolutionMap: JSON.stringify([{ duration: [3, 4, 5], resolution: ["480p", "720p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["singleImage"]),
},
{
id: 39,
manufacturer: "wan", manufacturer: "wan",
model: "wan2.2-kf2v-flash", model: "wan2.2-kf2v-flash",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p", "1080p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p", "1080p"] }]),
@ -1026,16 +886,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["startEndRequired"]), type: JSON.stringify(["startEndRequired"]),
}, },
{ {
id: 40,
manufacturer: "wan",
model: "wanx2.1-kf2v-plus",
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]),
aspectRatio: JSON.stringify([]),
audio: 0,
type: JSON.stringify(["startEndRequired"]),
},
{
id: 41,
manufacturer: "gemini", manufacturer: "gemini",
model: "veo-3.1-generate-preview", model: "veo-3.1-generate-preview",
durationResolutionMap: JSON.stringify([ durationResolutionMap: JSON.stringify([
@ -1047,7 +897,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "singleImage", "startEndRequired", "endFrameOptional", "reference"]), type: JSON.stringify(["text", "singleImage", "startEndRequired", "endFrameOptional", "reference"]),
}, },
{ {
id: 42,
manufacturer: "gemini", manufacturer: "gemini",
model: "veo-3.1-fast-generate-preview", model: "veo-3.1-fast-generate-preview",
durationResolutionMap: JSON.stringify([ durationResolutionMap: JSON.stringify([
@ -1059,7 +908,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "singleImage", "startEndRequired", "endFrameOptional", "reference"]), type: JSON.stringify(["text", "singleImage", "startEndRequired", "endFrameOptional", "reference"]),
}, },
{ {
id: 43,
manufacturer: "gemini", manufacturer: "gemini",
model: "veo-3.0-generate-preview", model: "veo-3.0-generate-preview",
durationResolutionMap: JSON.stringify([ durationResolutionMap: JSON.stringify([
@ -1071,7 +919,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "singleImage"]), type: JSON.stringify(["text", "singleImage"]),
}, },
{ {
id: 44,
manufacturer: "gemini", manufacturer: "gemini",
model: "veo-3.0-fast-generate-preview", model: "veo-3.0-fast-generate-preview",
durationResolutionMap: JSON.stringify([ durationResolutionMap: JSON.stringify([
@ -1083,7 +930,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "singleImage"]), type: JSON.stringify(["text", "singleImage"]),
}, },
{ {
id: 45,
manufacturer: "gemini", manufacturer: "gemini",
model: "veo-2.0-generate-001", model: "veo-2.0-generate-001",
durationResolutionMap: JSON.stringify([{ duration: [5, 6, 7, 8], resolution: ["720p"] }]), durationResolutionMap: JSON.stringify([{ duration: [5, 6, 7, 8], resolution: ["720p"] }]),
@ -1092,34 +938,30 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["text", "singleImage"]), type: JSON.stringify(["text", "singleImage"]),
}, },
{ {
id: 46,
manufacturer: "runninghub", manufacturer: "runninghub",
model: "sora-2", model: "sora-2",
durationResolutionMap: JSON.stringify([{ duration: [10, 15], resolution: [] }]), durationResolutionMap: JSON.stringify([{ duration: [10, 15], resolution: [] }]),
aspectRatio: JSON.stringify(["16:9", "9:16"]), aspectRatio: JSON.stringify(["16:9", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["singleImage", "text"]), type: JSON.stringify(["singleImage", "text", "multiImage"]),
}, },
{ {
id: 47,
manufacturer: "runninghub", manufacturer: "runninghub",
model: "sora-2-pro", model: "sora-2-pro",
durationResolutionMap: JSON.stringify([{ duration: [15, 25], resolution: [] }]), durationResolutionMap: JSON.stringify([{ duration: [15, 25], resolution: [] }]),
aspectRatio: JSON.stringify(["16:9", "9:16"]), aspectRatio: JSON.stringify(["16:9", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["singleImage", "text"]), type: JSON.stringify(["singleImage", "text", "multiImage"]),
}, },
{ {
id: 48,
manufacturer: "grsai", manufacturer: "grsai",
model: "sora-2", model: "sora-2",
durationResolutionMap: JSON.stringify([{ duration: [10, 15], resolution: [] }]), durationResolutionMap: JSON.stringify([{ duration: [10, 15], resolution: [] }]),
aspectRatio: JSON.stringify(["16:9", "9:16"]), aspectRatio: JSON.stringify(["16:9", "9:16"]),
audio: 0, audio: 0,
type: JSON.stringify(["singleImage", "text"]), type: JSON.stringify(["singleImage", "text", "multiImage"]),
}, },
{ {
id: 49,
manufacturer: "grsai", manufacturer: "grsai",
model: "veo3.1-pro", model: "veo3.1-pro",
durationResolutionMap: JSON.stringify([]), durationResolutionMap: JSON.stringify([]),
@ -1129,7 +971,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
}, },
{ {
id: 50,
manufacturer: "grsai", manufacturer: "grsai",
model: "veo3.1-pro-1080p", model: "veo3.1-pro-1080p",
durationResolutionMap: JSON.stringify([]), durationResolutionMap: JSON.stringify([]),
@ -1138,7 +979,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["startEndRequired", "text"]), type: JSON.stringify(["startEndRequired", "text"]),
}, },
{ {
id: 51,
manufacturer: "grsai", manufacturer: "grsai",
model: "veo3.1-pro-4k", model: "veo3.1-pro-4k",
durationResolutionMap: JSON.stringify([]), durationResolutionMap: JSON.stringify([]),
@ -1147,7 +987,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["startEndRequired", "text"]), type: JSON.stringify(["startEndRequired", "text"]),
}, },
{ {
id: 52,
manufacturer: "grsai", manufacturer: "grsai",
model: "veo3.1-fast", model: "veo3.1-fast",
durationResolutionMap: JSON.stringify([]), durationResolutionMap: JSON.stringify([]),
@ -1156,7 +995,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["startEndRequired", "text"]), type: JSON.stringify(["startEndRequired", "text"]),
}, },
{ {
id: 53,
manufacturer: "grsai", manufacturer: "grsai",
model: "veo3.1-fast-1080p", model: "veo3.1-fast-1080p",
durationResolutionMap: JSON.stringify([]), durationResolutionMap: JSON.stringify([]),
@ -1165,7 +1003,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
type: JSON.stringify(["startEndRequired", "text"]), type: JSON.stringify(["startEndRequired", "text"]),
}, },
{ {
id: 54,
manufacturer: "grsai", manufacturer: "grsai",
model: "veo3.1-fast-4k", model: "veo3.1-fast-4k",
durationResolutionMap: JSON.stringify([]), durationResolutionMap: JSON.stringify([]),
@ -1173,6 +1010,108 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
audio: 0, audio: 0,
type: JSON.stringify(["startEndRequired", "text"]), type: JSON.stringify(["startEndRequired", "text"]),
}, },
// {
// manufacturer: "formal",
// model: "Seedance-1.5-Pro-audio",
// durationResolutionMap: JSON.stringify([{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
// aspectRatio: JSON.stringify(["16:9", "9:16"]),
// audio: 0,
// type: JSON.stringify(["endFrameOptional"]),
// },
// {
// manufacturer: "formal",
// model: "Seedance-1.5-Pro-NotAudio",
// durationResolutionMap: JSON.stringify([{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
// aspectRatio: JSON.stringify(["16:9", "9:16"]),
// audio: 0,
// type: JSON.stringify(["endFrameOptional"]),
// },
// {
// manufacturer: "formal",
// model: "Seedance-1.0-Pro",
// durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
// aspectRatio: JSON.stringify(["16:9", "9:16"]),
// audio: 0,
// type: JSON.stringify(["endFrameOptional"]),
// },
// {
// manufacturer: "formal",
// model: "ViduQ3-turbo",
// durationResolutionMap: JSON.stringify([
// { duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], resolution: ["540p", "720p", "1080p"] },
// ]),
// aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
// audio: 1,
// type: JSON.stringify(["singleImage"]),
// },
// {
// manufacturer: "formal",
// model: "ViduQ3-pro",
// durationResolutionMap: JSON.stringify([
// { duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], resolution: ["540p", "720p", "1080p"] },
// ]),
// aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
// audio: 1,
// type: JSON.stringify(["singleImage"]),
// },
// {
// manufacturer: "formal",
// model: "ViduQ2-pro-fast",
// durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["720p", "1080p"] }]),
// aspectRatio: JSON.stringify([]),
// audio: 0,
// type: JSON.stringify(["singleImage", "startEndRequired"]),
// },
// {
// manufacturer: "formal",
// model: "ViduQ2-pro",
// durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
// aspectRatio: JSON.stringify([]),
// audio: 0,
// type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
// },
// {
// manufacturer: "formal",
// model: "ViduQ2-turbo",
// durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
// aspectRatio: JSON.stringify([]),
// audio: 0,
// type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
// },
// {
// manufacturer: "formal",
// model: "ViduQ2",
// durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
// aspectRatio: JSON.stringify([]),
// audio: 0,
// type: JSON.stringify(["singleImage", "reference"]),
// },
// {
// manufacturer: "formal",
// model: "Sora-2-I2V",
// durationResolutionMap: JSON.stringify([{ duration: [4, 8, 12], resolution: [] }]),
// aspectRatio: JSON.stringify([]),
// audio: 0,
// type: JSON.stringify(["singleImage", "reference"]),
// },
// {
// manufacturer: "formal",
// model: "Wan2.6-I2V-720P",
// durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p"] }]),
// aspectRatio: JSON.stringify([]),
// audio: 1,
// type: JSON.stringify(["singleImage"]),
// },
// {
// manufacturer: "formal",
// model: "Wan2.6-I2V-720P-1080P",
// durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["1080p"] }]),
// aspectRatio: JSON.stringify([]),
// audio: 1,
// type: JSON.stringify(["singleImage"]),
// },
]); ]);
}, },
}, },

View File

@ -1,17 +1,17 @@
// @routes-hash c97cf72361299980ea4b0c43549a0de8 // @routes-hash d8b95db972bd0ab01243d87d89a004f0
import { Express } from "express"; import { Express } from "express";
import route1 from "./routes/assets/addAssets"; import route1 from "./routes/artStyle/getArtStyle";
import route2 from "./routes/assets/delAssets"; import route2 from "./routes/assets/addAssets";
import route3 from "./routes/assets/delAssetsImage"; import route3 from "./routes/assets/delAssets";
import route4 from "./routes/assets/generateAssets"; import route4 from "./routes/assets/delAssetsImage";
import route5 from "./routes/assets/getAssets"; import route5 from "./routes/assets/generateAssets";
import route6 from "./routes/assets/getImage"; import route6 from "./routes/assets/getAssets";
import route7 from "./routes/assets/getStoryboard"; import route7 from "./routes/assets/getImage";
import route8 from "./routes/assets/polishPrompt"; import route8 from "./routes/assets/getScriptList";
import route9 from "./routes/assets/saveAssets"; import route9 from "./routes/assets/polishAssetsPrompt";
import route10 from "./routes/assets/updateAssets"; import route10 from "./routes/assets/saveAssets";
import route11 from "./routes/index/index"; import route11 from "./routes/assets/updateAssets";
import route12 from "./routes/novel/addNovel"; import route12 from "./routes/novel/addNovel";
import route13 from "./routes/novel/delNovel"; import route13 from "./routes/novel/delNovel";
import route14 from "./routes/novel/getNovel"; import route14 from "./routes/novel/getNovel";
@ -55,18 +55,18 @@ import route51 from "./routes/setting/getSetting";
import route52 from "./routes/setting/getVideoModelDetail"; import route52 from "./routes/setting/getVideoModelDetail";
import route53 from "./routes/setting/getVideoModelList"; import route53 from "./routes/setting/getVideoModelList";
import route54 from "./routes/setting/updateModel"; import route54 from "./routes/setting/updateModel";
import route55 from "./routes/setting/updeteModel"; import route55 from "./routes/storyboard/batchSuperScoreImage";
import route56 from "./routes/storyboard/batchSuperScoreImage"; import route56 from "./routes/storyboard/chatStoryboard";
import route57 from "./routes/storyboard/chatStoryboard"; import route57 from "./routes/storyboard/delStoryboard";
import route58 from "./routes/storyboard/delStoryboard"; import route58 from "./routes/storyboard/generateShotImage";
import route59 from "./routes/storyboard/generateShotImage"; import route59 from "./routes/storyboard/generateVideoPrompt";
import route60 from "./routes/storyboard/generateStoryboardApi"; import route60 from "./routes/storyboard/getStoryboard";
import route61 from "./routes/storyboard/generateVideoPrompt"; import route61 from "./routes/storyboard/keepStoryboard";
import route62 from "./routes/storyboard/getStoryboard"; import route62 from "./routes/storyboard/saveStoryboard";
import route63 from "./routes/storyboard/keepStoryboard"; import route63 from "./routes/storyboard/storyboardImageEdit";
import route64 from "./routes/storyboard/saveStoryboard"; import route64 from "./routes/storyboard/uploadImage";
import route65 from "./routes/storyboard/uploadImage"; import route65 from "./routes/task/getMyTaskApi";
import route66 from "./routes/task/getTaskApi"; import route66 from "./routes/task/getTaskCategories";
import route67 from "./routes/task/taskDetails"; import route67 from "./routes/task/taskDetails";
import route68 from "./routes/user/getUser"; import route68 from "./routes/user/getUser";
import route69 from "./routes/user/saveUser"; import route69 from "./routes/user/saveUser";
@ -85,17 +85,17 @@ import route81 from "./routes/video/saveVideo";
import route82 from "./routes/video/upDateVideoConfig"; import route82 from "./routes/video/upDateVideoConfig";
export default async (app: Express) => { export default async (app: Express) => {
app.use("/assets/addAssets", route1); app.use("/artStyle/getArtStyle", route1);
app.use("/assets/delAssets", route2); app.use("/assets/addAssets", route2);
app.use("/assets/delAssetsImage", route3); app.use("/assets/delAssets", route3);
app.use("/assets/generateAssets", route4); app.use("/assets/delAssetsImage", route4);
app.use("/assets/getAssets", route5); app.use("/assets/generateAssets", route5);
app.use("/assets/getImage", route6); app.use("/assets/getAssets", route6);
app.use("/assets/getStoryboard", route7); app.use("/assets/getImage", route7);
app.use("/assets/polishPrompt", route8); app.use("/assets/getScriptList", route8);
app.use("/assets/saveAssets", route9); app.use("/assets/polishAssetsPrompt", route9);
app.use("/assets/updateAssets", route10); app.use("/assets/saveAssets", route10);
app.use("/index", route11); app.use("/assets/updateAssets", route11);
app.use("/novel/addNovel", route12); app.use("/novel/addNovel", route12);
app.use("/novel/delNovel", route13); app.use("/novel/delNovel", route13);
app.use("/novel/getNovel", route14); app.use("/novel/getNovel", route14);
@ -139,18 +139,18 @@ export default async (app: Express) => {
app.use("/setting/getVideoModelDetail", route52); app.use("/setting/getVideoModelDetail", route52);
app.use("/setting/getVideoModelList", route53); app.use("/setting/getVideoModelList", route53);
app.use("/setting/updateModel", route54); app.use("/setting/updateModel", route54);
app.use("/setting/updeteModel", route55); app.use("/storyboard/batchSuperScoreImage", route55);
app.use("/storyboard/batchSuperScoreImage", route56); app.use("/storyboard/chatStoryboard", route56);
app.use("/storyboard/chatStoryboard", route57); app.use("/storyboard/delStoryboard", route57);
app.use("/storyboard/delStoryboard", route58); app.use("/storyboard/generateShotImage", route58);
app.use("/storyboard/generateShotImage", route59); app.use("/storyboard/generateVideoPrompt", route59);
app.use("/storyboard/generateStoryboardApi", route60); app.use("/storyboard/getStoryboard", route60);
app.use("/storyboard/generateVideoPrompt", route61); app.use("/storyboard/keepStoryboard", route61);
app.use("/storyboard/getStoryboard", route62); app.use("/storyboard/saveStoryboard", route62);
app.use("/storyboard/keepStoryboard", route63); app.use("/storyboard/storyboardImageEdit", route63);
app.use("/storyboard/saveStoryboard", route64); app.use("/storyboard/uploadImage", route64);
app.use("/storyboard/uploadImage", route65); app.use("/task/getMyTaskApi", route65);
app.use("/task/getTaskApi", route66); app.use("/task/getTaskCategories", route66);
app.use("/task/taskDetails", route67); app.use("/task/taskDetails", route67);
app.use("/user/getUser", route68); app.use("/user/getUser", route68);
app.use("/user/saveUser", route69); app.use("/user/saveUser", route69);

View File

@ -0,0 +1,19 @@
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({
name: z.string(),
}),
async (req, res) => {
const { name } = req.body;
const data = await u.db("t_artStyle").where("name", name).select("styles").first();
const styles = data?.styles ? JSON.parse(data.styles) : [];
res.status(200).send(success(styles));
},
);

View File

@ -40,7 +40,7 @@ export default router.post(
const { id, type, projectId, base64, prompt, name } = req.body; const { id, type, projectId, base64, prompt, name } = req.body;
//获取风格 //获取风格
const project = await u.db("t_project").where("id", projectId).select("artStyle", "type", "intro").first(); const project = await u.db("t_project").where("id", projectId).select("artStyle", "type", "intro", "videoRatio").first();
if (!project) return res.status(500).send(success({ message: "项目为空" })); if (!project) return res.status(500).send(success({ message: "项目为空" }));
const promptsList = await u const promptsList = await u
@ -123,8 +123,13 @@ export default router.post(
state: "生成中", state: "生成中",
assetsId: id, assetsId: id,
}); });
const apiConfig = await u.getPromptAi("assetsImage"); let taskClass = "";
if (type == "role") taskClass = "角色图生成";
if (type == "scene") taskClass = "场景图生成";
if (type == "props") taskClass = "道具图生成";
if (type == "storyboard") taskClass = "分镜图生成";
const apiConfig = await u.getPromptAi("assetsImage");
try { try {
const contentStr = await u.ai.image( const contentStr = await u.ai.image(
{ {
@ -132,7 +137,11 @@ export default router.post(
prompt: userPrompt, prompt: userPrompt,
imageBase64: base64 ? [base64] : [], imageBase64: base64 ? [base64] : [],
size: "2K", size: "2K",
aspectRatio: "16:9", aspectRatio: project.videoRatio ?? "16:9",
taskClass: taskClass,
name: name,
describe: prompt,
projectId: projectId,
}, },
apiConfig, apiConfig,
); );
@ -171,7 +180,6 @@ export default router.post(
filePath: imagePath, filePath: imagePath,
type: insertType, type: insertType,
}); });
const path = await u.oss.getFileUrl(imagePath!); const path = await u.oss.getFileUrl(imagePath!);
// const state = await u.db("t_assets").where("id", id).select("state").first(); // const state = await u.db("t_assets").where("id", id).select("state").first();

View File

@ -1,27 +0,0 @@
import express from "express";
import u from "@/utils";
const router = express.Router();
import { z } from "zod";
import { error } from "@/lib/responseFormat";
export default router.get("/", async (req, res, next) => {
const id = 14;
const targetOutlineData = await u.db("t_outline").where("id", id).select("data").first();
if (!targetOutlineData) return res.status(400).send(error("大纲不存在"));
//筛选出改大纲特有的资产
const allOutlineDataList = await u.db("t_outline").where("projectId", 8).andWhere("id", "!=", id).select("data");
//找出目标ID大纲特有的资产名称
const allOutlineData = allOutlineDataList
.map((item) => {
const data = JSON.parse(item?.data || "[]");
return [...data.characters, ...data.props, ...data.scenes].map((item: any) => item.name);
})
.flat();
const targetOutLineNames = JSON.parse(targetOutlineData?.data || "[]");
const targetNames = [...targetOutLineNames.characters, ...targetOutLineNames.props, ...targetOutLineNames.scenes].map((item: any) => item.name);
const diffAssetsNames = targetNames.filter((item) => !allOutlineData.includes(item));
res.status(200).send(123);
});

View File

@ -48,6 +48,7 @@ export default router.post(
); );
res.status(200).send(success(reply)); res.status(200).send(success(reply));
} catch (err) { } catch (err) {
console.log("%c Line:51 🍬 err", "background:#465975", err);
const msg = u.error(err).message; const msg = u.error(err).message;
console.error(msg); console.error(msg);
res.status(500).send(error(msg)); res.status(500).send(error(msg));

View File

@ -22,8 +22,12 @@ export default router.post(
prompt: prompt:
"一张16:9比例的图片完美等分为2x2四宫格布局各区域无缝衔接\n左上宫格一只可爱的猫毛发蓬松眼睛明亮姿态俏皮\n右上宫格一只友善的狗金毛犬表情愉悦摇着尾巴\n左下宫格一头健壮的牛田园背景目光温和皮毛光泽\n右下宫格一匹骏马姿态优雅鬃毛飘逸肌肉健美\n风格要求四个宫格风格统一色彩鲜艳饱和高清画质细节清晰锐利专业插画风格线条干净统一的左上方光源柔和阴影和谐配色卡通/半写实风格,宫格间用白色或浅灰细线分隔", "一张16:9比例的图片完美等分为2x2四宫格布局各区域无缝衔接\n左上宫格一只可爱的猫毛发蓬松眼睛明亮姿态俏皮\n右上宫格一只友善的狗金毛犬表情愉悦摇着尾巴\n左下宫格一头健壮的牛田园背景目光温和皮毛光泽\n右下宫格一匹骏马姿态优雅鬃毛飘逸肌肉健美\n风格要求四个宫格风格统一色彩鲜艳饱和高清画质细节清晰锐利专业插画风格线条干净统一的左上方光源柔和阴影和谐配色卡通/半写实风格,宫格间用白色或浅灰细线分隔",
imageBase64: [], imageBase64: [],
aspectRatio: "16:9", aspectRatio: "9:16",
size: "1K", size: "4K",
taskClass: "测试任务",
name: "测试图片生成",
describe: "测试语言模型生成图片",
projectId: 0,
}, },
{ {
model: modelName, model: modelName,
@ -34,6 +38,7 @@ export default router.post(
); );
res.status(200).send(success(image)); res.status(200).send(success(image));
} catch (err) { } catch (err) {
console.log("%c Line:41 🍖 err", "background:#fca650", err);
const msg = u.error(err).message; const msg = u.error(err).message;
console.error(msg); console.error(msg);
res.status(500).send(error(msg)); res.status(500).send(error(msg));

View File

@ -28,6 +28,10 @@ export default router.post(
aspectRatio: "16:9", aspectRatio: "16:9",
audio: false, audio: false,
mode: "single", mode: "single",
taskClass: "测试视频生成",
name: "测试视频生成",
describe: "测试视频生成",
projectId: 0,
}, },
{ {
model: modelName, model: modelName,

View File

@ -5,7 +5,7 @@ import { error, success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware"; import { validateFields } from "@/middleware/middleware";
const router = express.Router(); const router = express.Router();
// 删除大纲 // 获取历史消息记录
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({

View File

@ -5,7 +5,7 @@ import { error, success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware"; import { validateFields } from "@/middleware/middleware";
const router = express.Router(); const router = express.Router();
// 删除大纲 // 保存历史消息记录
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({

View File

@ -9,6 +9,7 @@ const router = express.Router();
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({
projectType: z.string().optional(),
name: z.string(), name: z.string(),
intro: z.string(), intro: z.string(),
type: z.string(), type: z.string(),
@ -16,7 +17,7 @@ export default router.post(
videoRatio: z.string(), videoRatio: z.string(),
}), }),
async (req, res) => { async (req, res) => {
const { name, intro, type, artStyle, videoRatio } = req.body; const { projectType, name, intro, type, artStyle, videoRatio } = req.body;
await u.db("t_project").insert({ await u.db("t_project").insert({
name, name,
@ -29,5 +30,5 @@ export default router.post(
}); });
res.status(200).send(success({ message: "新增项目成功" })); res.status(200).send(success({ message: "新增项目成功" }));
} },
); );

View File

@ -27,6 +27,7 @@ export default router.post(
await u.db("t_novel").where("projectId", id).delete(); await u.db("t_novel").where("projectId", id).delete();
await u.db("t_storyline").where("projectId", id).delete(); await u.db("t_storyline").where("projectId", id).delete();
await u.db("t_outline").where("projectId", id).delete(); await u.db("t_outline").where("projectId", id).delete();
// await u.db("t_myTasks").where("projectId", id).delete();
await u.db("t_script").where("projectId", id).delete(); await u.db("t_script").where("projectId", id).delete();
await u.db("t_assets").where("projectId", id).delete(); await u.db("t_assets").where("projectId", id).delete();
@ -55,5 +56,5 @@ export default router.post(
} }
res.status(200).send(success({ message: "删除项目成功" })); res.status(200).send(success({ message: "删除项目成功" }));
} },
); );

View File

@ -14,9 +14,10 @@ export default router.post(
type: z.string().optional().nullable(), type: z.string().optional().nullable(),
artStyle: z.string().optional().nullable(), artStyle: z.string().optional().nullable(),
videoRatio: z.string().optional().nullable(), videoRatio: z.string().optional().nullable(),
projectType: z.string().optional().nullable(),
}), }),
async (req, res) => { async (req, res) => {
const { id, intro, type, artStyle, videoRatio } = req.body; const { id, intro, type, artStyle, videoRatio, projectType } = req.body;
await u.db("t_project").where("id", id).update({ await u.db("t_project").where("id", id).update({
intro, intro,
@ -26,5 +27,5 @@ export default router.post(
}); });
res.status(200).send(success({ message: "修改成功" })); res.status(200).send(success({ message: "修改成功" }));
} },
); );

View File

@ -21,13 +21,22 @@ export default router.post(
.db(sqlTableMap[type as "image" | "text" | "video"]) .db(sqlTableMap[type as "image" | "text" | "video"])
.whereNot("manufacturer", "other") .whereNot("manufacturer", "other")
.select("id", "manufacturer", "model"); .select("id", "manufacturer", "model");
const result: Record<string, any[]> = {}; const result: Record<string, any[]> = {};
const modelCache: Record<string, Set<string>> = {};
for (const row of modelLists) { for (const row of modelLists) {
if (!result[row.manufacturer]) { if (!result[row.manufacturer]) {
result[row.manufacturer] = []; result[row.manufacturer] = [];
modelCache[row.manufacturer] = new Set();
}
if (!modelCache[row.manufacturer].has(row.model)) {
result[row.manufacturer].push({ label: row.model, value: row.model });
modelCache[row.manufacturer].add(row.model);
} }
result[row.manufacturer].push({ label: row.model, value: row.model });
} }
res.status(200).send(success(result)); res.status(200).send(success(result));
}, },
); );

View File

@ -1,32 +0,0 @@
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("编辑成功"));
},
);

View File

@ -27,6 +27,10 @@ async function superResolutionAndSave(src: string, projectId: number, videoRatio
systemPrompt: "你的核心任务是将所给的图片超分到 1K ,不改变图片任何内容,仅改变分辨率", systemPrompt: "你的核心任务是将所给的图片超分到 1K ,不改变图片任何内容,仅改变分辨率",
prompt: "你的核心任务是将所给的图片超分到 1K ,不改变图片任何内容,仅改变分辨率", prompt: "你的核心任务是将所给的图片超分到 1K ,不改变图片任何内容,仅改变分辨率",
imageBase64: [await urlToBase64(src)], imageBase64: [await urlToBase64(src)],
taskClass: "分镜图超分",
name: `分镜图超分-${v4()}`,
describe: `原始图片链接: ${src}`,
projectId,
}, },
apiConfig, apiConfig,
); );
@ -37,7 +41,7 @@ async function superResolutionAndSave(src: string, projectId: number, videoRatio
await u.oss.writeFile(ossPath, buffer); await u.oss.writeFile(ossPath, buffer);
return { ossPath, base64: `data:image/jpg;base64,${base64Str}` }; return { ossPath, base64: `data:image/jpg;base64,${base64Str}` };
} }
// 图片超分
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({

View File

@ -5,6 +5,7 @@ import Storyboard from "@/agents/storyboard";
const router = express.Router(); const router = express.Router();
expressWs(router as unknown as Application); expressWs(router as unknown as Application);
// 分镜对话Agent
router.ws("/", async (ws, req) => { router.ws("/", async (ws, req) => {
let agent: Storyboard; let agent: Storyboard;

View File

@ -5,6 +5,7 @@ import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware"; import { validateFields } from "@/middleware/middleware";
const router = express.Router(); const router = express.Router();
//删除分镜
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({

View File

@ -99,9 +99,9 @@ const prompt = `
`; `;
async function urlToBase64(imageUrl: string): Promise<string> { async function urlToBase64(imageUrl: string): Promise<string> {
const response = await axios.get(imageUrl, { responseType: "arraybuffer" }); const response = await axios.get(imageUrl, { responseType: "arraybuffer" });
const contentType = response.headers["content-type"] || "image/png"; const contentType = response.headers["content-type"] || "image/jpg";
const base64 = Buffer.from(response.data, "binary").toString("base64"); const base64 = Buffer.from(response.data, "binary").toString("base64");
return `data:${contentType};base64,${base64}`; return `${base64}`;
} }
// 生成单个分镜提示 // 生成单个分镜提示
async function generateSingleVideoPrompt({ async function generateSingleVideoPrompt({
@ -113,6 +113,8 @@ async function generateSingleVideoPrompt({
storyboardPrompt: string; storyboardPrompt: string;
ossPath: string; ossPath: string;
}): Promise<{ content: string; time: number; name: string }> { }): Promise<{ content: string; time: number; name: string }> {
console.log("%c Line:116 🍭 ossPath", "background:#6ec1c2", ossPath);
const messages: any[] = [ const messages: any[] = [
{ {
role: "system", role: "system",
@ -134,8 +136,10 @@ async function generateSingleVideoPrompt({
]; ];
try { try {
console.log("%c Line:140 🍩", "background:#4fff4B");
const apiConfig = await u.getPromptAi("videoPrompt"); const apiConfig = await u.getPromptAi("videoPrompt");
console.log("%c Line:143 🍑", "background:#e41a6a");
const result = await u.ai.text.invoke( const result = await u.ai.text.invoke(
{ {
messages, messages,
@ -159,6 +163,7 @@ async function generateSingleVideoPrompt({
return result; return result;
} catch (err: any) { } catch (err: any) {
console.log("%c Line:167 🥤 err", "background:#465975", err);
console.error("generateSingleVideoPrompt 调用失败:", err?.message || err); console.error("generateSingleVideoPrompt 调用失败:", err?.message || err);
throw new Error(`生成视频提示词失败: ${err?.message || "未知错误"}`); throw new Error(`生成视频提示词失败: ${err?.message || "未知错误"}`);
} }

View File

@ -5,7 +5,7 @@ import { validateFields } from "@/middleware/middleware";
import { z } from "zod"; import { z } from "zod";
const router = express.Router(); const router = express.Router();
// 生成分镜 // 片编辑
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({

View File

@ -6,7 +6,7 @@ import { z } from "zod";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
const router = express.Router(); const router = express.Router();
// 上传对话图片 // 图片上传
export default router.post( export default router.post(
"/", "/",
validateFields({ validateFields({

View File

@ -4,50 +4,45 @@ import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware"; import { validateFields } from "@/middleware/middleware";
import { number, z } from "zod"; import { number, z } from "zod";
const router = express.Router(); const router = express.Router();
export default router.post(
export default router.get(
"/", "/",
validateFields({ validateFields({
projectName: z.string(), state: z.string().optional().nullable(),
taskName: z.string(), taskClass: z.string().optional().nullable(),
state: z.string(),
page: z.number(), page: z.number(),
limit: z.number(), limit: z.number(),
projectId: z.number(),
}), }),
async (req, res) => { async (req, res) => {
const { projectName, taskName, state, page = 1, limit = 10 }: any = req.query; const { taskClass, state, page = 1, limit = 10, projectId }: any = req.body;
const offset = (page - 1) * limit; const offset = (page - 1) * limit;
const data = await u const data = await u
.db("t_taskList") .db("t_myTasks")
.where("projectId", projectId)
.andWhere((qb) => { .andWhere((qb) => {
if (projectName) { if (taskClass) {
qb.andWhere("t_taskList.projectName", projectName); qb.andWhere("t_myTasks.taskClass", taskClass);
}
if (taskName) {
qb.andWhere("t_taskList.name", taskName);
} }
if (state) { if (state) {
qb.andWhere("t_taskList.state", state); qb.andWhere("t_myTasks.state", state);
} }
}) })
.select("*") .select("*")
.offset(offset) .offset(offset)
.limit(limit); .limit(limit);
const totalQuery = (await u const totalQuery = (await u
.db("t_taskList") .db("t_myTasks")
.where("projectId", projectId)
.andWhere((qb) => { .andWhere((qb) => {
if (projectName) { if (taskClass) {
qb.andWhere("t_taskList.projectName", projectName); qb.andWhere("t_myTasks.taskClass", taskClass);
}
if (taskName) {
qb.andWhere("t_taskList.name", taskName);
} }
if (state) { if (state) {
qb.andWhere("t_taskList.state", state); qb.andWhere("t_myTasks.state", state);
} }
}) })
.count("* as total") .count("* as total")
.first()) as any; .first()) as any;
res.status(200).send(success({ data, total: totalQuery?.total })); res.status(200).send(success({ data, total: totalQuery?.total }));
} },
); );

View File

@ -0,0 +1,17 @@
import express from "express";
import u from "@/utils";
import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { number, z } from "zod";
const router = express.Router();
export default router.post(
"/",
validateFields({
projectId: z.number(),
}),
async (req, res) => {
const data = await u.db("t_myTasks").where("projectId", req.body.projectId).select("taskClass").groupBy("taskClass");
res.status(200).send(success(data));
},
);

View File

@ -35,11 +35,18 @@ export default router.post(
return res.status(500).send(error("请先选择图片")); return res.status(500).send(error("请先选择图片"));
} }
const configData = await u.db("t_videoConfig").where("id", configId).first(); const configData = await u.db("t_videoConfig").where("id", configId).first();
if (!configData) { if (!configData) {
return res.status(500).send(error("视频配置不存在")); return res.status(500).send(error("视频配置不存在"));
} }
if (configData.manufacturer == "runninghub") { // 优先使用视频配置中的AI配置ID查询,查不到再使用传入的aiConfigId
let aiConfigData = null;
if (configData.aiConfigId) {
aiConfigData = await u.db("t_config").where("id", configData.aiConfigId).first();
}
if (!aiConfigData || !aiConfigData?.model) {
return res.status(500).send(error("模型不存在"));
}
if (aiConfigData.model?.includes("sora")) {
if (filePath.length > 1) { if (filePath.length > 1) {
const gridUrl = await sharpProcessingImage(filePath, projectId); const gridUrl = await sharpProcessingImage(filePath, projectId);
if (gridUrl) { if (gridUrl) {
@ -49,18 +56,6 @@ export default router.post(
} }
} }
// 优先使用视频配置中的AI配置ID查询,查不到再使用传入的aiConfigId
let aiConfigData = null;
if (configData.aiConfigId) {
aiConfigData = await u.db("t_config").where("id", configData.aiConfigId).first();
}
if (!aiConfigData) {
aiConfigData = await u.db("t_config").where("id", aiConfigId).first();
}
if (!aiConfigData) {
return res.status(500).send(error("模型配置不存在"));
}
// 过滤掉空值 // 过滤掉空值
let fileUrl = filePath.filter((p: string) => p && p.trim() !== ""); let fileUrl = filePath.filter((p: string) => p && p.trim() !== "");
@ -177,6 +172,10 @@ ${prompt}
resolution: resolution as any, resolution: resolution as any,
audio: audioEnabled, audio: audioEnabled,
mode: mode as any, mode: mode as any,
taskClass: "视频生成",
name: `视频生成-${videoId}`,
describe: `视频生成,时长${duration}秒,分辨率${resolution}`,
projectId,
}, },
{ {
baseURL: aiConfigData?.baseUrl!, baseURL: aiConfigData?.baseUrl!,
@ -197,7 +196,7 @@ ${prompt}
await u.db("t_video").where("id", videoId).update({ state: -1 }); await u.db("t_video").where("id", videoId).update({ state: -1 });
} }
} catch (err) { } catch (err) {
console.error(`视频生成失败 videoId=${videoId}:`, err); // console.error(`视频生成失败 videoId=${videoId}:`, (err as any).response);
await u await u
.db("t_video") .db("t_video")
.where("id", videoId) .where("id", videoId)

View File

@ -1,12 +1,203 @@
// @db-hash 0f9789bd5ad2eebd79bd502988efcb4e // @db-hash f991a54893850ab9ff4a67665391191d
//该文件由脚本自动生成,请勿手动修改 //该文件由脚本自动生成,请勿手动修改
export interface memories {
'access_count'?: number;
'content': string;
'created_at': number;
'embedding': string;
'id'?: string;
'type': string;
}
export interface o_agentDeploy {
'id'?: number;
'name'?: string | null;
'startTime'?: number | null;
}
export interface o_artStyle {
'id'?: number;
'name'?: string | null;
'styles'?: string | null;
}
export interface o_assets {
'id'?: number;
'name'?: string | null;
'startTime'?: number | null;
}
export interface o_chatHistory {
'data'?: string | null;
'id'?: number;
'novel'?: string | null;
'projectId'?: number | null;
'type'?: string | null;
}
export interface o_event {
'createTime'?: number | null;
'detail'?: string | null;
'id'?: number;
'name'?: string | null;
}
export interface o_eventChapter {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_image {
'assetsId'?: number | null;
'filePath'?: string | null;
'id'?: number;
'projectId'?: number | null;
'scriptId'?: number | null;
'state'?: string | null;
'type'?: string | null;
'videoId'?: number | null;
}
export interface o_model {
'id'?: number;
'name'?: string | null;
'startTime'?: number | null;
}
export interface o_myTasks {
'describe'?: string | null;
'id'?: number;
'model'?: string | null;
'projectId'?: number | null;
'reason'?: string | null;
'relatedObjects'?: string | null;
'startTime'?: number | null;
'state'?: string | null;
'taskClass'?: string | null;
}
export interface o_novel {
'chapter'?: string | null;
'chapterData'?: string | null;
'chapterIndex'?: number | null;
'createTime'?: number | null;
'id'?: number;
'projectId'?: number | null;
'reel'?: string | null;
}
export interface o_outline {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_outlineNovel {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_project {
'artStyle'?: string | null;
'createTime'?: number | null;
'id'?: number | null;
'intro'?: string | null;
'name'?: string | null;
'projectType'?: string | null;
'type'?: string | null;
'userId'?: number | null;
'videoRatio'?: string | null;
}
export interface o_prompts {
'code'?: string | null;
'customValue'?: string | null;
'defaultValue'?: string | null;
'id'?: number;
'name'?: string | null;
'parentCode'?: string | null;
'type'?: string | null;
}
export interface o_script {
'content'?: string | null;
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
'projectId'?: number | null;
}
export interface o_scriptAssets {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_scriptOutline {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_setting {
'id'?: number;
'imageModel'?: string | null;
'languageModel'?: string | null;
'projectId'?: number | null;
'tokenKey'?: string | null;
'userId'?: number | null;
}
export interface o_skills {
'id'?: number;
'name'?: string | null;
'startTime'?: number | null;
}
export interface o_storyboard {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_storyboardScript {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_user {
'id'?: number;
'name'?: string | null;
'password'?: string | null;
'tokenKey'?: string | null;
}
export interface o_vendorConfig {
'code'?: string | null;
'createTime'?: number | null;
'icon'?: string | null;
'id'?: number;
'inputs'?: string | null;
'inputValues'?: string | null;
'models'?: string | null;
'name'?: string | null;
'version'?: string | null;
}
export interface o_video {
'createTime'?: number | null;
'id'?: number;
'name'?: string | null;
}
export interface o_videoConfig {
'aiConfigId'?: number | null;
'audioEnabled'?: number | null;
'createTime'?: number | null;
'duration'?: number | null;
'endFrame'?: string | null;
'id'?: number;
'images'?: string | null;
'manufacturer'?: string | null;
'mode'?: string | null;
'projectId'?: number | null;
'prompt'?: string | null;
'resolution'?: string | null;
'scriptId'?: number | null;
'selectedResultId'?: number | null;
'startFrame'?: string | null;
'updateTime'?: number | null;
}
export interface t_aiModelMap { export interface t_aiModelMap {
'configId'?: number | null; 'configId'?: number | null;
'id'?: number; 'id'?: number;
'key'?: string | null; 'key'?: string | null;
'name'?: string | null; 'name'?: string | null;
} }
export interface t_artStyle {
'id'?: number;
'name'?: string | null;
'styles'?: string | null;
}
export interface t_assets { export interface t_assets {
'duration'?: string | null; 'duration'?: string | null;
'episode'?: string | null; 'episode'?: string | null;
@ -115,15 +306,6 @@ export interface t_storyline {
'novelIds'?: string | null; 'novelIds'?: string | null;
'projectId'?: number | null; 'projectId'?: number | null;
} }
export interface t_taskList {
'endTime'?: string | null;
'id'?: number;
'name'?: string | null;
'projectName'?: number | null;
'prompt'?: string | null;
'startTime'?: string | null;
'state'?: string | null;
}
export interface t_textModel { export interface t_textModel {
'id'?: number; 'id'?: number;
'image'?: number | null; 'image'?: number | null;
@ -182,7 +364,34 @@ export interface t_videoModel {
} }
export interface DB { export interface DB {
"memories": memories;
"o_agentDeploy": o_agentDeploy;
"o_artStyle": o_artStyle;
"o_assets": o_assets;
"o_chatHistory": o_chatHistory;
"o_event": o_event;
"o_eventChapter": o_eventChapter;
"o_image": o_image;
"o_model": o_model;
"o_myTasks": o_myTasks;
"o_novel": o_novel;
"o_outline": o_outline;
"o_outlineNovel": o_outlineNovel;
"o_project": o_project;
"o_prompts": o_prompts;
"o_script": o_script;
"o_scriptAssets": o_scriptAssets;
"o_scriptOutline": o_scriptOutline;
"o_setting": o_setting;
"o_skills": o_skills;
"o_storyboard": o_storyboard;
"o_storyboardScript": o_storyboardScript;
"o_user": o_user;
"o_vendorConfig": o_vendorConfig;
"o_video": o_video;
"o_videoConfig": o_videoConfig;
"t_aiModelMap": t_aiModelMap; "t_aiModelMap": t_aiModelMap;
"t_artStyle": t_artStyle;
"t_assets": t_assets; "t_assets": t_assets;
"t_chatHistory": t_chatHistory; "t_chatHistory": t_chatHistory;
"t_config": t_config; "t_config": t_config;
@ -195,7 +404,6 @@ export interface DB {
"t_script": t_script; "t_script": t_script;
"t_setting": t_setting; "t_setting": t_setting;
"t_storyline": t_storyline; "t_storyline": t_storyline;
"t_taskList": t_taskList;
"t_textModel": t_textModel; "t_textModel": t_textModel;
"t_user": t_user; "t_user": t_user;
"t_video": t_video; "t_video": t_video;

View File

@ -0,0 +1,35 @@
import "../type";
export function buildReqBody(input: ImageConfig, config: AIConfig) {
const size = input.size === "1K" ? "2K" : input.size;
const sizeMap: Record<string, Record<string, string>> = {
"16:9": {
"2K": "2848x1600",
"4K": "4096x2304",
},
"9:16": {
"2K": "1600x2848",
"4K": "2304x4096",
},
};
const fullPrompt = input.systemPrompt ? `${input.systemPrompt}\n\n${input.prompt}` : input.prompt;
const requestBody: Record<string, any> = {
model: config.model,
prompt: fullPrompt,
size: sizeMap[input.aspectRatio][size],
response_format: "url",
sequential_image_generation: "disabled",
stream: false,
watermark: false,
...(input.imageBase64 && { image: input.imageBase64 }),
};
return requestBody;
}
export function buildReqUrl(baseUrl: string) {
return {
requestUrl: `${baseUrl}/v1/images/generations`,
};
}

View File

@ -12,7 +12,8 @@ import other from "./owned/other";
import gemini from "./owned/gemini"; import gemini from "./owned/gemini";
import modelScope from "./owned/modelScope"; import modelScope from "./owned/modelScope";
import grsai from "./owned/grsai"; import grsai from "./owned/grsai";
import { tr } from "zod/locales";
import formal from "./owned/formal";
const urlToBase64 = async (url: string): Promise<string> => { const urlToBase64 = async (url: string): Promise<string> => {
const res = await axios.get(url, { responseType: "arraybuffer" }); const res = await axios.get(url, { responseType: "arraybuffer" });
const base64 = Buffer.from(res.data).toString("base64"); const base64 = Buffer.from(res.data).toString("base64");
@ -29,20 +30,32 @@ const modelInstance = {
// apimart: apimart, // apimart: apimart,
modelScope, modelScope,
other, other,
grsai grsai,
formal,
} as const; } as const;
export default async (input: ImageConfig, config: AIConfig) => { export default async (input: ImageConfig, config: AIConfig) => {
const { model, apiKey, baseURL, manufacturer } = { ...config }; const { model, apiKey, baseURL, manufacturer } = { ...config };
if (!config || !config?.model || !config?.apiKey || !config?.manufacturer) throw new Error("请检查模型配置是否正确"); if (!config || !config?.model || !config?.apiKey || !config?.manufacturer) throw new Error("请检查模型配置是否正确");
const manufacturerFn = modelInstance[manufacturer as keyof typeof modelInstance]; const manufacturerFn = modelInstance[manufacturer as keyof typeof modelInstance];
if (!manufacturerFn) if (!manufacturerFn) throw new Error("不支持的图片厂商"); if (!manufacturerFn) if (!manufacturerFn) throw new Error("不支持的图片厂商");
// if (manufacturer !== "other") { // if (manufacturer !== "other") {
// const owned = modelList.find((m) => m.model === model); // const owned = modelList.find((m) => m.model === model);
// if (!owned) throw new Error("不支持的模型"); // if (!owned) throw new Error("不支持的模型");
// } // }
//添加到任务中心
// const [taskId] = await u.db("t_myTasks").insert({
// taskClass: input.taskClass,
// relatedObjects: input.name,
// model: config?.model ? config.model : "未知模型",
// describe: input.describe ? input.describe : "无",
// state: "进行中",
// startTime: Date.now(),
// projectId: input.projectId,
// });
// 补充图片的 base64 内容类型字符串 // 补充图片的 base64 内容类型字符串
if (input.imageBase64 && input.imageBase64.length > 0) { if (input.imageBase64 && input.imageBase64.length > 0) {
input.imageBase64 = input.imageBase64.map((img) => { input.imageBase64 = input.imageBase64.map((img) => {
@ -66,9 +79,20 @@ export default async (input: ImageConfig, config: AIConfig) => {
return `data:image/png;base64,${img}`; return `data:image/png;base64,${img}`;
}); });
} }
try {
let imageUrl = await manufacturerFn(input, { model, apiKey, baseURL });
let imageUrl = await manufacturerFn(input, { model, apiKey, baseURL }); if (!input.resType) input.resType = "b64";
if (!input.resType) input.resType = "b64"; if (input.resType === "b64" && imageUrl.startsWith("http")) imageUrl = await urlToBase64(imageUrl);
if (input.resType === "b64" && imageUrl.startsWith("http")) imageUrl = await urlToBase64(imageUrl); // await u.db("t_myTasks").where("id", taskId).update({
return imageUrl; // state: "已完成",
// });
return imageUrl;
} catch (error: any) {
// await u.db("t_myTasks").where("id", taskId).update({
// state: "生成失败",
// reason: error.message,
// });
throw error;
}
}; };

View File

@ -0,0 +1,62 @@
import "../type";
import { generateImage, generateText, ModelMessage } from "ai";
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { pollTask } from "@/utils/ai/utils";
import u from "@/utils";
import axios from "axios";
import * as volcengine from "../adapter/volcengine";
const modelFn = {
volcengine,
} as const;
function template(replaceObj: Record<string, any>, url: string) {
return url.replace(/\{(\w+)\}/g, (match, varName) => {
return replaceObj.hasOwnProperty(varName) ? replaceObj[varName] : match;
});
}
export default async (input: ImageConfig, config: AIConfig): Promise<string> => {
if (!config.model) throw new Error("缺少Model名称");
if (!config.apiKey) throw new Error("缺少API Key");
const { requestUrl, queryUrl = null } = modelFn["volcengine"].buildReqUrl(config?.baseURL);
const taskBody = modelFn["volcengine"].buildReqBody(input, config);
const apiKey = config.apiKey.replace("Bearer ", "");
try {
const { data } = await axios.post(requestUrl, taskBody, { headers: { Authorization: `Bearer ${apiKey}` } });
if (queryUrl) {
if (data.code != "success") throw new Error(`任务提交失败: ${data || "未知错误"}`);
const taskId = data.data;
return await pollTask(async () => {
const { data: queryData } = await axios.get(template({ id: taskId }, queryUrl), {
headers: { Authorization: `Bearer ${apiKey}` },
});
const { status, result_url, fail_reason } = queryData.data || {};
if (status === "FAILURE") {
return { completed: false, error: fail_reason ?? "图片生成失败" };
}
if (status === "SUCCESS") {
return { completed: true, url: result_url };
}
return { completed: false };
});
} else {
return data.data[0]?.url;
}
} catch (error: any) {
const msg = u.error(error).message || "图片生成失败";
throw new Error(msg);
}
};
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}`;
}

View File

@ -6,7 +6,7 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
if (!config.model) throw new Error("缺少Model名称"); if (!config.model) throw new Error("缺少Model名称");
if (!config.apiKey) throw new Error("缺少API Key"); if (!config.apiKey) throw new Error("缺少API Key");
if (!input.prompt) throw new Error("缺少提示词"); if (!input.prompt) throw new Error("缺少提示词");
const options: any = {}; const options: any = {};
if (config.apiKey) options.apiKey = config.apiKey; if (config.apiKey) options.apiKey = config.apiKey;
if (config?.baseURL) options.baseURL = config.baseURL; if (config?.baseURL) options.baseURL = config.baseURL;
@ -42,7 +42,6 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
}, },
}, },
}, },
timeout: 60000,
}); });
if (!result.files.length) { if (!result.files.length) {

View File

@ -1,6 +1,9 @@
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 { createOpenAI, OpenAIProviderSettings } from "@ai-sdk/openai";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import axios from "axios"; import axios from "axios";
export default async (input: ImageConfig, config: AIConfig): Promise<string> => { export default async (input: ImageConfig, config: AIConfig): Promise<string> => {
@ -11,7 +14,7 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
const apiKey = config.apiKey.replace("Bearer ", ""); const apiKey = config.apiKey.replace("Bearer ", "");
const otherProvider = createOpenAICompatible({ const otherProvider = createOpenAICompatible({
name: "xixixi", name: "other",
baseURL: config.baseURL, baseURL: config.baseURL,
headers: { headers: {
Authorization: `Bearer ${apiKey}`, Authorization: `Bearer ${apiKey}`,
@ -28,6 +31,12 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
const fullPrompt = input.systemPrompt ? `${input.systemPrompt}\n\n${input.prompt}` : input.prompt; const fullPrompt = input.systemPrompt ? `${input.systemPrompt}\n\n${input.prompt}` : input.prompt;
const model = config.model; const model = config.model;
if (model.includes("gemini") || model.includes("nano")) { if (model.includes("gemini") || model.includes("nano")) {
// 对于 Gemini 模型,使用 Google provider 以支持 imageConfig 参数
const googleProvider = createGoogleGenerativeAI({
apiKey: apiKey,
baseURL: config.baseURL,
});
let promptData; let promptData;
if (input.imageBase64 && input.imageBase64.length) { if (input.imageBase64 && input.imageBase64.length) {
promptData = [{ role: "system", content: fullPrompt + `请直接输出图片` }]; promptData = [{ role: "system", content: fullPrompt + `请直接输出图片` }];
@ -35,15 +44,14 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
role: "user", role: "user",
content: input.imageBase64.map((i) => ({ content: input.imageBase64.map((i) => ({
type: "image", type: "image",
image: i, image: i.replace(/^data:image\/[^;]+;base64,/, ""),
})), })),
}); });
} else { } else {
promptData = fullPrompt + `请直接输出图片`; promptData = fullPrompt + `请直接输出图片`;
} }
const result = await generateText({ const result = await generateText({
model: otherProvider.languageModel(model), model: googleProvider.languageModel(model),
prompt: promptData as string | ModelMessage[], prompt: promptData as string | ModelMessage[],
providerOptions: { providerOptions: {
google: { google: {
@ -52,7 +60,6 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
? { aspectRatio: input.aspectRatio } ? { aspectRatio: input.aspectRatio }
: { aspectRatio: input.aspectRatio, imageSize: input.size }), : { aspectRatio: input.aspectRatio, imageSize: input.size }),
}, },
responseModalities: ["IMAGE"],
}, },
}, },
}); });
@ -105,7 +112,11 @@ export default async (input: ImageConfig, config: AIConfig): Promise<string> =>
size: sizeMap[input.size] ?? "1024x1024", size: sizeMap[input.size] ?? "1024x1024",
}); });
return image.base64; if (image.base64.startsWith("data:image/")) {
return image.base64;
} else {
return `data:image/png;base64,${image.base64}`;
}
} }
}; };

View File

@ -5,6 +5,10 @@ interface ImageConfig {
size: "1K" | "2K" | "4K"; size: "1K" | "2K" | "4K";
aspectRatio: string; aspectRatio: string;
resType?: "url" | "b64"; resType?: "url" | "b64";
taskClass: string;
name: string;
describe: string;
projectId: number;
} }
interface AIConfig { interface AIConfig {

View File

@ -35,11 +35,18 @@ const buildOptions = async (input: AIInput<any>, config: AIConfig = {}) => {
} }
if (!owned) throw new Error("不支持的厂商"); if (!owned) throw new Error("不支持的厂商");
const modelInstance = owned.instance({ apiKey, baseURL: baseURL!, name: "xixixi" }); const modelInstance = owned.instance({ apiKey: apiKey!, baseURL: baseURL! });
const maxStep = input.maxStep ?? (input.tools ? Object.keys(input.tools).length * 5 : undefined); const maxStep = input.maxStep ?? (input.tools ? Object.keys(input.tools).length * 5 : undefined);
const outputBuilders: Record<string, (schema: any) => any> = { const outputBuilders: Record<string, (schema: any) => any> = {
schema: (s) => { schema: (s) => {
const schemaPrompt = `\n请按照以下 schema 格式返回结果:\n${JSON.stringify(
z.toJSONSchema(z.object(s)),
null,
2,
)}\n请输出JSON格式Schema返回`;
input.system = (input.system ?? "") + schemaPrompt;
// 返回验证模式
return Output.object({ schema: z.object(s) }); return Output.object({ schema: z.object(s) });
}, },
object: () => { object: () => {
@ -47,14 +54,14 @@ const buildOptions = async (input: AIInput<any>, config: AIConfig = {}) => {
z.toJSONSchema(z.object(input.output)), z.toJSONSchema(z.object(input.output)),
null, null,
2, 2,
)}\n只返回结果Schema返回`; )}\n请输出JSON格式Schema返回`;
input.system = (input.system ?? "") + jsonSchemaPrompt; input.system = (input.system ?? "") + jsonSchemaPrompt;
// return Output.json(); // return Output.json();
}, },
}; };
const output = input.output ? (outputBuilders[owned.responseFormat]?.(input.output) ?? null) : null; const output = input.output ? (outputBuilders[owned.responseFormat]?.(input.output) ?? null) : null;
const chatModelManufacturer = ["volcengine", "other", "openai", "modelScope","grsai"]; const chatModelManufacturer = ["volcengine", "other", "openai", "modelScope", "grsai", "formal"];
const modelFn = chatModelManufacturer.includes(owned.manufacturer) ? (modelInstance as OpenAIProvider).chat(model!) : modelInstance(model!); const modelFn = chatModelManufacturer.includes(owned.manufacturer) ? (modelInstance as OpenAIProvider).chat(model!) : modelInstance(model!);
return { return {

View File

@ -21,8 +21,7 @@ interface Owned {
| typeof createZhipu | typeof createZhipu
| typeof createQwen | typeof createQwen
| typeof createGoogleGenerativeAI | typeof createGoogleGenerativeAI
| typeof createAnthropic | typeof createAnthropic;
| typeof createOpenAICompatible;
} }
const instanceMap = { const instanceMap = {
deepSeek: createDeepSeek, deepSeek: createDeepSeek,
@ -35,7 +34,8 @@ const instanceMap = {
modelScope: (options: OpenAIProviderSettings) => createOpenAI({ ...options, headers: { ...options?.headers, "X-ModelScope-Async-Mode": "true" } }), modelScope: (options: OpenAIProviderSettings) => createOpenAI({ ...options, headers: { ...options?.headers, "X-ModelScope-Async-Mode": "true" } }),
xai: createXai, xai: createXai,
other: createOpenAI, other: createOpenAI,
grsai:createOpenAI grsai: createOpenAI,
formal: createOpenAI,
}; };
const modelList: Owned[] = [ const modelList: Owned[] = [
// DeepSeek // DeepSeek

View File

@ -0,0 +1,39 @@
import sharp from "sharp";
import "../type";
import FormData from "form-data";
export async function buildReqBody(input: VideoConfig, config: AIConfig) {
const sizeMap: Record<string, string> = {
"16:9": "1280x720",
"9:16": "720x1280",
};
const formData = new FormData();
formData.append("model", config.model!);
formData.append("prompt", input.prompt);
formData.append("seconds", String(input.duration));
const size = sizeMap[input.aspectRatio] || "1280x720";
formData.append("size", size);
if (input.imageBase64 && input.imageBase64.length) {
const base64Data = input.imageBase64[0]!.replace(/^data:image\/\w+;base64,/, "");
const buffer = Buffer.from(base64Data, "base64");
// 解析尺寸
const [width, height] = size.split("x").map(Number);
// 使用 sharp 调整图片尺寸
const resizedBuffer = await sharp(buffer).resize(width, height, { fit: "cover" }).jpeg({ quality: 100 }).toBuffer();
formData.append("input_reference", resizedBuffer, { filename: "image.jpg", contentType: "image/jpeg" });
}
return formData;
}
export function buildReqUrl(baseUrl: string): { requestUrl: string; queryUrl: string; downLoadUrl: string } {
return {
requestUrl: `${baseUrl}/v1/videos`,
queryUrl: `${baseUrl}/v1/videos/{id}`,
downLoadUrl: `${baseUrl}/v1/videos/{id}/content`,
};
}

View File

@ -0,0 +1,26 @@
import "../type";
export function buildReqBody(input: VideoConfig, config: AIConfig) {
const requestBody: any = {
model: config.model,
...(input.imageBase64 && input.imageBase64.length ? { images: input.imageBase64 } : {}),
prompt: input.prompt,
duration: input.duration,
size: input.resolution,
metadata: {
aspect_ratio: input.aspectRatio,
audio: input?.audio ?? false,
off_peak: false,
},
};
console.log("%c Line:5 🍔 requestBody", "background:#465975", requestBody);
return requestBody;
}
export function buildReqUrl(baseUrl: string): { requestUrl: string; queryUrl: string } {
return {
requestUrl: `${baseUrl}/v1/video/generations`,
queryUrl: `${baseUrl}/v1/video/generations/{id}`,
};
}

View File

@ -0,0 +1,54 @@
import "../type";
export function buildReqBody(input: VideoConfig, config: AIConfig) {
const hasStartEndType = input.mode === "startEnd";
const images = input.imageBase64 || [];
// 判断是否为首尾帧模式(需要两张图且类型支持首尾帧)
const isStartEndMode = images.length === 2 && hasStartEndType;
// 构建图片内容
const imageContent = images.map((base64, index) => {
const item: Record<string, any> = {
type: "image_url",
image_url: { url: base64 },
};
if (isStartEndMode) {
item.role = index === 0 ? "first_frame" : "last_frame";
}
return item;
});
// // 构建请求体
// const requestBody: Record<string, any> = {
// model: config.model,
// content: [{ type: "text", text: input.prompt }, ...imageContent],
// duration: input.duration,
// resolution: input.resolution,
// watermark: false,
// };
const requestBody: any = {
model: config.model,
...(input.imageBase64 && input.imageBase64.length ? { images: input.imageBase64 } : {}),
prompt: input.prompt,
duration: input.duration,
size: input.resolution,
metadata: {
generate_audio: input?.audio ?? false,
ratio: input.aspectRatio,
image_roles: ["first_frame", "last_frame"],
},
};
// // 仅当模型支持音频时才添加 generate_audio 字段
// if (typeof input.audio == "boolean") {
// requestBody.generate_audio = input.audio ?? false;
// }
return requestBody;
}
export function buildReqUrl(baseUrl: string): { requestUrl: string; queryUrl: string } {
return {
requestUrl: `${baseUrl}/v1/video/generations`,
queryUrl: `${baseUrl}/v1/video/generations/{id}`,
};
}

View File

@ -0,0 +1,61 @@
import "../type";
export function buildReqBody(input: VideoConfig, config: AIConfig) {
const images = input.imageBase64 || [];
// 构建图片内容
const imageContent = images.map((base64, index) => {
const item: Record<string, any> = {
type: "image_url",
image: { url: base64 },
};
return item;
});
const sizeMap: Record<string, Record<string, string>> = {
"480p": {
"16:9": "832*480",
"9:16": "480*832",
},
"720p": {
"16:9": "1280*720",
"9:16": "720*1280",
},
"1080p": {
"16:9": "1920*1080",
"9:16": "1080*1920",
},
};
const hasStartEnd = input.mode == "startEnd";
const imageReq: Record<string, string> = {};
if (hasStartEnd && Array.isArray(images) && images.length) {
if (images[0]) imageReq.first_frame_url = images[0];
if (images[1]) imageReq.last_frame_url = images[1];
} else if (!hasStartEnd && Array.isArray(images) && images[0]) {
imageReq.img_url = images[0];
}
const resolutionKey = input.resolution;
const size = sizeMap[resolutionKey]?.[input.aspectRatio];
const requestBody: any = {
model: config.model,
...(imageReq?.img_url ? { input_reference: imageReq.img_url } : {}),
prompt: input.prompt,
size,
duration: input.duration,
metadata: {
...imageReq,
audio: input?.audio ?? false,
},
};
return requestBody;
}
export function buildReqUrl(baseUrl: string): { requestUrl: string; queryUrl: string } {
return {
requestUrl: `${baseUrl}/v1/video/generations`,
queryUrl: `${baseUrl}/v1/video/generations/{id}`,
};
}

View File

@ -12,6 +12,8 @@ import gemini from "./owned/gemini";
import apimart from "./owned/apimart"; import apimart from "./owned/apimart";
import other from "./owned/other"; import other from "./owned/other";
import grsai from "./owned/grsai"; import grsai from "./owned/grsai";
import formal from "./owned/formal";
const modelInstance = { const modelInstance = {
volcengine: volcengine, volcengine: volcengine,
kling: kling, kling: kling,
@ -21,7 +23,8 @@ const modelInstance = {
runninghub: runninghub, runninghub: runninghub,
apimart: apimart, apimart: apimart,
other: other, other: other,
grsai:grsai grsai: grsai,
formal: formal,
} as const; } as const;
export default async (input: VideoConfig, config?: AIConfig) => { export default async (input: VideoConfig, config?: AIConfig) => {
@ -32,7 +35,16 @@ export default async (input: VideoConfig, config?: AIConfig) => {
if (!manufacturerFn) if (!manufacturerFn) throw new Error("不支持的视频厂商"); if (!manufacturerFn) if (!manufacturerFn) throw new Error("不支持的视频厂商");
// const owned = modelList.find((m) => m.model === model); // const owned = modelList.find((m) => m.model === model);
// if (!owned) throw new Error("不支持的模型"); // if (!owned) throw new Error("不支持的模型");
//添加到任务中心
// const [taskId] = await u.db("t_myTasks").insert({
// taskClass: input.taskClass,
// relatedObjects: input.name,
// model: config?.model ? config.model : "未知模型",
// describe: input.describe ? input.describe : "无",
// state: "进行中",
// startTime: Date.now(),
// projectId: input.projectId,
// });
// 补充图片的 base64 内容类型字符串 // 补充图片的 base64 内容类型字符串
if (input.imageBase64 && input.imageBase64.length > 0) { if (input.imageBase64 && input.imageBase64.length > 0) {
input.imageBase64 = input.imageBase64.map((img) => { input.imageBase64 = input.imageBase64.map((img) => {
@ -59,9 +71,20 @@ export default async (input: VideoConfig, config?: AIConfig) => {
let videoUrl = await manufacturerFn(input, { model, apiKey, baseURL }); let videoUrl = await manufacturerFn(input, { model, apiKey, baseURL });
if (videoUrl) { if (videoUrl) {
const response = await axios.get(videoUrl, { responseType: "stream" }); try {
await u.oss.writeFile(input.savePath, response.data); const response = await axios.get(videoUrl, { responseType: "stream" });
return input.savePath; await u.oss.writeFile(input.savePath, response.data);
// await u.db("t_myTasks").where("id", taskId).update({
// state: "已完成",
// });
return input.savePath;
} catch (err: any) {
// await u.db("t_myTasks").where("id", taskId).update({
// state: "生成失败",
// reason: err.message,
// });
return videoUrl;
}
} }
return videoUrl; return videoUrl;
}; };

View File

@ -159,15 +159,6 @@ const modelList: Owned[] = [
audio: false, audio: false,
}, },
// ================== ViduQ3系列 ================== // ================== ViduQ3系列 ==================
// viduq3-pro 文生视频
{
manufacturer: "vidu",
model: "viduq3-pro",
durationResolutionMap: [{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], resolution: ["540p", "720p", "1080p"] }],
aspectRatio: ["16:9", "9:16", "3:4", "4:3", "1:1"],
type: ["text"],
audio: true,
},
// viduq3-pro 图生视频 // viduq3-pro 图生视频
{ {
manufacturer: "vidu", manufacturer: "vidu",
@ -187,14 +178,6 @@ const modelList: Owned[] = [
audio: false, audio: false,
}, },
// viduq2-pro 文生视频 // viduq2-pro 文生视频
{
manufacturer: "vidu",
model: "viduq2-pro",
durationResolutionMap: [{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }],
aspectRatio: ["16:9", "9:16", "3:4", "4:3", "1:1"],
type: ["text"],
audio: false,
},
// viduq2-pro 图生视频 // viduq2-pro 图生视频
{ {
manufacturer: "vidu", manufacturer: "vidu",
@ -205,14 +188,6 @@ const modelList: Owned[] = [
audio: false, audio: false,
}, },
// viduq2-turbo 文生视频 // viduq2-turbo 文生视频
{
manufacturer: "vidu",
model: "viduq2-turbo",
durationResolutionMap: [{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }],
aspectRatio: ["16:9", "9:16", "3:4", "4:3", "1:1"],
type: ["text"],
audio: false,
},
// viduq2-turbo 图生视频 // viduq2-turbo 图生视频
{ {
manufacturer: "vidu", manufacturer: "vidu",
@ -223,14 +198,6 @@ const modelList: Owned[] = [
audio: false, audio: false,
}, },
// viduq1 文生视频 // viduq1 文生视频
{
manufacturer: "vidu",
model: "viduq1",
durationResolutionMap: [{ duration: [5], resolution: ["1080p"] }],
aspectRatio: ["16:9", "9:16", "1:1"],
type: ["text"],
audio: false,
},
// viduq1 图生视频 // viduq1 图生视频
{ {
manufacturer: "vidu", manufacturer: "vidu",
@ -465,7 +432,7 @@ const modelList: Owned[] = [
type: ["singleImage", "text"], type: ["singleImage", "text"],
audio: false, audio: false,
}, },
// ================== Apimart 系列 ================== // ================== Apimart 系列 ==================
// sora // sora
{ {
manufacturer: "apimart", manufacturer: "apimart",

View File

@ -0,0 +1,169 @@
import "../type";
import { pollTask } from "@/utils/ai/utils";
import u from "@/utils";
import axios from "axios";
import path from "path";
import * as volcengine from "../adapter/volcengine";
import * as openai from "../adapter/openai";
import * as vidu from "../adapter/vidu";
import * as wan from "../adapter/wan";
// 适配器映射
const modelFn = {
volcengine,
vidu,
openai,
wan,
} as const;
// 模型名称到适配器的映射(精确匹配)
const modelMapping: Record<string, keyof typeof modelFn> = {
// Volcengine 火山引擎模型
"doubao-seedance-1-5-pro-251215": "volcengine",
"doubao-seedance-1-0-pro-250528": "volcengine",
"Seedance-2.0": "volcengine",
// Vidu 模型
ViduQ2: "vidu",
"ViduQ2-turbo": "vidu",
"ViduQ2-pro": "vidu",
"ViduQ3-pro": "vidu",
// OpenAI 模型
sora2: "openai",
"sora2-pro": "openai",
"gpt-video": "openai",
// 万象/Wan 模型
"Wan2.6-T2V": "wan",
"Wan2.6-I2V": "wan",
};
// 模型名称关键字到适配器的映射(模糊匹配)
const modelKeywords: Array<{ keywords: string[]; adapter: keyof typeof modelFn }> = [
{ keywords: ["doubao", "volcengine", "seedance"], adapter: "volcengine" },
{ keywords: ["vidu"], adapter: "vidu" },
{ keywords: ["sora", "openai", "gpt"], adapter: "openai" },
{ keywords: ["wan", "wanx"], adapter: "wan" },
];
/**
*
*/
function getModelAdapter(modelName: string) {
// 1. 先尝试精确匹配
const exactMatch = modelMapping[modelName.toLowerCase()];
if (exactMatch) {
return modelFn[exactMatch];
}
// 2. 尝试关键字模糊匹配
const lowerModelName = modelName.toLowerCase();
for (const { keywords, adapter } of modelKeywords) {
if (keywords.some((kw) => lowerModelName.includes(kw.toLowerCase()))) {
return modelFn[adapter];
}
}
// 3. 如果模型名称本身就是适配器名称
if (modelName in modelFn) {
return modelFn[modelName as keyof typeof modelFn];
}
return modelFn["wan"];
}
function template(replaceObj: Record<string, any>, url: string) {
return url.replace(/\{(\w+)\}/g, (match, varName) => {
return replaceObj.hasOwnProperty(varName) ? replaceObj[varName] : match;
});
}
export default async (input: VideoConfig, config: AIConfig): Promise<string> => {
if (!config.model) throw new Error("缺少Model名称");
if (!config.apiKey) throw new Error("缺少API Key");
// 根据模型名称获取对应的适配器
const modelAdapter = getModelAdapter(config.model);
const { requestUrl, queryUrl, downLoadUrl = null } = modelAdapter.buildReqUrl(config.baseURL);
const taskBody = await modelAdapter.buildReqBody(input, config);
const apiKey = config.apiKey.replace("Bearer ", "");
try {
const { data } = await axios.post(requestUrl, taskBody, { headers: { Authorization: `Bearer ${apiKey}` } });
console.log("%c Line:91 🌽 data", "background:#3f7cff", data);
const taskId = data.id ?? data.taskId ?? data.task_id ?? data.data;
if (!taskId) throw new Error(`任务提交失败: ${data ? JSON.stringify(data) : "未知错误"}`);
return await pollTask(async () => {
const { data: queryData } = await axios.get(template({ id: taskId }, queryUrl), {
headers: { Authorization: `Bearer ${apiKey}` },
});
console.log("%c Line:99 🥝 queryData", "background:#e41a6a", queryData);
// const { status, result_url, fail_reason } = queryData.data || {};
const status = queryData?.status ?? queryData?.data?.status;
const result_url = queryData?.metadata?.url ?? queryData?.data?.result_url;
const fail_reason = queryData?.data?.fail_reason ?? queryData?.data;
switch (status) {
case "completed":
case "SUCCESS":
case "success":
if (downLoadUrl) {
// 下载视频,带重试机制
let videoRes;
let retries = 3;
let lastError;
for (let i = 0; i < retries; i++) {
try {
// 构建下载URL
const finalDownloadUrl = downLoadUrl
? template({ id: taskId }, downLoadUrl)
: queryData.video_url || queryData.url || queryData.metadata.url; // 从响应中获取视频URL
videoRes = await axios.get(finalDownloadUrl, {
headers: { Authorization: `Bearer ${apiKey}` },
responseType: "arraybuffer",
timeout: 60 * 1000 * 10, // 60秒超时
});
break; // 成功则跳出循环
} catch (error) {
lastError = error;
console.error(`视频下载失败,第 ${i + 1}/${retries} 次尝试:`, error);
if (i < retries - 1) {
// 等待后重试,使用指数退避
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000));
}
}
}
if (!videoRes) {
throw new Error(`视频下载失败,已重试 ${retries} 次: ${lastError}`);
}
// 将视频buffer转换为base64或直接返回buffer
const savePath = input.savePath.endsWith(".mp4") ? input.savePath : path.join(input.savePath, `other_${Date.now()}.mp4`);
await u.oss.writeFile(input.savePath, videoRes.data);
return { completed: true, url: savePath };
} else {
return { completed: true, url: result_url };
}
}
if (status === "FAILURE") {
return { completed: false, error: fail_reason ? fail_reason : "视频生成失败" };
}
if (status === "SUCCESS") {
return { completed: true, url: result_url };
}
return { completed: false };
});
} catch (error: any) {
const msg = u.error(error).message || "图片生成失败";
throw new Error(msg);
}
};

View File

@ -2,58 +2,145 @@ import "../type";
import axios from "axios"; import axios from "axios";
import sharp from "sharp"; import sharp from "sharp";
import FormData from "form-data"; import FormData from "form-data";
import fs from "fs";
import path from "path";
import u from "@/utils";
import { pollTask, validateVideoConfig } from "@/utils/ai/utils"; import { pollTask, validateVideoConfig } from "@/utils/ai/utils";
import { createOpenAI } from "@ai-sdk/openai"; function template(replaceObj: Record<string, any>, url: string) {
import { experimental_generateVideo as generateVideo } from "ai"; return url.replace(/\{(\w+)\}/g, (match, varName) => {
return replaceObj.hasOwnProperty(varName) ? replaceObj[varName] : match;
});
}
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 authorization = `Bearer ${config.apiKey}`; const authorization = `Bearer ${config.apiKey}`;
const urls = config.baseURL.split("|");
const isThreeUrlMode = urls.length === 3;
console.log("%c Line:24 🌭 isThreeUrlMode", "background:#ed9ec7", isThreeUrlMode);
const formData = new FormData(); let requestUrl: string, queryUrl: string, downLoadUrl: string | undefined;
formData.append("model", config.model);
formData.append("prompt", input.prompt); if (isThreeUrlMode) {
formData.append("seconds", String(input.duration)); [requestUrl, queryUrl, downLoadUrl] = urls;
} else {
[requestUrl, queryUrl] = urls;
}
// 根据 aspectRatio 设置 size // 根据 aspectRatio 设置 size
const sizeMap: Record<string, string> = { const sizeMap: Record<string, string> = {
"16:9": "1280x720", "16:9": "1280x720",
"9:16": "720x1280", "9:16": "720x1280",
}; };
formData.append("size", sizeMap[input.aspectRatio] || "1920x1080"); let resData;
let taskId = "";
if (isThreeUrlMode) {
// 三个地址:使用 FormData 方式
const formData = new FormData();
formData.append("model", config.model);
formData.append("prompt", input.prompt);
formData.append("seconds", String(input.duration));
if (input.imageBase64 && input.imageBase64.length) { const size = sizeMap[input.aspectRatio] || "1280x720";
const base64Data = input.imageBase64[0]!.replace(/^data:image\/\w+;base64,/, ""); formData.append("size", size);
const buffer = Buffer.from(base64Data, "base64");
formData.append("input_reference", buffer, { filename: "image.jpg", contentType: "image/jpeg" });
}
const body = { if (input.imageBase64 && input.imageBase64.length) {
model: config.model, const base64Data = input.imageBase64[0]!.replace(/^data:image\/\w+;base64,/, "");
messages: [ const buffer = Buffer.from(base64Data, "base64");
{
role: "user", // 解析尺寸
content: [ const [width, height] = size.split("x").map(Number);
{
type: "text", // 使用 sharp 调整图片尺寸
text: input.prompt, const resizedBuffer = await sharp(buffer).resize(width, height, { fit: "cover" }).jpeg({ quality: 90 }).toBuffer();
},
], formData.append("input_reference", resizedBuffer, { filename: "image.jpg", contentType: "image/jpeg" });
}
const response = await axios.post(requestUrl, formData, {
headers: { Authorization: authorization, ...formData.getHeaders() },
});
taskId = response.data?.task_id || response.data?.id;
resData = response.data;
} else {
// 两个地址:使用 JSON 方式
const requestBody: any = {
model: config.model,
prompt: input.prompt,
aspect_ratio: input.aspectRatio || "16:9",
size: "720p",
};
if (input.imageBase64 && input.imageBase64.length) {
requestBody.images = input.imageBase64;
}
const response = await axios.post(requestUrl, JSON.stringify(requestBody), {
headers: {
Authorization: authorization,
"Content-Type": "application/json",
}, },
], });
}; taskId = response.data.id;
const { data } = await axios.post( resData = response.data;
config.baseURL, }
{ ...body }, console.log("%c Line:87 🥒 taskId", "background:#f5ce50", taskId);
{
headers: { "Content-Type": "application/json", Authorization: authorization },
},
);
console.log("%c Line:49 🥓 data", "background:#ffdd4d", data); if (!taskId) throw new Error(`任务提交失败: ${resData ? JSON.stringify(resData) : "未知错误"}`);
if (data.status === "FAILED") throw new Error(`任务提交失败: ${data.errorMessage || "未知错误"}`); return await pollTask(async () => {
// 构建查询URL两个地址模式时使用URL参数
const finalQueryUrl = isThreeUrlMode ? template({ id: taskId }, queryUrl) : `${queryUrl}?id=${taskId}`;
const { data: queryData } = await axios.get(finalQueryUrl, {
headers: { Authorization: authorization },
});
console.log("%c Line:100 🥑 queryData", "background:#42b983", queryData);
if (queryData.status === "completed") {
// 下载视频,带重试机制
let videoRes;
let retries = 3;
let lastError;
for (let i = 0; i < retries; i++) {
try {
// 构建下载URL
const finalDownloadUrl = isThreeUrlMode && downLoadUrl ? template({ id: taskId }, downLoadUrl) : queryData.video_url || queryData.url; // 从响应中获取视频URL
videoRes = await axios.get(finalDownloadUrl, {
headers: isThreeUrlMode ? { Authorization: authorization } : {},
responseType: "arraybuffer",
timeout: 60 * 1000 * 10, // 60秒超时
});
break; // 成功则跳出循环
} catch (error) {
lastError = error;
console.error(`视频下载失败,第 ${i + 1}/${retries} 次尝试:`, error);
if (i < retries - 1) {
// 等待后重试,使用指数退避
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000));
}
}
}
if (!videoRes) {
throw new Error(`视频下载失败,已重试 ${retries} 次: ${lastError}`);
}
// 将视频buffer转换为base64或直接返回buffer
const savePath = input.savePath.endsWith(".mp4") ? input.savePath : path.join(input.savePath, `other_${Date.now()}.mp4`);
await u.oss.writeFile(input.savePath, videoRes.data);
return { completed: true, url: savePath };
}
if (queryData.status === "failed") return { completed: false, error: `任务失败: ${queryData.error || "未知错误"}` };
// if (queryData.status === "QUEUED" || queryData.status === "RUNNING") return { completed: false };
return { completed: false };
});
}; };

View File

@ -67,6 +67,9 @@ export default async (input: VideoConfig, config: AIConfig) => {
Authorization: authorization, Authorization: authorization,
}, },
}); });
console.log("%c Line:65 🍢 response", "background:#3f7cff", response);
taskId = response.data.task_id; taskId = response.data.task_id;
} else { } else {
// 图生视频 // 图生视频
@ -89,6 +92,8 @@ export default async (input: VideoConfig, config: AIConfig) => {
Authorization: authorization, Authorization: authorization,
}, },
}); });
console.log("%c Line:90 🍷 response", "background:#2eafb0", response.data);
taskId = response.data.task_id; taskId = response.data.task_id;
} }
@ -103,6 +108,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
task_ids: [taskId], task_ids: [taskId],
}, },
}); });
console.log("%c Line:113 🧀 response.data", "background:#33a5ff", response.data);
const tasks = response.data.tasks; const tasks = response.data.tasks;
if (!tasks || tasks.length === 0) { if (!tasks || tasks.length === 0) {

View File

@ -21,6 +21,8 @@ export default async (input: VideoConfig, config: AIConfig) => {
}; };
if (isStartEndMode) { if (isStartEndMode) {
item.role = index === 0 ? "first_frame" : "last_frame"; item.role = index === 0 ? "first_frame" : "last_frame";
} else {
item.role = "reference_image";
} }
return item; return item;
}); });
@ -35,9 +37,10 @@ export default async (input: VideoConfig, config: AIConfig) => {
}; };
// 仅当模型支持音频时才添加 generate_audio 字段 // 仅当模型支持音频时才添加 generate_audio 字段
if (input?.audio) { if (typeof input?.audio == "boolean") {
requestBody.generate_audio = input.audio ?? false; requestBody.generate_audio = input.audio ?? false;
} }
// 创建视频生成任务 // 创建视频生成任务
const createResponse = await axios.post(baseUrl, requestBody, { const createResponse = await axios.post(baseUrl, requestBody, {
headers: { headers: {
@ -45,6 +48,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
Authorization: authorization, Authorization: authorization,
}, },
}); });
console.log("%c Line:44 🍡 createResponse", "background:#2eafb0", createResponse.data);
const taskId = createResponse.data.id; const taskId = createResponse.data.id;
@ -55,18 +59,29 @@ export default async (input: VideoConfig, config: AIConfig) => {
const data = await axios.get(`${baseUrl}/${taskId}`, { const data = await axios.get(`${baseUrl}/${taskId}`, {
headers: { Authorization: authorization }, headers: { Authorization: authorization },
}); });
console.log("%c Line:62 🥕 data.data", "background:#e41a6a", data.data);
const { status, content } = data.data; const { status, content, error } = data.data;
switch (status) { switch (status) {
case "succeeded": case "succeeded":
case "completed":
return { completed: true, url: content?.video_url }; return { completed: true, url: content?.video_url };
case "failed": case "failed":
case "cancelled": case "cancelled":
case "expired": case "expired":
return { completed: false, error: `任务${status}` }; let errorMsg = "";
try {
errorMsg = typeof error === "string" ? error : JSON.stringify(error);
} catch (e) {
errorMsg = error || "";
}
return { completed: false, error: `任务${status}: ${errorMsg}` };
case "queued": case "queued":
case "running": case "running":
case "unknown":
case "submit":
case "in_progress":
return { completed: false }; return { completed: false };
default: default:
return { completed: false, error: `未知状态: ${status}` }; return { completed: false, error: `未知状态: ${status}` };

View File

@ -7,6 +7,10 @@ interface VideoConfig {
imageBase64?: string[]; imageBase64?: string[];
audio?: boolean; audio?: boolean;
mode: "startEnd" | "multi" | "single" | "text"; mode: "startEnd" | "multi" | "single" | "text";
taskClass: string;
name: string;
projectId: number;
describe?: string;
} }
interface AIConfig { interface AIConfig {

View File

@ -88,6 +88,10 @@ export default async (images: Record<string, string>, directive: string, project
imageBase64: base64Images, imageBase64: base64Images,
aspectRatio: aspectRatio ? aspectRatio : "16:9", aspectRatio: aspectRatio ? aspectRatio : "16:9",
size: "1K", size: "1K",
taskClass: "图片编辑",
name: `图片编辑-${uuid()}`,
describe: `编辑指令: ${directive}`,
projectId,
}, },
apiConfig, apiConfig,
); );

File diff suppressed because it is too large Load Diff

716
yarn.lock

File diff suppressed because it is too large Load Diff