|
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; |
|
} |
|
} |