Skip to content

Commit 6c5cf18

Browse files
committed
feat: all vidu task
1 parent 290748b commit 6c5cf18

File tree

3 files changed

+696
-80
lines changed

3 files changed

+696
-80
lines changed

app/client/ViduProxy.ts

Lines changed: 257 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// app/client/ViduProxy.ts
22

3-
import {SunoEndpoint, ViduEndpoint} from "@/constant";
3+
import {ViduEndpoint} from "@/constant";
44
import {api2ProviderBaseUrl} from "@/app/store";
55
import {getRequestOptions} from "@/app/client/helper";
66

@@ -12,17 +12,185 @@ export interface ViduTaskGenerationRequest {
1212
enhance: boolean;
1313
}[];
1414
};
15-
type: string | "img2video";
15+
type: keyof typeof ViduTaskType;
1616
settings: {
17-
style: string;
17+
style: keyof typeof ViduStyle;
1818
aspect_ratio: string;
19-
duration: number;
20-
model: string | "vidu-1";
19+
duration: 4 | 8;
20+
model: "vidu-1" | "stable";
2121
};
2222
}
2323

2424
export interface ViduTaskGenerationResponse {
25+
id: string;
26+
input: {
27+
creation_id: string;
28+
prompts: {
29+
type: string;
30+
content: string;
31+
negative: boolean;
32+
enhance: boolean;
33+
recaption: string;
34+
}[];
35+
lang: "zh" | "en";
36+
};
37+
settings: ViduTaskGenerationRequest["settings"];
38+
type: ViduTaskGenerationRequest["type"];
39+
state: "queueing" | "processing" | "success" | string;
40+
creations: {
41+
id: string;
42+
task_id: string;
43+
type: "video" | string;
44+
grade: "draft" | string;
45+
uri: string;
46+
cover_uri: string;
47+
resolution: {
48+
width: number;
49+
height: number;
50+
};
51+
vote: "unspecified" | string;
52+
is_favor: boolean;
53+
src_video_duration: number;
54+
creator_id: string;
55+
video: {
56+
duration: number;
57+
fps: number;
58+
resolution: {
59+
width: number;
60+
height: number;
61+
};
62+
};
63+
is_deleted: boolean;
64+
err_code: string;
65+
created_at: string;
66+
}[];
67+
err_code: string;
68+
created_at: string;
69+
}
70+
71+
export interface ViduUpscaleTaskRequest {
72+
input: {
73+
creation_id: string;
74+
};
75+
type: "upscale";
76+
settings: {
77+
model: "stable";
78+
duration: 4 | 8;
79+
};
80+
}
81+
82+
export enum ViduTaskType {
83+
upscale = 'upscale',
84+
text2video = 'text2video', // 无图片时
85+
img2video = 'img2video', // 图片用途:用作起始帧(Use as First Frame)
86+
character2video = 'character2video', // 图片用途:参考人物角色(Use for Character Reference)
87+
}
2588

89+
export enum ViduModel {
90+
stable = 'stable',
91+
vidu1 = 'vidu-1',
92+
}
93+
94+
export enum ViduStyle {
95+
general = 'general',
96+
anime = 'anime',
97+
}
98+
99+
const vidu_task_generation_response_queueing_example: ViduTaskGenerationResponse = {
100+
"id": "2375606842092581",
101+
"input": {
102+
"creation_id": "0",
103+
"prompts": [
104+
{
105+
"type": "text",
106+
"content": "cloud",
107+
"negative": false,
108+
"enhance": false,
109+
"recaption": ""
110+
},
111+
{
112+
"type": "image",
113+
"content": "https://vidu.cf.vidu.studio/infer/tasks/24/0805/04/2375606842092581/input/prompt-02.jpeg?Expires=1722918735&Signature=ihq-BYHZm8TKhCcT3bTiv0ryQNyiNkizZYT1crgCinMxPtMVTfkLcXWWHwTKzrPxSebHFshqd7dgou1mjf0MbG8anwTxZdohytf6xjlPbFL94tYnx5YdAfwuwQX0-pJ7rcSY3VFqqbMNFGyHsu2h4OINGV0GEYn~bKPoRbkwYyVwN6XcLEeabxByCKhpw75b8~4cOSr4NH~YhOSfk~~6JWwqdezafxj7g823P0tVXms3TolK0e1FWu7Y~jJ0MmBdsfwcRrvDC5yjuWKPFb10dH32QOjKHY3QP1zLQXkD4QES11ZA6sCv4aq9j0MLQmq97mJSIRczRmCzH8ZP~0AiRQ__&Key-Pair-Id=KNW0SL0E7LV4E",
114+
"negative": false,
115+
"enhance": false,
116+
"recaption": ""
117+
}
118+
],
119+
"lang": "en"
120+
},
121+
"settings": {
122+
"style": "general",
123+
"duration": 4,
124+
"aspect_ratio": "16:9",
125+
"model": "vidu-1"
126+
},
127+
"type": "img2video",
128+
"state": "queueing",
129+
"creations": [],
130+
"err_code": "",
131+
"created_at": "2024-08-05T04:32:14.795273Z"
132+
}
133+
const vidu_task_generation_response_success_example: ViduTaskGenerationResponse = {
134+
"id": "2375646005579044",
135+
"input": {
136+
"creation_id": "0",
137+
"prompts": [
138+
{
139+
"type": "text",
140+
"content": "在经典的西部风格中,一个表情忧郁的男人凝视着镜头,微微颤抖的嘴唇吐出一口烟雾。他的香烟发出的温暖光芒照亮了他的脸,投下的阴影强调了他的忧郁情绪。他戴着宽边帽,穿着粗犷的衣服,配有弹带。他的靴子放在栏杆上,姿态放松,但眼神中透露出更深的悲伤。",
141+
"negative": false,
142+
"enhance": false,
143+
"recaption": ""
144+
}
145+
],
146+
"lang": "zh"
147+
},
148+
"settings": {
149+
"style": "general",
150+
"duration": 4,
151+
"aspect_ratio": "16:9",
152+
"model": "vidu-1"
153+
},
154+
"type": "text2video",
155+
"state": "success",
156+
"creations": [
157+
{
158+
"id": "2375646502238865",
159+
"task_id": "2375646005579044",
160+
"type": "video",
161+
"grade": "draft",
162+
"uri": "https://vidu.cf.vidu.studio/infer/tasks/24/0805/05/2375646005579044/creation-01/watermarked.mp4?Expires=1722838357&Signature=m4WAZQ~M3Xcwv2z3EBdHrRXWHakBXhxlaeCcI9DxjsDMf~DbcBS0h6S29A-EtymTyEUkYHJ6jFSIBt0kQitGvluT7oN5rA1H6zR~tVSUd6FM2jjRQg4FR0ff5P48I0pUtbnLNcyr2Kb-f873gNkm3WS78JTpsk0SZ1yObTFqoVBTaA2PX7FyttuEYES2Z8YrMPEVMRta0W9iV9GgB12N3cqzWrMAW9uNHftqH0T78Al4PCk9xv3TnjdZw9H3i3wyIE6kpBTfWeh2zBtNm2zzN6u5BGsX6ujPxX72X9JeRCIUpE~Rsj0dTPwJDARJhVPoTLbwvnchPdoOn3zHWQUfFA__&Key-Pair-Id=KNW0SL0E7LV4E&response-content-disposition=attachment;filename=vidu-1-general-4-2024-08-05T05%3A12%3A35Z.mp4",
163+
"cover_uri": "https://vidu.cf.vidu.studio/infer/tasks/24/0805/05/2375646005579044/creation-01/cover.jpeg?Expires=1722838357&Signature=ppjg5IrEjp45-zU4w5H2cH~~ZP7AW4-n9kBpS0uOQnl2DiuWIAw6y517F9jk6smVu8Q8R~Mm0nscAvdCrNABc-p~lGmYmzBBSJknjrGZVjSlZuVifuzKDR2C93qI63A1ZOemXBDUs6Ogx0Z7qqanG4HthUj1yOrAXmdWQ-Fk1pjptbBfuPwKZUDrbfVpiJdQsux6tTrYRkK1esJU~1ooIGS-NXar20FD36qGbFycQ1dCs~eQhn1vCQJ63gDkhPhcd7HDrxaZDdsACNtpF-pQi-br7Ixo4GomM~LoSblA2tohPl2e1HH8G4L0zCvLEVj6mORbJU-R2Nj6YZ8UipErNw__&Key-Pair-Id=KNW0SL0E7LV4E",
164+
"resolution": {
165+
"width": 688,
166+
"height": 384
167+
},
168+
"vote": "unspecified",
169+
"is_favor": false,
170+
"src_video_duration": 0,
171+
"creator_id": "0",
172+
"video": {
173+
"duration": 3.85,
174+
"fps": 16,
175+
"resolution": {
176+
"width": 688,
177+
"height": 384
178+
}
179+
},
180+
"is_deleted": false,
181+
"err_code": "",
182+
"created_at": "2024-08-05T05:12:35.458210Z"
183+
}
184+
],
185+
"err_code": "",
186+
"created_at": "2024-08-05T05:12:05.145617Z"
187+
}
188+
const vidu_task_upscale_request_example: ViduUpscaleTaskRequest = {
189+
"input": {
190+
"creation_id": "2375584778357517"
191+
},
192+
"type": "upscale",
193+
"settings": {"model": "stable", "duration": 4}
26194
}
27195

28196
export class ViduAPI {
@@ -36,6 +204,66 @@ export class ViduAPI {
36204
return [api2ProviderBaseUrl.Vidu, endpoint].join("/");
37205
}
38206

207+
// 根据用户输入的提示,返回可选用的任务类型
208+
availableTaskTypes(prompts: ViduTaskGenerationRequest["input"]["prompts"], isUpscale = false) {
209+
if (isUpscale) {
210+
return [ViduTaskType.upscale];
211+
}
212+
if (Array.isArray(prompts)) {
213+
214+
if (prompts.length === 0) {
215+
return Object.values(ViduTaskType);
216+
}
217+
218+
if (prompts.length === 1) {
219+
const prompt = prompts[0];
220+
if (prompt.type === "text") {
221+
return [ViduTaskType.text2video];
222+
}
223+
224+
if (prompt.type === "image") {
225+
return [ViduTaskType.img2video, ViduTaskType.character2video];
226+
}
227+
}
228+
229+
if (prompts.length === 2) {
230+
return [ViduTaskType.img2video, ViduTaskType.character2video];
231+
}
232+
}
233+
234+
return [];
235+
}
236+
237+
// 根据用户输入的提示,返回可选用的风格
238+
availableStyles(prompts: ViduTaskGenerationRequest["input"]["prompts"]) {
239+
if (Array.isArray(prompts)) {
240+
if (prompts.length === 0) {
241+
return Object.values(ViduStyle);
242+
}
243+
244+
if (prompts.length === 1) {
245+
const prompt = prompts[0];
246+
if (prompt.type === "text") {
247+
return [ViduStyle.general, ViduStyle.anime];
248+
}
249+
250+
if (prompt.type === "image") {
251+
return [ViduStyle.general,];
252+
}
253+
}
254+
255+
if (prompts.length === 2) {
256+
return [ViduStyle.general];
257+
}
258+
}
259+
260+
return [];
261+
}
262+
263+
availableModels(isUpscale: boolean) {
264+
return isUpscale ? [ViduModel.stable] : [ViduModel.vidu1];
265+
}
266+
39267
async generateViduTask(request: ViduTaskGenerationRequest, signal?: AbortSignal, timeoutMs: number = 10000) {
40268
const controller = new AbortController();
41269
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
@@ -58,7 +286,29 @@ export class ViduAPI {
58286
}
59287
}
60288

61-
async getViduTask(id: string, signal?: AbortSignal, timeoutMs: number = 10000) {
289+
async submitUpscaleTask(request: ViduUpscaleTaskRequest, signal?: AbortSignal, timeoutMs: number = 10000) {
290+
const controller = new AbortController();
291+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
292+
const abortSignal = signal || controller.signal;
293+
294+
signal && signal.addEventListener("abort", () => controller.abort());
295+
296+
try {
297+
const res = await fetch(this.path(ViduEndpoint.GENERATION), {
298+
...getRequestOptions(this.apiKey, request),
299+
signal: abortSignal
300+
});
301+
302+
clearTimeout(timeoutId);
303+
304+
return res;
305+
} catch (e) {
306+
console.error("[ViduProxy] failed to make a vidu upscale-task request", e);
307+
throw e;
308+
}
309+
}
310+
311+
async getViduTask(id: ViduTaskGenerationResponse["id"], signal?: AbortSignal, timeoutMs: number = 10000) {
62312
const controller = new AbortController();
63313
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
64314
const abortSignal = signal || controller.signal;
@@ -67,7 +317,7 @@ export class ViduAPI {
67317

68318
try {
69319
const res = await fetch(this.path(ViduEndpoint.TASK_GET).replace("{{id}}", id), {
70-
...getRequestOptions(this.apiKey,"GET"),
320+
...getRequestOptions(this.apiKey, "GET"),
71321
signal: abortSignal
72322
});
73323

0 commit comments

Comments
 (0)