拆镜复刻片段重试切片API | 真人/虚拟素材库上传API

This commit is contained in:
2026-07-09 12:07:28 +08:00
parent c05a52498e
commit 2b996e114e
18 changed files with 737 additions and 25 deletions
+41 -2
View File
@@ -165,6 +165,43 @@ export async function uploadVideo(file: File, durationSeconds?: number): Promise
return await res.json();
}
function privatePortraitUploadEndpoint(path: string, durationSeconds?: number): string {
const base = `${import.meta.env.VITE_API_BASE || 'http://localhost:8000'}/api${path}`;
const query = typeof durationSeconds === 'number' && durationSeconds > 0
? `?duration_seconds=${encodeURIComponent(String(durationSeconds))}`
: '';
return `${base}${query}`;
}
async function uploadPrivatePortraitFile(path: string, file: File, durationSeconds?: number, errorMessage = '素材上传失败'): Promise<UploadResourceResult> {
const form = new FormData();
form.append('file', file);
const token = localStorage.getItem('auth_token');
const res = await fetch(privatePortraitUploadEndpoint(path, durationSeconds), {
method: 'POST',
headers: token ? { Authorization: `Bearer ${token}` } : {},
body: form,
});
if (!res.ok) throw new Error(errorMessage);
return await res.json();
}
export async function uploadPrivatePortraitImage(file: File): Promise<UploadResourceResult> {
return uploadPrivatePortraitFile('/private-portrait/uploads/image', file, undefined, '真人图片素材上传失败');
}
export async function uploadPrivatePortraitVideo(file: File, durationSeconds?: number): Promise<UploadResourceResult> {
return uploadPrivatePortraitFile('/private-portrait/uploads/video', file, durationSeconds, '真人视频素材上传失败');
}
export async function uploadPrivatePortraitVirtualImage(file: File): Promise<UploadResourceResult> {
return uploadPrivatePortraitFile('/private-portrait/virtual/uploads/image', file, undefined, '虚拟图片素材上传失败');
}
export async function uploadPrivatePortraitVirtualVideo(file: File, durationSeconds?: number): Promise<UploadResourceResult> {
return uploadPrivatePortraitFile('/private-portrait/virtual/uploads/video', file, durationSeconds, '虚拟视频素材上传失败');
}
export async function uploadHotOpeningImage(file: File): Promise<UploadResourceResult> {
const form = new FormData();
form.append('file', file);
@@ -957,7 +994,7 @@ export async function getPrivatePortraitValidateSession(sessionId: string): Prom
return api.get<PrivatePortraitValidateSession>(`/private-portrait/validate-sessions/${sessionId}`);
}
export async function createPrivatePortraitAsset(projectId: string, payload: { url: string; assetType?: string; name?: string | null; videoDuration?: number | null; videoCoverUrl?: string | null; fileSize?: number | null; mimeType?: string | null }): Promise<PrivatePortraitAsset> {
export async function createPrivatePortraitAsset(projectId: string, payload: { url: string; assetType?: string; name?: string | null; videoDuration?: number | null; videoCoverUrl?: string | null; fileSize?: number | null; mimeType?: string | null; uploadResourceId?: string | null }): Promise<PrivatePortraitAsset> {
return api.post<PrivatePortraitAsset>(`/private-portrait/projects/${projectId}/assets`, {
url: payload.url,
asset_type: payload.assetType || 'Image',
@@ -966,6 +1003,7 @@ export async function createPrivatePortraitAsset(projectId: string, payload: { u
video_cover_url: payload.videoCoverUrl || null,
file_size: payload.fileSize ?? null,
mime_type: payload.mimeType || null,
upload_resource_id: payload.uploadResourceId || null,
});
}
@@ -1027,7 +1065,7 @@ export async function deletePrivatePortraitVirtualProject(projectId: string): Pr
await api.delete(`/private-portrait/virtual-projects/${projectId}`);
}
export async function createPrivatePortraitVirtualAsset(projectId: string, payload: { url: string; assetType?: string; name?: string | null; videoDuration?: number | null; videoCoverUrl?: string | null; fileSize?: number | null; mimeType?: string | null }): Promise<PrivatePortraitAsset> {
export async function createPrivatePortraitVirtualAsset(projectId: string, payload: { url: string; assetType?: string; name?: string | null; videoDuration?: number | null; videoCoverUrl?: string | null; fileSize?: number | null; mimeType?: string | null; uploadResourceId?: string | null }): Promise<PrivatePortraitAsset> {
return api.post<PrivatePortraitAsset>(`/private-portrait/virtual-projects/${projectId}/assets`, {
url: payload.url,
asset_type: payload.assetType || 'Image',
@@ -1036,6 +1074,7 @@ export async function createPrivatePortraitVirtualAsset(projectId: string, paylo
video_cover_url: payload.videoCoverUrl || null,
file_size: payload.fileSize ?? null,
mime_type: payload.mimeType || null,
upload_resource_id: payload.uploadResourceId || null,
});
}
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Button, Input, Modal, Space, Upload, message } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import type { UploadFile } from 'antd/es/upload/interface';
import { createPrivatePortraitAsset, uploadImage, uploadVideo } from '../../../api';
import { createPrivatePortraitAsset, uploadPrivatePortraitImage, uploadPrivatePortraitVideo } from '../../../api';
const MIN_PRIVATE_VIDEO_DURATION = 2;
const MAX_PRIVATE_VIDEO_DURATION = 15;
@@ -89,14 +89,15 @@ const PrivatePortraitAssetUpload: React.FC<Props> = ({ projectId, open, onClose,
return;
}
const uploaded = assetType === 'Video' ? await uploadVideo(file) : await uploadImage(file);
const uploaded = assetType === 'Video' ? await uploadPrivatePortraitVideo(file, videoDuration || undefined) : await uploadPrivatePortraitImage(file);
await createPrivatePortraitAsset(projectId, {
url: uploaded.url,
assetType,
name: name.trim() || file.name,
videoDuration,
fileSize: file.size,
videoDuration: uploaded.duration_seconds ?? videoDuration,
fileSize: uploaded.file_size_bytes ?? file.size,
mimeType: file.type || null,
uploadResourceId: uploaded.resource_id || null,
});
message.success(assetType === 'Video' ? '视频素材已提交入库,处理中' : '图片素材已提交入库,处理中');
reset();
@@ -35,8 +35,8 @@ import {
deletePrivatePortraitVirtualProject,
getPrivatePortraitVirtualAssets,
getPrivatePortraitVirtualProjects,
uploadImage,
uploadVideo,
uploadPrivatePortraitVirtualImage,
uploadPrivatePortraitVirtualVideo,
} from '../../../api';
import type { PrivatePortraitAsset, PrivatePortraitProject } from '../../../types';
@@ -276,14 +276,15 @@ const VirtualMaterialPanel: React.FC = () => {
if (currentType === 'Video' && !validatePrivateVideoDuration(duration, message.error)) {
return;
}
const uploaded = currentType === 'Video' ? await uploadVideo(file) : await uploadImage(file);
const uploaded = currentType === 'Video' ? await uploadPrivatePortraitVirtualVideo(file, duration || undefined) : await uploadPrivatePortraitVirtualImage(file);
await createPrivatePortraitVirtualAsset(selectedProjectId, {
url: uploaded.url,
assetType: currentType,
name: assetName.trim() || file.name,
videoDuration: duration,
fileSize: file.size,
videoDuration: uploaded.duration_seconds ?? duration,
fileSize: uploaded.file_size_bytes ?? file.size,
mimeType: file.type || null,
uploadResourceId: uploaded.resource_id || null,
});
message.success(currentType === 'Video' ? '视频素材已提交入库,处理中' : '图片素材已提交入库,处理中');
setUploadOpen(false);