| import { db } from './index'; |
| import type { TrainerScanProgress } from './schema'; |
|
|
| |
| export async function initializeTrainerScanProgress(imagePaths: string[]): Promise<void> { |
| let processedCount = 0; |
| let skippedCount = 0; |
| |
| for (let i = 0; i < imagePaths.length; i++) { |
| const imagePath = imagePaths[i]; |
| try { |
| if (typeof imagePath !== 'string') { |
| console.error(`❌ Path at index ${i} is not a string:`, imagePath, typeof imagePath); |
| skippedCount++; |
| continue; |
| } |
| |
| |
| |
| const pathParts = imagePath.split('/'); |
| |
| if (pathParts.length < 3) { |
| console.warn(`⚠️ Skipping invalid path format: ${imagePath}`); |
| skippedCount++; |
| continue; |
| } |
| |
| const trainerName = pathParts[1]?.trim(); |
| const imageFile = pathParts[2]?.trim(); |
| |
| if (!trainerName || !imageFile) { |
| console.warn(`⚠️ Skipping path with missing parts: ${imagePath}`); |
| 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/${trainerName}/${imageFile}`; |
| |
| |
| const existing = await db.trainerScanProgress.get(imagePath); |
| |
| if (!existing) { |
| const progressRecord: Omit<TrainerScanProgress, 'id'> = { |
| imagePath, |
| trainerName, |
| imageIndex, |
| status: 'pending', |
| remoteUrl |
| }; |
| |
| await db.trainerScanProgress.add(progressRecord); |
| processedCount++; |
| } else { |
| processedCount++; |
| } |
| |
| |
| if (i % 500 === 0 && i > 0) { |
| console.log(`Progress: ${i}/${imagePaths.length} (${Math.round((i/imagePaths.length)*100)}%)`); |
| } |
| |
| } catch (error) { |
| console.error(`❌ ERROR processing path ${i}: "${imagePath}"`, error); |
| if (error instanceof Error && error.message.includes('replace')) { |
| console.error('❌ REPLACE ERROR FOUND at path:', imagePath); |
| console.error('❌ Path parts:', imagePath.split('/')); |
| console.error('❌ Full error:', error); |
| } |
| skippedCount++; |
| } |
| } |
| |
| console.log(`✅ Initialization complete: ${processedCount} processed, ${skippedCount} skipped`); |
| } |
|
|
| |
| export async function getNextPendingImage(): Promise<TrainerScanProgress | null> { |
| try { |
| const pendingRecord = await db.trainerScanProgress.where('status').equals('pending').first(); |
| return pendingRecord || null; |
| } catch (error) { |
| console.error('❌ Failed to get next pending image:', error); |
| return null; |
| } |
| } |
|
|
| |
| export async function updateScanProgress( |
| imagePath: string, |
| updates: Partial<Omit<TrainerScanProgress, 'id' | 'imagePath'>> |
| ): Promise<void> { |
| try { |
| await db.trainerScanProgress.update(imagePath, updates); |
| } catch (error) { |
| console.error(`❌ Failed to update scan progress for ${imagePath}:`, error); |
| throw error; |
| } |
| } |
|
|
| |
| export async function markImageProcessingStarted(imagePath: string): Promise<void> { |
| await updateScanProgress(imagePath, { |
| status: 'processing', |
| startedAt: new Date() |
| }); |
| } |
|
|
| |
| export async function markImageProcessingCompleted( |
| imagePath: string, |
| picletInstanceId: number |
| ): Promise<void> { |
| await updateScanProgress(imagePath, { |
| status: 'completed', |
| picletInstanceId, |
| completedAt: new Date() |
| }); |
| } |
|
|
| |
| export async function markImageProcessingFailed( |
| imagePath: string, |
| errorMessage: string |
| ): Promise<void> { |
| await updateScanProgress(imagePath, { |
| status: 'failed', |
| errorMessage, |
| completedAt: new Date() |
| }); |
| } |
|
|
| |
| export async function getScanningStats(): Promise<{ |
| total: number; |
| pending: number; |
| processing: number; |
| completed: number; |
| failed: number; |
| }> { |
| try { |
| const [total, pending, processing, completed, failed] = await Promise.all([ |
| db.trainerScanProgress.count(), |
| db.trainerScanProgress.where('status').equals('pending').count(), |
| db.trainerScanProgress.where('status').equals('processing').count(), |
| db.trainerScanProgress.where('status').equals('completed').count(), |
| db.trainerScanProgress.where('status').equals('failed').count(), |
| ]); |
| |
| return { |
| total, |
| pending, |
| processing, |
| completed, |
| failed |
| }; |
| } catch (error) { |
| console.error('❌ Failed to get scanning stats:', error); |
| return { |
| total: 0, |
| pending: 0, |
| processing: 0, |
| completed: 0, |
| failed: 0 |
| }; |
| } |
| } |
|
|
| |
| export async function getCompletedScansForTrainer(trainerName: string): Promise<TrainerScanProgress[]> { |
| try { |
| return await db.trainerScanProgress |
| .where('trainerName').equals(trainerName) |
| .and(record => record.status === 'completed') |
| .toArray(); |
| } catch (error) { |
| console.error(`❌ Failed to get completed scans for trainer ${trainerName}:`, error); |
| return []; |
| } |
| } |
|
|
| |
| export async function resetFailedScans(): Promise<number> { |
| try { |
| const failedRecords = await db.trainerScanProgress.where('status').equals('failed').toArray(); |
| |
| for (const record of failedRecords) { |
| await db.trainerScanProgress.update(record.imagePath, { |
| status: 'pending', |
| errorMessage: undefined, |
| startedAt: undefined, |
| completedAt: undefined |
| }); |
| } |
| |
| return failedRecords.length; |
| } catch (error) { |
| console.error('❌ Failed to reset failed scans:', error); |
| return 0; |
| } |
| } |
|
|
| |
| export async function getCurrentProcessingImage(): Promise<TrainerScanProgress | null> { |
| try { |
| const processingRecord = await db.trainerScanProgress.where('status').equals('processing').first(); |
| return processingRecord || null; |
| } catch (error) { |
| console.error('❌ Failed to get current processing image:', error); |
| return null; |
| } |
| } |