feat(ai): 自由创作视频生成全量移植(/free-create)——Seedance 文/图生视频+按时长计费+人物素材库
后端: - free_video.py: 提交/轮询/收藏/软删全链路,复用 AITask(新增 is_deleted/is_favorited, migration 0022) - video_pricing.py: 按时长 token 计费(×1.10 buffer+clamp); video_errors.py 错误归一; media_probe.py 时长探测 - catalog/volcano: Seedance free-video 模型接入+seed(migration 0023) - 素材库: FreeAssetGroup/FreeAsset(火山 Assets API 引用登记, migration 0009)+ 上传/轮询/删除接口 - settings: FREE_VIDEO_MAX_CONCURRENT 团队并发闸(默认3); CELERY_TASK_ALWAYS_EAGER 本地联调开关(生产恒关) - 测试: test_free_video.py 新增; billing/products/projects tests 配套调整 前端: - /free-create 页面+components/free-create/ 全套(输入栏/@mention 素材引用/生成卡/视频详情弹窗/素材库弹窗) - api.ts/types.ts 扩展 free-video 与 free-assets 接口; 路由/侧边栏入口接入 bug/: 测试清单 (11)(12) 与截图归档 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,11 @@ import type {
|
||||
AuthPayload,
|
||||
BillingSummary,
|
||||
BillingTrend,
|
||||
FreeAssetGroup,
|
||||
FreeAssetItem,
|
||||
FreeVideoRef,
|
||||
FreeVideoTask,
|
||||
FreeVideoUploadResult,
|
||||
Ledger,
|
||||
LoginSession,
|
||||
Invitation,
|
||||
@@ -660,6 +665,67 @@ export const api = {
|
||||
generateImageStatus(ids: string[]) {
|
||||
return request<{ tasks: { id: string; status: string; error_message: string; assets: Asset[] }[] }>(`/api/ai/generate-image/?ids=${encodeURIComponent(ids.join(","))}`);
|
||||
},
|
||||
// —— 自由创作·视频生成 ——
|
||||
// 提交秒回(火山 create 同步、慢轮询交给 worker 兜底 + 前端主动 poll);校验失败 400 带中文 detail。
|
||||
submitFreeVideo(payload: {
|
||||
prompt: string;
|
||||
mode: "universal" | "keyframe";
|
||||
model?: string;
|
||||
aspect_ratio?: string;
|
||||
resolution?: string;
|
||||
duration?: number;
|
||||
seed?: number;
|
||||
generate_audio?: boolean;
|
||||
references?: FreeVideoRef[];
|
||||
}) {
|
||||
return request<{ task: FreeVideoTask }>("/api/ai/free-video/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
freeVideoTasks(offset = 0, pageSize = 20) {
|
||||
return request<{ results: FreeVideoTask[]; total: number; has_more: boolean }>(
|
||||
`/api/ai/free-video/?offset=${offset}&page_size=${pageSize}`
|
||||
);
|
||||
},
|
||||
// web 进程内单次轮询+终态化(幂等):前端渐进轮询打这里,本地无 worker 也能收尾
|
||||
pollFreeVideo(id: string) {
|
||||
return request<{ task: FreeVideoTask }>(`/api/ai/free-video/${id}/poll/`, { method: "POST" });
|
||||
},
|
||||
toggleFreeVideoFavorite(id: string) {
|
||||
return request<{ is_favorited: boolean }>(`/api/ai/free-video/${id}/favorite/`, { method: "POST" });
|
||||
},
|
||||
deleteFreeVideo(id: string) {
|
||||
return request<void>(`/api/ai/free-video/${id}/`, { method: "DELETE" });
|
||||
},
|
||||
uploadFreeVideoRef(formData: FormData) {
|
||||
return request<FreeVideoUploadResult>("/api/ai/free-video/upload/", { method: "POST", body: formData });
|
||||
},
|
||||
// 自由创作·人物素材库(火山 Assets API asset:// 引用登记)
|
||||
freeAssetGroups() {
|
||||
return request<{ results: FreeAssetGroup[] }>("/api/assets/free-groups/");
|
||||
},
|
||||
createFreeAssetGroup(payload: { name: string; description?: string }) {
|
||||
return request<{ group: FreeAssetGroup }>("/api/assets/free-groups/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
freeAssetGroup(id: string) {
|
||||
return request<{ group: FreeAssetGroup; assets: FreeAssetItem[] }>(`/api/assets/free-groups/${id}/`);
|
||||
},
|
||||
updateFreeAssetGroup(id: string, payload: { name?: string; description?: string }) {
|
||||
return request<{ group: FreeAssetGroup }>(`/api/assets/free-groups/${id}/`, { method: "PATCH", body: JSON.stringify(payload) });
|
||||
},
|
||||
deleteFreeAssetGroup(id: string) {
|
||||
return request<void>(`/api/assets/free-groups/${id}/`, { method: "DELETE" });
|
||||
},
|
||||
uploadFreeAsset(groupId: string, formData: FormData) {
|
||||
return request<{ asset: FreeAssetItem }>(`/api/assets/free-groups/${groupId}/assets/`, { method: "POST", body: formData });
|
||||
},
|
||||
renameFreeAsset(id: string, name: string) {
|
||||
return request<{ asset: FreeAssetItem }>(`/api/assets/free-assets/${id}/`, { method: "PATCH", body: JSON.stringify({ name }) });
|
||||
},
|
||||
deleteFreeAsset(id: string) {
|
||||
return request<void>(`/api/assets/free-assets/${id}/`, { method: "DELETE" });
|
||||
},
|
||||
pollFreeAsset(id: string) {
|
||||
return request<{ asset: FreeAssetItem }>(`/api/assets/free-assets/${id}/poll/`, { method: "POST" });
|
||||
},
|
||||
recharge(payload: { amount: number | string; bonus?: number | string; channel?: string }) {
|
||||
return request<RechargeResult>("/api/billing/recharge/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user