File size: 10,100 Bytes
5435413
 
 
 
 
c0fc1ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2aa7dab
c0fc1ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2aa7dab
5435413
c0fc1ad
 
 
 
 
 
 
 
 
 
 
 
 
 
5435413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
import { db } from './index';
import type { TrainerScanProgress } from './schema';

// Initialize trainer scan progress records from paths
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;
        }
        
        // Extract trainer name and image index from path
        // Format: "trainer_images/001_Willow_Snap/image_001.jpg"
        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?.(); // Safe call with optional chaining
        const imageFile = rawImageFile?.trim?.(); // Safe call with optional chaining
        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}`);
        
        // Check if this path already exists to avoid duplicates
        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 {
          // Record exists, don't count as processed but note it
          processedCount++;
        }
        
        // Log progress every 100 items
        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++;
        // Continue processing other paths despite this failure
      }
    }
    
    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;
  }
}

// Get next pending image to process
export async function getNextPendingImage(): Promise<TrainerScanProgress | null> {
  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<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;
}

// Mark image processing as started
export async function markImageProcessingStarted(imagePath: string): Promise<void> {
  await updateScanProgress(imagePath, {
    status: 'processing',
    startedAt: new Date()
  });
}

// Mark image processing as completed successfully
export async function markImageProcessingCompleted(
  imagePath: string, 
  picletInstanceId: number
): Promise<void> {
  await updateScanProgress(imagePath, {
    status: 'completed',
    picletInstanceId,
    completedAt: new Date()
  });
}

// Mark image processing as failed
export async function markImageProcessingFailed(
  imagePath: string, 
  errorMessage: string
): Promise<void> {
  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<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;
}

// Reset all failed scans back to pending (for retry)
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;
}

// Get current processing status (for resuming interrupted sessions)
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;
}