import { db } from './index'; import type { TrainerScanProgress } from './schema'; const TRAINER_SCAN_STORE = 'trainerScanProgress'; // Initialize trainer scan progress records from paths export async function initializeTrainerScanProgress(imagePaths: string[]): Promise { const tx = db.transaction([TRAINER_SCAN_STORE], 'readwrite'); const store = tx.objectStore(TRAINER_SCAN_STORE); let processedCount = 0; let skippedCount = 0; for (const imagePath of imagePaths) { try { // Extract trainer name and image index from path // Format: "trainer_images/001_Willow_Snap/image_001.jpg" const pathParts = imagePath.split('/'); if (pathParts.length < 3) { console.warn(`⚠️ Skipping invalid path format: ${imagePath}`); skippedCount++; continue; } const trainerName = pathParts[1]?.trim(); // "001_Willow_Snap" const imageFile = pathParts[2]?.trim(); // "image_001.jpg" if (!trainerName || !imageFile || typeof trainerName !== 'string' || typeof imageFile !== 'string') { console.warn(`⚠️ Skipping path with missing or invalid parts: ${imagePath}`, { trainerName, imageFile }); skippedCount++; continue; } const imageMatch = imageFile.match(/image_(\d+)\.jpg/); const imageIndex = imageMatch ? parseInt(imageMatch[1]) : 1; const remoteUrl = `https://huggingface.co/datasets/Fraser/piclets/resolve/main/${imagePath}`; // Check if this path already exists to avoid duplicates const existing = await store.get(imagePath); if (!existing) { const progressRecord: Omit = { imagePath, trainerName, imageIndex, status: 'pending', remoteUrl }; await store.add(progressRecord); processedCount++; } else { // Record exists, don't count as processed but note it processedCount++; } } catch (error) { console.error(`❌ Failed to process path entry: ${imagePath}`, error); skippedCount++; // Continue processing other paths despite this failure } } console.log(`✅ Trainer scan initialization complete: ${processedCount} records processed, ${skippedCount} skipped`); await tx.complete; } // Get next pending image to process export async function getNextPendingImage(): Promise { const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly'); const store = tx.objectStore(TRAINER_SCAN_STORE); // Get all pending records const pendingRecords: TrainerScanProgress[] = []; let cursor = await store.openCursor(); while (cursor) { if (cursor.value.status === 'pending') { pendingRecords.push(cursor.value); } cursor = await cursor.continue(); } // Return the first pending record (if any) return pendingRecords.length > 0 ? pendingRecords[0] : null; } // Update scan progress status export async function updateScanProgress( imagePath: string, updates: Partial> ): Promise { const tx = db.transaction([TRAINER_SCAN_STORE], 'readwrite'); const store = tx.objectStore(TRAINER_SCAN_STORE); const existing = await store.get(imagePath); if (existing) { const updated = { ...existing, ...updates }; await store.put(updated); } await tx.complete; } // Mark image processing as started export async function markImageProcessingStarted(imagePath: string): Promise { await updateScanProgress(imagePath, { status: 'processing', startedAt: new Date() }); } // Mark image processing as completed successfully export async function markImageProcessingCompleted( imagePath: string, picletInstanceId: number ): Promise { await updateScanProgress(imagePath, { status: 'completed', picletInstanceId, completedAt: new Date() }); } // Mark image processing as failed export async function markImageProcessingFailed( imagePath: string, errorMessage: string ): Promise { await updateScanProgress(imagePath, { status: 'failed', errorMessage, completedAt: new Date() }); } // Get scanning statistics export async function getScanningStats(): Promise<{ total: number; pending: number; processing: number; completed: number; failed: number; }> { const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly'); const store = tx.objectStore(TRAINER_SCAN_STORE); const stats = { total: 0, pending: 0, processing: 0, completed: 0, failed: 0 }; let cursor = await store.openCursor(); while (cursor) { stats.total++; const status = cursor.value.status; stats[status]++; cursor = await cursor.continue(); } return stats; } // Get all completed scans for a specific trainer export async function getCompletedScansForTrainer(trainerName: string): Promise { const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly'); const store = tx.objectStore(TRAINER_SCAN_STORE); const results: TrainerScanProgress[] = []; let cursor = await store.openCursor(); while (cursor) { const record = cursor.value; if (record.trainerName === trainerName && record.status === 'completed') { results.push(record); } cursor = await cursor.continue(); } return results; } // Reset all failed scans back to pending (for retry) export async function resetFailedScans(): Promise { const tx = db.transaction([TRAINER_SCAN_STORE], 'readwrite'); const store = tx.objectStore(TRAINER_SCAN_STORE); let resetCount = 0; let cursor = await store.openCursor(); while (cursor) { if (cursor.value.status === 'failed') { const updated = { ...cursor.value, status: 'pending' as const, errorMessage: undefined, startedAt: undefined, completedAt: undefined }; await cursor.update(updated); resetCount++; } cursor = await cursor.continue(); } await tx.complete; return resetCount; } // Get current processing status (for resuming interrupted sessions) export async function getCurrentProcessingImage(): Promise { const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly'); const store = tx.objectStore(TRAINER_SCAN_STORE); let cursor = await store.openCursor(); while (cursor) { if (cursor.value.status === 'processing') { return cursor.value; } cursor = await cursor.continue(); } return null; }