317 lines
8.5 KiB
TypeScript
317 lines
8.5 KiB
TypeScript
import { create } from 'zustand';
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
import {
|
|
Material,
|
|
MaterialStats,
|
|
CreateMaterialRequest,
|
|
MaterialImportResult,
|
|
ProcessingStatus,
|
|
MaterialProcessingConfig
|
|
} from '../types/material';
|
|
|
|
/**
|
|
* 素材状态管理
|
|
* 遵循 Tauri 开发规范的状态管理模式
|
|
*/
|
|
interface MaterialState {
|
|
// 状态
|
|
materials: Material[];
|
|
currentMaterial: Material | null;
|
|
stats: MaterialStats | null;
|
|
isLoading: boolean;
|
|
isImporting: boolean;
|
|
isProcessing: boolean;
|
|
error: string | null;
|
|
importProgress: {
|
|
current_file: string;
|
|
processed_count: number;
|
|
total_count: number;
|
|
current_status: string;
|
|
errors: string[];
|
|
} | null;
|
|
|
|
// 操作
|
|
loadMaterials: (projectId: string) => Promise<void>;
|
|
loadMaterialStats: (projectId: string) => Promise<void>;
|
|
importMaterials: (request: CreateMaterialRequest) => Promise<MaterialImportResult>;
|
|
getMaterialById: (id: string) => Promise<Material | null>;
|
|
deleteMaterial: (id: string) => Promise<void>;
|
|
processMaterials: (materialIds: string[]) => Promise<void>;
|
|
updateMaterialStatus: (id: string, status: ProcessingStatus, errorMessage?: string) => Promise<void>;
|
|
cleanupInvalidMaterials: (projectId: string) => Promise<string>;
|
|
setCurrentMaterial: (material: Material | null) => void;
|
|
clearError: () => void;
|
|
|
|
// 工具方法
|
|
selectMaterialFiles: () => Promise<string[]>;
|
|
validateMaterialFiles: (filePaths: string[]) => Promise<string[]>;
|
|
getFileInfo: (filePath: string) => Promise<any>;
|
|
checkFFmpegAvailable: () => Promise<boolean>;
|
|
getFFmpegVersion: () => Promise<string>;
|
|
getSupportedExtensions: () => Promise<string[]>;
|
|
}
|
|
|
|
export const useMaterialStore = create<MaterialState>((set, get) => ({
|
|
// 初始状态
|
|
materials: [],
|
|
currentMaterial: null,
|
|
stats: null,
|
|
isLoading: false,
|
|
isImporting: false,
|
|
isProcessing: false,
|
|
error: null,
|
|
importProgress: null,
|
|
|
|
// 加载项目素材
|
|
loadMaterials: async (projectId: string) => {
|
|
set({ isLoading: true, error: null });
|
|
try {
|
|
const materials = await invoke<Material[]>('get_project_materials', { projectId });
|
|
set({ materials, isLoading: false });
|
|
} catch (error) {
|
|
set({
|
|
error: error as string,
|
|
isLoading: false,
|
|
materials: []
|
|
});
|
|
}
|
|
},
|
|
|
|
// 加载素材统计
|
|
loadMaterialStats: async (projectId: string) => {
|
|
try {
|
|
const stats = await invoke<MaterialStats>('get_project_material_stats', { projectId });
|
|
set({ stats });
|
|
} catch (error) {
|
|
console.error('加载素材统计失败:', error);
|
|
}
|
|
},
|
|
|
|
// 导入素材
|
|
importMaterials: async (request: CreateMaterialRequest) => {
|
|
set({ isImporting: true, error: null, importProgress: {
|
|
current_file: '',
|
|
processed_count: 0,
|
|
total_count: request.file_paths.length,
|
|
current_status: '准备导入...',
|
|
errors: []
|
|
}});
|
|
|
|
try {
|
|
const result = await invoke<MaterialImportResult>('import_materials', { request });
|
|
|
|
// 更新素材列表
|
|
const { materials } = get();
|
|
set({
|
|
materials: [...materials, ...result.created_materials],
|
|
isImporting: false,
|
|
importProgress: null
|
|
});
|
|
|
|
// 重新加载统计信息
|
|
get().loadMaterialStats(request.project_id);
|
|
|
|
return result;
|
|
} catch (error) {
|
|
set({
|
|
error: error as string,
|
|
isImporting: false,
|
|
importProgress: null
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// 获取素材详情
|
|
getMaterialById: async (id: string) => {
|
|
try {
|
|
const material = await invoke<Material | null>('get_material_by_id', { id });
|
|
return material;
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
return null;
|
|
}
|
|
},
|
|
|
|
// 删除素材
|
|
deleteMaterial: async (id: string) => {
|
|
try {
|
|
await invoke('delete_material', { id });
|
|
|
|
// 从状态中移除
|
|
const { materials } = get();
|
|
set({ materials: materials.filter(m => m.id !== id) });
|
|
|
|
// 如果删除的是当前素材,清空当前素材
|
|
const { currentMaterial } = get();
|
|
if (currentMaterial?.id === id) {
|
|
set({ currentMaterial: null });
|
|
}
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// 批量处理素材
|
|
processMaterials: async (materialIds: string[]) => {
|
|
set({ isProcessing: true, error: null });
|
|
try {
|
|
const processedIds = await invoke<string[]>('batch_process_materials', { materialIds });
|
|
|
|
// 更新处理状态
|
|
const { materials } = get();
|
|
const updatedMaterials = materials.map(material => {
|
|
if (processedIds.includes(material.id)) {
|
|
return { ...material, processing_status: 'Processing' as ProcessingStatus };
|
|
}
|
|
return material;
|
|
});
|
|
|
|
set({ materials: updatedMaterials, isProcessing: false });
|
|
} catch (error) {
|
|
set({ error: error as string, isProcessing: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// 更新素材状态
|
|
updateMaterialStatus: async (id: string, status: ProcessingStatus, errorMessage?: string) => {
|
|
try {
|
|
await invoke('update_material_status', { id, status, errorMessage });
|
|
|
|
// 更新本地状态
|
|
const { materials } = get();
|
|
const updatedMaterials = materials.map(material => {
|
|
if (material.id === id) {
|
|
return {
|
|
...material,
|
|
processing_status: status,
|
|
error_message: errorMessage,
|
|
updated_at: new Date().toISOString()
|
|
};
|
|
}
|
|
return material;
|
|
});
|
|
|
|
set({ materials: updatedMaterials });
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// 清理失效素材
|
|
cleanupInvalidMaterials: async (projectId: string) => {
|
|
try {
|
|
const result = await invoke<string>('cleanup_invalid_materials', { projectId });
|
|
|
|
// 重新加载素材列表
|
|
await get().loadMaterials(projectId);
|
|
|
|
return result;
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// 设置当前素材
|
|
setCurrentMaterial: (material: Material | null) => {
|
|
set({ currentMaterial: material });
|
|
},
|
|
|
|
// 清除错误
|
|
clearError: () => {
|
|
set({ error: null });
|
|
},
|
|
|
|
// 选择素材文件
|
|
selectMaterialFiles: async () => {
|
|
try {
|
|
const filePaths = await invoke<string[]>('select_material_files');
|
|
return filePaths;
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
return [];
|
|
}
|
|
},
|
|
|
|
// 验证素材文件
|
|
validateMaterialFiles: async (filePaths: string[]) => {
|
|
try {
|
|
const validFiles = await invoke<string[]>('validate_material_files', { filePaths });
|
|
return validFiles;
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
return [];
|
|
}
|
|
},
|
|
|
|
// 获取文件信息
|
|
getFileInfo: async (filePath: string) => {
|
|
try {
|
|
const fileInfo = await invoke('get_file_info', { filePath });
|
|
return fileInfo;
|
|
} catch (error) {
|
|
set({ error: error as string });
|
|
return null;
|
|
}
|
|
},
|
|
|
|
// 检查 FFmpeg 是否可用
|
|
checkFFmpegAvailable: async () => {
|
|
try {
|
|
const isAvailable = await invoke<boolean>('check_ffmpeg_available');
|
|
return isAvailable;
|
|
} catch (error) {
|
|
console.error('检查 FFmpeg 失败:', error);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// 获取 FFmpeg 版本
|
|
getFFmpegVersion: async () => {
|
|
try {
|
|
const version = await invoke<string>('get_ffmpeg_version');
|
|
return version;
|
|
} catch (error) {
|
|
console.error('获取 FFmpeg 版本失败:', error);
|
|
return 'Unknown';
|
|
}
|
|
},
|
|
|
|
// 获取支持的扩展名
|
|
getSupportedExtensions: async () => {
|
|
try {
|
|
const extensions = await invoke<string[]>('get_supported_extensions');
|
|
return extensions;
|
|
} catch (error) {
|
|
console.error('获取支持的扩展名失败:', error);
|
|
return [];
|
|
}
|
|
},
|
|
|
|
// 测试场景检测
|
|
testSceneDetection: async (filePath: string) => {
|
|
try {
|
|
const result = await invoke<string>('test_scene_detection', { filePath });
|
|
return result;
|
|
} catch (error) {
|
|
console.error('测试场景检测失败:', error);
|
|
return `测试失败: ${error}`;
|
|
}
|
|
},
|
|
|
|
// 获取FFmpeg状态
|
|
getFFmpegStatus: async () => {
|
|
try {
|
|
const status = await invoke<string>('get_ffmpeg_status');
|
|
return status;
|
|
} catch (error) {
|
|
console.error('获取FFmpeg状态失败:', error);
|
|
return `获取状态失败: ${error}`;
|
|
}
|
|
},
|
|
}));
|