mixvideo-v2/apps/desktop/src/store/materialStore.ts

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}`;
}
},
}));