import { useState, useCallback } from 'react'; import { FileItem, UploadStatus } from '../types'; import { uploadBatchToHub } from '../services/hfService'; export const useFileUpload = () => { const [files, setFiles] = useState([]); const [isUploading, setIsUploading] = useState(false); // --- CONFIGURATION --- const BATCH_SIZE = 5; // Files per request const CONCURRENCY_LIMIT = 3; // Parallel requests // --- UPLOAD LOGIC --- const addFiles = useCallback((newFilesList: FileItem[]) => { setFiles((prev) => [...prev, ...newFilesList]); }, []); const removeFile = useCallback((id: string) => { setFiles((prev) => prev.filter((f) => f.id !== id)); }, []); const updateFilePath = useCallback((id: string, newPath: string) => { setFiles((prev) => prev.map((f) => (f.id === id ? { ...f, path: newPath } : f))); }, []); const startUpload = useCallback(async () => { const filesToUpload = files.filter( (f) => f.status === UploadStatus.IDLE || f.status === UploadStatus.ERROR ); if (filesToUpload.length === 0) return; setIsUploading(true); // 1. Create Batches (Chunks) const batches = []; for (let i = 0; i < filesToUpload.length; i += BATCH_SIZE) { batches.push(filesToUpload.slice(i, i + BATCH_SIZE)); } // 2. Process Batch Function const processBatch = async (batch: FileItem[]) => { // Set status UPLOADING for this batch setFiles((prev) => prev.map((f) => batch.find((b) => b.id === f.id) ? { ...f, status: UploadStatus.UPLOADING, error: undefined } : f ) ); try { // Prepare payload for service const batchPayload = batch.map(item => ({ id: item.id, file: item.file, path: item.path })); // Call API const urls = await uploadBatchToHub(batchPayload); // Success: Update status and add URLs setFiles((prev) => prev.map((f) => { const index = batch.findIndex(b => b.id === f.id); if (index !== -1) { return { ...f, status: UploadStatus.SUCCESS, url: urls[index] }; } return f; }) ); } catch (err: any) { // Error: Update status for whole batch setFiles((prev) => prev.map((f) => batch.find((b) => b.id === f.id) ? { ...f, status: UploadStatus.ERROR, error: err.message } : f ) ); } }; // 3. Execute with Concurrency Limit // We process batches in groups of CONCURRENCY_LIMIT for (let i = 0; i < batches.length; i += CONCURRENCY_LIMIT) { const activeBatches = batches.slice(i, i + CONCURRENCY_LIMIT); await Promise.allSettled(activeBatches.map(batch => processBatch(batch))); } setIsUploading(false); }, [files]); return { files, isUploading, addFiles, removeFile, updateFilePath, startUpload }; };