auto-save 2026-05-18 23:55 (+5, ~9)
This commit is contained in:
87
src/lib/videoProviders.ts
Normal file
87
src/lib/videoProviders.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { VideoGenerationRequest, VideoGenerationResponse } from './types';
|
||||
|
||||
export const SEEDANCE_MODEL = process.env.SEEDANCE_MODEL || 'seedance-1-0-pro';
|
||||
const SEEDANCE_API_BASE = process.env.SEEDANCE_API_BASE || 'https://ark.cn-beijing.volces.com/api/v3';
|
||||
|
||||
function durationOrDefault(duration?: number): number {
|
||||
return Math.min(Math.max(duration ?? 6, 3), 10);
|
||||
}
|
||||
|
||||
function normalizeStatus(status?: string): VideoGenerationResponse['status'] {
|
||||
if (status === 'succeeded') return 'succeeded';
|
||||
if (status === 'failed') return 'failed';
|
||||
if (status === 'processing' || status === 'running') return 'processing';
|
||||
return 'submitted';
|
||||
}
|
||||
|
||||
export async function generateSeedanceVideo(opts: VideoGenerationRequest): Promise<VideoGenerationResponse> {
|
||||
const key = process.env.SEEDANCE_API_KEY;
|
||||
if (!key) throw new Error('SEEDANCE_API_KEY missing');
|
||||
if (!opts.prompt?.trim()) throw new Error('prompt required');
|
||||
|
||||
const content: Array<Record<string, unknown>> = [{ type: 'text', text: opts.prompt.trim() }];
|
||||
if (opts.imageUrl) {
|
||||
content.push({ type: 'image_url', image_url: { url: opts.imageUrl } });
|
||||
}
|
||||
|
||||
const res = await fetch(`${SEEDANCE_API_BASE}/contents/generations/tasks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: SEEDANCE_MODEL,
|
||||
content,
|
||||
duration: durationOrDefault(opts.duration),
|
||||
ratio: opts.ratio || '16:9',
|
||||
resolution: opts.resolution || '1080p',
|
||||
}),
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error(`Seedance ${res.status}: ${await res.text()}`);
|
||||
const raw = await res.json() as {
|
||||
id?: string;
|
||||
task_id?: string;
|
||||
status?: string;
|
||||
video_url?: string;
|
||||
output?: { video_url?: string; url?: string };
|
||||
};
|
||||
|
||||
return {
|
||||
provider: 'seedance',
|
||||
model: SEEDANCE_MODEL,
|
||||
taskId: raw.task_id || raw.id,
|
||||
status: normalizeStatus(raw.status),
|
||||
videoUrl: raw.video_url || raw.output?.video_url || raw.output?.url,
|
||||
raw,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSeedanceVideoTask(taskId: string): Promise<VideoGenerationResponse> {
|
||||
const key = process.env.SEEDANCE_API_KEY;
|
||||
if (!key) throw new Error('SEEDANCE_API_KEY missing');
|
||||
if (!taskId) throw new Error('taskId required');
|
||||
|
||||
const res = await fetch(`${SEEDANCE_API_BASE}/contents/generations/tasks/${encodeURIComponent(taskId)}`, {
|
||||
headers: { Authorization: `Bearer ${key}` },
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error(`Seedance ${res.status}: ${await res.text()}`);
|
||||
const raw = await res.json() as {
|
||||
id?: string;
|
||||
task_id?: string;
|
||||
status?: string;
|
||||
video_url?: string;
|
||||
output?: { video_url?: string; url?: string };
|
||||
};
|
||||
|
||||
return {
|
||||
provider: 'seedance',
|
||||
model: SEEDANCE_MODEL,
|
||||
taskId: raw.task_id || raw.id || taskId,
|
||||
status: normalizeStatus(raw.status),
|
||||
videoUrl: raw.video_url || raw.output?.video_url || raw.output?.url,
|
||||
raw,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user