feat(video): 视频页「导出全部」— 把项目所有视频片段打包成 zip 下载
- 后端 ProjectViewSet 加 export-clips action:逐段从 TOS 拉取已采用视频片段、内存 zip 打包返回下载 (单段失败跳过不毁整包;空项目 400);文件名走 RFC5987 UTF-8 编码兼容中文 - 前端视频阶段 queue-bar 在「上传视频」旁加「导出全部」按钮(下载图标 + 导出中/失败态), api.exportProjectClips 取 blob → 触发浏览器下载;仅有已完成片段时可点 - 单测 ExportClipsTests:打包全部片段 + 空项目 400 验收:后端单测绿(基线 4 失败零新增);tsc+build 绿;真打包 补水面膜 项目得 950KB zip;无头 0 报错 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -443,6 +443,19 @@ export const api = {
|
||||
if (metaKeys.length) qs.set("meta_keys", metaKeys.join(","));
|
||||
return request<{ sources: string[]; kinds: string[]; metadata: Record<string, string[]> }>(`/api/assets/facets/?${qs.toString()}`);
|
||||
},
|
||||
// 导出全部:把项目所有已采用视频片段打成一个 zip 下载(后端逐段从 TOS 拉取打包)
|
||||
async exportProjectClips(projectId: string): Promise<Blob> {
|
||||
const token = getToken();
|
||||
const res = await fetch(`${API_BASE}/api/projects/${projectId}/export-clips/`, {
|
||||
headers: token ? { Authorization: `Token ${token}` } : undefined
|
||||
});
|
||||
if (!res.ok) {
|
||||
let msg = `导出失败 (${res.status})`;
|
||||
try { msg = (await res.json()).detail || msg; } catch { /* 非 JSON 错误体 */ }
|
||||
throw new ApiError(res.status, msg);
|
||||
}
|
||||
return res.blob();
|
||||
},
|
||||
// 经后端同源代理取资产原始文件(TOS 未配 CORS,浏览器抽帧/解码必须同源)
|
||||
async fetchAssetBlob(id: string): Promise<Blob> {
|
||||
const token = getToken();
|
||||
|
||||
Reference in New Issue
Block a user