对齐火山 API 文档(Asset URI 小写、HEIC/HEIF、DeleteAsset) 素材库支持视频/音频上传(按类型分三区显示、前端校验、拖拽上传) @ 引用从素材组改为单个素材(搜索返回具体素材、即时数量/时长检查) ffmpeg 视频封面帧提取 + 音频时长读取(Celery 异步) 生产级安全修复(跨团队校验、异常信息脱敏、下载大小限制)
80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import { create } from 'zustand';
|
|
import { assetsApi } from '../lib/api';
|
|
import type { AssetGroup, AssetSearchResult } from '../types';
|
|
import { showToast } from '../components/Toast';
|
|
|
|
interface AssetLibraryState {
|
|
groups: AssetGroup[];
|
|
loading: boolean;
|
|
total: number;
|
|
page: number;
|
|
searchResults: AssetSearchResult[];
|
|
searching: boolean;
|
|
|
|
loadGroups: (page?: number) => Promise<void>;
|
|
searchAssets: (query: string) => Promise<void>;
|
|
createGroup: (name: string, file: File | null) => Promise<AssetGroup | null>;
|
|
pollAssetStatus: (assetId: number) => void;
|
|
}
|
|
|
|
export const useAssetLibraryStore = create<AssetLibraryState>((set) => ({
|
|
groups: [],
|
|
loading: false,
|
|
total: 0,
|
|
page: 1,
|
|
searchResults: [],
|
|
searching: false,
|
|
|
|
loadGroups: async (page = 1) => {
|
|
set({ loading: true });
|
|
try {
|
|
const { data } = await assetsApi.getGroups({ page, page_size: 20 });
|
|
set({ groups: data.results, total: data.total, page, loading: false });
|
|
} catch {
|
|
set({ loading: false });
|
|
}
|
|
},
|
|
|
|
searchAssets: async (query: string) => {
|
|
set({ searching: true });
|
|
try {
|
|
const { data } = await assetsApi.search(query);
|
|
set({ searchResults: data.results, searching: false });
|
|
} catch {
|
|
set({ searching: false });
|
|
}
|
|
},
|
|
|
|
createGroup: async (name: string, file: File | null) => {
|
|
const formData = new FormData();
|
|
formData.append('name', name);
|
|
if (file) formData.append('file', file);
|
|
try {
|
|
const { data } = await assetsApi.createGroup(formData);
|
|
showToast('角色创建成功');
|
|
return data;
|
|
} catch {
|
|
showToast('创建失败,请重试');
|
|
return null;
|
|
}
|
|
},
|
|
|
|
pollAssetStatus: (assetId: number) => {
|
|
const poll = async () => {
|
|
try {
|
|
const { data } = await assetsApi.pollStatus(assetId);
|
|
if (data.status === 'processing') {
|
|
setTimeout(poll, 3000);
|
|
} else if (data.status === 'active') {
|
|
showToast('素材处理完成');
|
|
} else if (data.status === 'failed') {
|
|
showToast(data.error_message || '素材处理失败');
|
|
}
|
|
} catch {
|
|
setTimeout(poll, 3000);
|
|
}
|
|
};
|
|
setTimeout(poll, 3000);
|
|
},
|
|
}));
|