|
import { db } from './index'; |
|
import type { TrainerScanProgress } from './schema'; |
|
|
|
|
|
export async function initializeTrainerScanProgress(imagePaths: string[]): Promise<void> { |
|
try { |
|
console.log('π initializeTrainerScanProgress: Starting with', imagePaths.length, 'paths'); |
|
console.log('π initializeTrainerScanProgress: First few paths:', imagePaths.slice(0, 3)); |
|
|
|
console.log('π initializeTrainerScanProgress: Creating transaction...'); |
|
const tx = db.transaction([TRAINER_SCAN_STORE], 'readwrite'); |
|
console.log('π initializeTrainerScanProgress: Transaction created'); |
|
|
|
const store = tx.objectStore(TRAINER_SCAN_STORE); |
|
console.log('π initializeTrainerScanProgress: Got object store'); |
|
|
|
let processedCount = 0; |
|
let skippedCount = 0; |
|
|
|
for (let i = 0; i < imagePaths.length; i++) { |
|
const imagePath = imagePaths[i]; |
|
try { |
|
console.log(`π initializeTrainerScanProgress: Processing path ${i}: "${imagePath}" (type: ${typeof imagePath})`); |
|
|
|
if (typeof imagePath !== 'string') { |
|
console.error(`β initializeTrainerScanProgress: Path at index ${i} is not a string:`, imagePath); |
|
skippedCount++; |
|
continue; |
|
} |
|
|
|
|
|
|
|
console.log(`π initializeTrainerScanProgress: Splitting path: "${imagePath}"`); |
|
const pathParts = imagePath.split('/'); |
|
console.log(`π initializeTrainerScanProgress: Path parts:`, pathParts); |
|
|
|
if (pathParts.length < 3) { |
|
console.warn(`β οΈ Skipping invalid path format: ${imagePath} (length: ${pathParts.length})`); |
|
skippedCount++; |
|
continue; |
|
} |
|
|
|
const rawTrainerName = pathParts[1]; |
|
const rawImageFile = pathParts[2]; |
|
console.log(`π initializeTrainerScanProgress: Raw parts - trainer: "${rawTrainerName}" (${typeof rawTrainerName}), image: "${rawImageFile}" (${typeof rawImageFile})`); |
|
|
|
const trainerName = rawTrainerName?.trim?.(); |
|
const imageFile = rawImageFile?.trim?.(); |
|
console.log(`π initializeTrainerScanProgress: Trimmed parts - trainer: "${trainerName}" (${typeof trainerName}), image: "${imageFile}" (${typeof imageFile})`); |
|
|
|
if (!trainerName || !imageFile || typeof trainerName !== 'string' || typeof imageFile !== 'string') { |
|
console.warn(`β οΈ Skipping path with missing or invalid parts: ${imagePath}`, { |
|
trainerName, |
|
imageFile, |
|
trainerType: typeof trainerName, |
|
imageType: typeof imageFile |
|
}); |
|
skippedCount++; |
|
continue; |
|
} |
|
|
|
console.log(`π initializeTrainerScanProgress: Matching image file: "${imageFile}"`); |
|
const imageMatch = imageFile.match(/image_(\d+)\.jpg/); |
|
const imageIndex = imageMatch ? parseInt(imageMatch[1]) : 1; |
|
console.log(`π initializeTrainerScanProgress: Image index: ${imageIndex}`); |
|
|
|
const remoteUrl = `https://huggingface.co/datasets/Fraser/piclets/resolve/main/${imagePath}`; |
|
console.log(`π initializeTrainerScanProgress: Remote URL: ${remoteUrl}`); |
|
|
|
|
|
console.log(`π initializeTrainerScanProgress: Checking for existing record...`); |
|
const existing = await store.get(imagePath); |
|
console.log(`π initializeTrainerScanProgress: Existing record:`, existing ? 'found' : 'not found'); |
|
|
|
if (!existing) { |
|
const progressRecord: Omit<TrainerScanProgress, 'id'> = { |
|
imagePath, |
|
trainerName, |
|
imageIndex, |
|
status: 'pending', |
|
remoteUrl |
|
}; |
|
|
|
console.log(`π initializeTrainerScanProgress: Adding record:`, progressRecord); |
|
await store.add(progressRecord); |
|
console.log(`π initializeTrainerScanProgress: Record added successfully`); |
|
processedCount++; |
|
} else { |
|
|
|
processedCount++; |
|
} |
|
|
|
|
|
if (i % 100 === 0) { |
|
console.log(`π initializeTrainerScanProgress: Progress: ${i}/${imagePaths.length} (${Math.round((i/imagePaths.length)*100)}%)`); |
|
} |
|
|
|
} catch (error) { |
|
console.error(`β Failed to process path entry at index ${i}: "${imagePath}"`, error); |
|
console.error('β Error details:', { |
|
message: error instanceof Error ? error.message : 'Unknown error', |
|
stack: error instanceof Error ? error.stack : 'No stack trace', |
|
pathValue: imagePath, |
|
pathType: typeof imagePath |
|
}); |
|
skippedCount++; |
|
|
|
} |
|
} |
|
|
|
console.log(`β
Trainer scan initialization complete: ${processedCount} records processed, ${skippedCount} skipped`); |
|
|
|
console.log('π initializeTrainerScanProgress: Completing transaction...'); |
|
await tx.complete; |
|
console.log('π initializeTrainerScanProgress: Transaction completed successfully'); |
|
|
|
} catch (error) { |
|
console.error('β initializeTrainerScanProgress: Fatal error during initialization:', error); |
|
console.error('β initializeTrainerScanProgress: Error details:', { |
|
message: error instanceof Error ? error.message : 'Unknown error', |
|
stack: error instanceof Error ? error.stack : 'No stack trace' |
|
}); |
|
throw error; |
|
} |
|
} |
|
|
|
|
|
export async function getNextPendingImage(): Promise<TrainerScanProgress | null> { |
|
const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly'); |
|
const store = tx.objectStore(TRAINER_SCAN_STORE); |
|
|
|
|
|
const pendingRecords: TrainerScanProgress[] = []; |
|
let cursor = await store.openCursor(); |
|
|
|
while (cursor) { |
|
if (cursor.value.status === 'pending') { |
|
pendingRecords.push(cursor.value); |
|
} |
|
cursor = await cursor.continue(); |
|
} |
|
|
|
|
|
return pendingRecords.length > 0 ? pendingRecords[0] : null; |
|
} |
|
|
|
|
|
export async function updateScanProgress( |
|
imagePath: string, |
|
updates: Partial<Omit<TrainerScanProgress, 'id' | 'imagePath'>> |
|
): Promise<void> { |
|
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; |
|
} |
|
|
|
|
|
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; |
|
}> { |
|
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; |
|
} |
|
|
|
|
|
export async function getCompletedScansForTrainer(trainerName: string): Promise<TrainerScanProgress[]> { |
|
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; |
|
} |
|
|
|
|
|
export async function resetFailedScans(): Promise<number> { |
|
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; |
|
} |
|
|
|
|
|
export async function getCurrentProcessingImage(): Promise<TrainerScanProgress | null> { |
|
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; |
|
} |