feat(canvas): 添加视频模型配置和生成功能

- 在系统配置中新增 defaultVideoModel 字段用于设置默认视频模型
- 更新应用配置模态框以支持视频模型的选择和验证
- 修改画布节点逻辑以支持视频类型的生成配置
- 实现视频节点的生成和上传功能
- 更新模型选择器组件以支持视频模型选择
- 在管理后台设置页面添加视频模型配置选项
- 优化模型选择器的层级和交互体验
- 添加对 DeepSeek 和 GLM 模型图标的支持
This commit is contained in:
HouYunFei
2026-05-23 18:02:05 +08:00
parent 370263cd83
commit ef7772a703
9 changed files with 192 additions and 20 deletions
+22 -2
View File
@@ -1,6 +1,9 @@
import axios from "axios";
import { dataUrlToFile } from "@/lib/image-utils";
import { imageToDataUrl } from "@/services/image-storage";
import { buildApiUrl, type AiConfig } from "@/stores/use-config-store";
import type { ReferenceImage } from "@/types/image";
type VideoResponse = { id: string; status?: string; error?: { message?: string } };
@@ -12,9 +15,16 @@ function aiHeaders(config: AiConfig) {
return config.channelMode === "remote" ? undefined : { Authorization: `Bearer ${config.apiKey}` };
}
export async function requestVideoGeneration(config: AiConfig, prompt: string) {
export async function requestVideoGeneration(config: AiConfig, prompt: string, references: ReferenceImage[] = []) {
const model = config.model || config.videoModel;
const created = await axios.post<VideoResponse>(aiApiUrl(config, "/videos"), { model, prompt, size: config.size || undefined }, { headers: { ...(aiHeaders(config) || {}), "Content-Type": "application/json" } });
const body = new FormData();
body.append("model", model);
body.append("prompt", prompt);
body.append("seconds", normalizeVideoSeconds(config.videoSeconds));
if (normalizeVideoSize(config.size)) body.append("size", normalizeVideoSize(config.size)!);
if (config.vquality) body.append("vquality", config.vquality);
if (references[0]) body.append("input_reference", dataUrlToFile({ ...references[0], dataUrl: await imageToDataUrl(references[0]) }));
const created = await axios.post<VideoResponse>(aiApiUrl(config, "/videos"), body, { headers: aiHeaders(config) });
for (;;) {
const video = await axios.get<VideoResponse>(aiApiUrl(config, `/videos/${created.data.id}`), { headers: aiHeaders(config), params: config.channelMode === "remote" ? { model } : undefined });
if (video.data.status === "completed") break;
@@ -24,3 +34,13 @@ export async function requestVideoGeneration(config: AiConfig, prompt: string) {
const content = await axios.get<Blob>(aiApiUrl(config, `/videos/${created.data.id}/content`), { headers: aiHeaders(config), params: config.channelMode === "remote" ? { model } : undefined, responseType: "blob" });
return content.data;
}
function normalizeVideoSeconds(value: string) {
return String(Math.max(1, Math.floor(Number(value) || 6)));
}
function normalizeVideoSize(value: string) {
const size = value || "1280x720";
if (/^\d+x\d+$/.test(size)) return size;
return ["9:16", "2:3", "3:4"].includes(size) ? "720x1280" : "1280x720";
}