Files
xiaoxia-saas/apps/web/src/api/assets.ts
T
Xiaoxia AI 37e849f90e
Deploy / Build Production Runtime Images (push) Successful in 9m8s
Deploy / Deploy Production (push) Successful in 55s
Deploy / Production Browser E2E (push) Successful in 36s
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
Deploy / Deploy Staging (push) Has been skipped
feat(assets): track generation usage review state
2026-06-23 21:52:47 +08:00

193 lines
5.3 KiB
TypeScript

/**
* 素材相关 API
*/
import apiClient from './client';
export interface AssetItem {
id: string;
workspace_id: string;
project_id: string;
library_id: string;
name: string;
storage_key: string;
mime_type: string;
metadata: Record<string, unknown>;
file_size?: number;
status?: string;
classification_status?: string | null;
quality_score?: number | null;
}
export interface AssetLibraryItem {
id: string;
workspace_id: string;
project_id: string;
name: string;
kind: 'video' | 'voice' | 'image';
asset_count?: number;
total_size?: number;
}
export interface IngestJob {
id: string;
workspace_id: string;
project_id: string;
library_id: string;
storage_key: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
error_message: string;
result_asset_id: string;
}
export interface ClassificationJob {
id: string;
workspace_id: string;
project_id: string;
asset_id: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
classification: string;
confidence: number;
error_message: string;
}
export interface ProjectAssetDiagnosis {
workspace_id: string;
project_id: string;
readiness_score: number;
readiness_label: string;
total_assets: number;
ready_assets: number;
video_assets: number;
image_assets: number;
voice_assets: number;
total_duration_seconds: number;
estimated_video_count: number;
used_assets: number;
unused_assets: number;
pending_review_assets: number;
smart_views: Array<{ key: string; label: string; count: number; description: string }>;
gaps: Array<{ key: string; severity: 'critical' | 'warning' | 'info'; message: string; recommendation: string }>;
}
export const getProjectAssetDiagnosis = async (projectId: string): Promise<ProjectAssetDiagnosis> => {
const response = await apiClient.get(`/projects/${projectId}/asset-diagnosis`);
return response.data;
};
export const getAssetLibraries = async (projectId: string): Promise<AssetLibraryItem[]> => {
const response = await apiClient.get('/asset-libraries', {
params: { project_id: projectId },
});
return response.data.items;
};
export const createAssetLibrary = async (data: {
workspace_id: string;
project_id: string;
name: string;
kind: 'video' | 'voice' | 'image';
}): Promise<AssetLibraryItem> => {
const response = await apiClient.post('/asset-libraries', data);
return response.data;
};
export const getAssets = async (libraryId: string): Promise<AssetItem[]> => {
const response = await apiClient.get('/assets', {
params: { library_id: libraryId },
});
return response.data.items;
};
export const uploadAsset = async (
formData: FormData
): Promise<{ storage_key: string; ingest_job_id: string; url: string }> => {
const response = await apiClient.post('/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 30 * 60 * 1000,
});
return response.data;
};
export const prepareDirectUpload = async (data: {
workspace_id: string;
project_id: string;
library_id: string;
filename: string;
content_type: string;
file_size: number;
}): Promise<{
upload_url: string;
method: string;
storage_key: string;
expires_at: string;
fields: Record<string, string>;
max_size_bytes: number;
}> => {
const response = await apiClient.post('/upload/direct/prepare', data);
return response.data;
};
export const completeDirectUpload = async (data: {
workspace_id: string;
project_id: string;
library_id: string;
storage_key: string;
}): Promise<{ storage_key: string; ingest_job_id: string }> => {
const response = await apiClient.post('/upload/direct/complete', data);
return response.data;
};
export const uploadAssetDirect = async (data: {
file: File;
workspace_id: string;
project_id: string;
library_id: string;
}): Promise<{ storage_key: string; ingest_job_id: string }> => {
const prepared = await prepareDirectUpload({
workspace_id: data.workspace_id,
project_id: data.project_id,
library_id: data.library_id,
filename: data.file.name,
content_type: data.file.type || 'application/octet-stream',
file_size: data.file.size,
});
const directForm = new FormData();
Object.entries(prepared.fields).forEach(([key, value]) => directForm.append(key, value));
directForm.append('file', data.file);
const uploadResponse = await fetch(prepared.upload_url, {
method: prepared.method,
body: directForm,
});
if (!uploadResponse.ok) {
throw new Error(`OSS direct upload failed: ${uploadResponse.status}`);
}
return completeDirectUpload({
workspace_id: data.workspace_id,
project_id: data.project_id,
library_id: data.library_id,
storage_key: prepared.storage_key,
});
};
export const getIngestJob = async (jobId: string): Promise<IngestJob> => {
const response = await apiClient.get(`/ingest-jobs/${jobId}`);
return response.data;
};
export const submitClassificationJob = async (data: {
workspace_id: string;
project_id: string;
asset_id: string;
}): Promise<ClassificationJob> => {
const response = await apiClient.post('/classification-jobs', data);
return response.data;
};
export const getClassificationJob = async (jobId: string): Promise<ClassificationJob> => {
const response = await apiClient.get(`/classification-jobs/${jobId}`);
return response.data;
};