Fraser commited on
Commit
414e700
Β·
1 Parent(s): 09cc536
src/lib/db/trainerScanning.ts CHANGED
@@ -3,114 +3,76 @@ import type { TrainerScanProgress } from './schema';
3
 
4
  // Initialize trainer scan progress records from paths
5
  export async function initializeTrainerScanProgress(imagePaths: string[]): Promise<void> {
6
- try {
7
- console.log('πŸ” initializeTrainerScanProgress: Starting with', imagePaths.length, 'paths');
8
- console.log('πŸ” initializeTrainerScanProgress: First few paths:', imagePaths.slice(0, 3));
9
-
10
- let processedCount = 0;
11
- let skippedCount = 0;
12
-
13
- for (let i = 0; i < imagePaths.length; i++) {
14
- const imagePath = imagePaths[i];
15
- try {
16
- console.log(`πŸ” initializeTrainerScanProgress: Processing path ${i}: "${imagePath}" (type: ${typeof imagePath})`);
17
-
18
- if (typeof imagePath !== 'string') {
19
- console.error(`❌ initializeTrainerScanProgress: Path at index ${i} is not a string:`, imagePath);
20
- skippedCount++;
21
- continue;
22
- }
23
-
24
- // Extract trainer name and image index from path
25
- // Format: "trainer_images/001_Willow_Snap/image_001.jpg"
26
- console.log(`πŸ” initializeTrainerScanProgress: Splitting path: "${imagePath}"`);
27
- const pathParts = imagePath.split('/');
28
- console.log(`πŸ” initializeTrainerScanProgress: Path parts:`, pathParts);
29
-
30
- if (pathParts.length < 3) {
31
- console.warn(`⚠️ Skipping invalid path format: ${imagePath} (length: ${pathParts.length})`);
32
- skippedCount++;
33
- continue;
34
- }
35
-
36
- const rawTrainerName = pathParts[1];
37
- const rawImageFile = pathParts[2];
38
- console.log(`πŸ” initializeTrainerScanProgress: Raw parts - trainer: "${rawTrainerName}" (${typeof rawTrainerName}), image: "${rawImageFile}" (${typeof rawImageFile})`);
39
-
40
- const trainerName = rawTrainerName?.trim?.(); // Safe call with optional chaining
41
- const imageFile = rawImageFile?.trim?.(); // Safe call with optional chaining
42
- console.log(`πŸ” initializeTrainerScanProgress: Trimmed parts - trainer: "${trainerName}" (${typeof trainerName}), image: "${imageFile}" (${typeof imageFile})`);
43
-
44
- if (!trainerName || !imageFile || typeof trainerName !== 'string' || typeof imageFile !== 'string') {
45
- console.warn(`⚠️ Skipping path with missing or invalid parts: ${imagePath}`, {
46
- trainerName,
47
- imageFile,
48
- trainerType: typeof trainerName,
49
- imageType: typeof imageFile
50
- });
51
- skippedCount++;
52
- continue;
53
- }
54
-
55
- console.log(`πŸ” initializeTrainerScanProgress: Matching image file: "${imageFile}"`);
56
- const imageMatch = imageFile.match(/image_(\d+)\.jpg/);
57
- const imageIndex = imageMatch ? parseInt(imageMatch[1]) : 1;
58
- console.log(`πŸ” initializeTrainerScanProgress: Image index: ${imageIndex}`);
59
-
60
- const remoteUrl = `https://huggingface.co/datasets/Fraser/piclets/resolve/main/${imagePath}`;
61
- console.log(`πŸ” initializeTrainerScanProgress: Remote URL: ${remoteUrl}`);
62
-
63
- // Check if this path already exists to avoid duplicates (using Dexie syntax)
64
- console.log(`πŸ” initializeTrainerScanProgress: Checking for existing record...`);
65
- const existing = await db.trainerScanProgress.get(imagePath);
66
- console.log(`πŸ” initializeTrainerScanProgress: Existing record:`, existing ? 'found' : 'not found');
67
-
68
- if (!existing) {
69
- const progressRecord: Omit<TrainerScanProgress, 'id'> = {
70
- imagePath,
71
- trainerName,
72
- imageIndex,
73
- status: 'pending',
74
- remoteUrl
75
- };
76
-
77
- console.log(`πŸ” initializeTrainerScanProgress: Adding record:`, progressRecord);
78
- await db.trainerScanProgress.add(progressRecord);
79
- console.log(`πŸ” initializeTrainerScanProgress: Record added successfully`);
80
- processedCount++;
81
- } else {
82
- // Record exists, don't count as processed but note it
83
- processedCount++;
84
- }
85
-
86
- // Log progress every 100 items
87
- if (i % 100 === 0) {
88
- console.log(`πŸ” initializeTrainerScanProgress: Progress: ${i}/${imagePaths.length} (${Math.round((i/imagePaths.length)*100)}%)`);
89
- }
90
-
91
- } catch (error) {
92
- console.error(`❌ Failed to process path entry at index ${i}: "${imagePath}"`, error);
93
- console.error('❌ Error details:', {
94
- message: error instanceof Error ? error.message : 'Unknown error',
95
- stack: error instanceof Error ? error.stack : 'No stack trace',
96
- pathValue: imagePath,
97
- pathType: typeof imagePath
98
- });
99
  skippedCount++;
100
- // Continue processing other paths despite this failure
101
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
103
-
104
- console.log(`βœ… Trainer scan initialization complete: ${processedCount} records processed, ${skippedCount} skipped`);
105
-
106
- } catch (error) {
107
- console.error('❌ initializeTrainerScanProgress: Fatal error during initialization:', error);
108
- console.error('❌ initializeTrainerScanProgress: Error details:', {
109
- message: error instanceof Error ? error.message : 'Unknown error',
110
- stack: error instanceof Error ? error.stack : 'No stack trace'
111
- });
112
- throw error;
113
  }
 
 
114
  }
115
 
116
  // Get next pending image to process
@@ -177,86 +139,75 @@ export async function getScanningStats(): Promise<{
177
  completed: number;
178
  failed: number;
179
  }> {
180
- const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly');
181
- const store = tx.objectStore(TRAINER_SCAN_STORE);
182
-
183
- const stats = {
184
- total: 0,
185
- pending: 0,
186
- processing: 0,
187
- completed: 0,
188
- failed: 0
189
- };
190
-
191
- let cursor = await store.openCursor();
192
- while (cursor) {
193
- stats.total++;
194
- const status = cursor.value.status;
195
- stats[status]++;
196
- cursor = await cursor.continue();
 
 
 
 
 
 
 
 
197
  }
198
-
199
- return stats;
200
  }
201
 
202
  // Get all completed scans for a specific trainer
203
  export async function getCompletedScansForTrainer(trainerName: string): Promise<TrainerScanProgress[]> {
204
- const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly');
205
- const store = tx.objectStore(TRAINER_SCAN_STORE);
206
-
207
- const results: TrainerScanProgress[] = [];
208
- let cursor = await store.openCursor();
209
-
210
- while (cursor) {
211
- const record = cursor.value;
212
- if (record.trainerName === trainerName && record.status === 'completed') {
213
- results.push(record);
214
- }
215
- cursor = await cursor.continue();
216
  }
217
-
218
- return results;
219
  }
220
 
221
  // Reset all failed scans back to pending (for retry)
222
  export async function resetFailedScans(): Promise<number> {
223
- const tx = db.transaction([TRAINER_SCAN_STORE], 'readwrite');
224
- const store = tx.objectStore(TRAINER_SCAN_STORE);
225
-
226
- let resetCount = 0;
227
- let cursor = await store.openCursor();
228
-
229
- while (cursor) {
230
- if (cursor.value.status === 'failed') {
231
- const updated = {
232
- ...cursor.value,
233
- status: 'pending' as const,
234
  errorMessage: undefined,
235
  startedAt: undefined,
236
  completedAt: undefined
237
- };
238
- await cursor.update(updated);
239
- resetCount++;
240
  }
241
- cursor = await cursor.continue();
 
 
 
 
242
  }
243
-
244
- await tx.complete;
245
- return resetCount;
246
  }
247
 
248
  // Get current processing status (for resuming interrupted sessions)
249
  export async function getCurrentProcessingImage(): Promise<TrainerScanProgress | null> {
250
- const tx = db.transaction([TRAINER_SCAN_STORE], 'readonly');
251
- const store = tx.objectStore(TRAINER_SCAN_STORE);
252
-
253
- let cursor = await store.openCursor();
254
- while (cursor) {
255
- if (cursor.value.status === 'processing') {
256
- return cursor.value;
257
- }
258
- cursor = await cursor.continue();
259
  }
260
-
261
- return null;
262
  }
 
3
 
4
  // Initialize trainer scan progress records from paths
5
  export async function initializeTrainerScanProgress(imagePaths: string[]): Promise<void> {
6
+ let processedCount = 0;
7
+ let skippedCount = 0;
8
+
9
+ for (let i = 0; i < imagePaths.length; i++) {
10
+ const imagePath = imagePaths[i];
11
+ try {
12
+ if (typeof imagePath !== 'string') {
13
+ console.error(`❌ Path at index ${i} is not a string:`, imagePath, typeof imagePath);
14
+ skippedCount++;
15
+ continue;
16
+ }
17
+
18
+ // Extract trainer name and image index from path
19
+ // Format: "trainer_images/001_Willow_Snap/image_001.jpg"
20
+ const pathParts = imagePath.split('/');
21
+
22
+ if (pathParts.length < 3) {
23
+ console.warn(`⚠️ Skipping invalid path format: ${imagePath}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  skippedCount++;
25
+ continue;
26
  }
27
+
28
+ const trainerName = pathParts[1]?.trim();
29
+ const imageFile = pathParts[2]?.trim();
30
+
31
+ if (!trainerName || !imageFile) {
32
+ console.warn(`⚠️ Skipping path with missing parts: ${imagePath}`);
33
+ skippedCount++;
34
+ continue;
35
+ }
36
+
37
+ const imageMatch = imageFile.match(/image_(\d+)\.jpg/);
38
+ const imageIndex = imageMatch ? parseInt(imageMatch[1]) : 1;
39
+ const remoteUrl = `https://huggingface.co/datasets/Fraser/piclets/resolve/main/${imagePath}`;
40
+
41
+ // Check if this path already exists to avoid duplicates
42
+ const existing = await db.trainerScanProgress.get(imagePath);
43
+
44
+ if (!existing) {
45
+ const progressRecord: Omit<TrainerScanProgress, 'id'> = {
46
+ imagePath,
47
+ trainerName,
48
+ imageIndex,
49
+ status: 'pending',
50
+ remoteUrl
51
+ };
52
+
53
+ await db.trainerScanProgress.add(progressRecord);
54
+ processedCount++;
55
+ } else {
56
+ processedCount++;
57
+ }
58
+
59
+ // Log progress every 500 items instead of every 100
60
+ if (i % 500 === 0 && i > 0) {
61
+ console.log(`Progress: ${i}/${imagePaths.length} (${Math.round((i/imagePaths.length)*100)}%)`);
62
+ }
63
+
64
+ } catch (error) {
65
+ console.error(`❌ ERROR processing path ${i}: "${imagePath}"`, error);
66
+ if (error instanceof Error && error.message.includes('replace')) {
67
+ console.error('❌ REPLACE ERROR FOUND at path:', imagePath);
68
+ console.error('❌ Path parts:', imagePath.split('/'));
69
+ console.error('❌ Full error:', error);
70
+ }
71
+ skippedCount++;
72
  }
 
 
 
 
 
 
 
 
 
 
73
  }
74
+
75
+ console.log(`βœ… Initialization complete: ${processedCount} processed, ${skippedCount} skipped`);
76
  }
77
 
78
  // Get next pending image to process
 
139
  completed: number;
140
  failed: number;
141
  }> {
142
+ try {
143
+ const [total, pending, processing, completed, failed] = await Promise.all([
144
+ db.trainerScanProgress.count(),
145
+ db.trainerScanProgress.where('status').equals('pending').count(),
146
+ db.trainerScanProgress.where('status').equals('processing').count(),
147
+ db.trainerScanProgress.where('status').equals('completed').count(),
148
+ db.trainerScanProgress.where('status').equals('failed').count(),
149
+ ]);
150
+
151
+ return {
152
+ total,
153
+ pending,
154
+ processing,
155
+ completed,
156
+ failed
157
+ };
158
+ } catch (error) {
159
+ console.error('❌ Failed to get scanning stats:', error);
160
+ return {
161
+ total: 0,
162
+ pending: 0,
163
+ processing: 0,
164
+ completed: 0,
165
+ failed: 0
166
+ };
167
  }
 
 
168
  }
169
 
170
  // Get all completed scans for a specific trainer
171
  export async function getCompletedScansForTrainer(trainerName: string): Promise<TrainerScanProgress[]> {
172
+ try {
173
+ return await db.trainerScanProgress
174
+ .where('trainerName').equals(trainerName)
175
+ .and(record => record.status === 'completed')
176
+ .toArray();
177
+ } catch (error) {
178
+ console.error(`❌ Failed to get completed scans for trainer ${trainerName}:`, error);
179
+ return [];
 
 
 
 
180
  }
 
 
181
  }
182
 
183
  // Reset all failed scans back to pending (for retry)
184
  export async function resetFailedScans(): Promise<number> {
185
+ try {
186
+ const failedRecords = await db.trainerScanProgress.where('status').equals('failed').toArray();
187
+
188
+ for (const record of failedRecords) {
189
+ await db.trainerScanProgress.update(record.imagePath, {
190
+ status: 'pending',
 
 
 
 
 
191
  errorMessage: undefined,
192
  startedAt: undefined,
193
  completedAt: undefined
194
+ });
 
 
195
  }
196
+
197
+ return failedRecords.length;
198
+ } catch (error) {
199
+ console.error('❌ Failed to reset failed scans:', error);
200
+ return 0;
201
  }
 
 
 
202
  }
203
 
204
  // Get current processing status (for resuming interrupted sessions)
205
  export async function getCurrentProcessingImage(): Promise<TrainerScanProgress | null> {
206
+ try {
207
+ const processingRecord = await db.trainerScanProgress.where('status').equals('processing').first();
208
+ return processingRecord || null;
209
+ } catch (error) {
210
+ console.error('❌ Failed to get current processing image:', error);
211
+ return null;
 
 
 
212
  }
 
 
213
  }
src/lib/services/trainerScanService.ts CHANGED
@@ -76,63 +76,30 @@ export class TrainerScanService {
76
  // Initialize scanning database with image paths from file
77
  async initializeFromFile(): Promise<void> {
78
  try {
79
- console.log('πŸ” TrainerScanService: Starting initializeFromFile...');
80
-
81
  const response = await fetch('/trainer_image_paths.txt');
82
  if (!response.ok) {
83
  throw new Error(`Failed to fetch trainer_image_paths.txt: ${response.statusText}`);
84
  }
85
 
86
- console.log('πŸ” TrainerScanService: Successfully fetched trainer_image_paths.txt');
87
-
88
  const content = await response.text();
89
  if (!content) {
90
  throw new Error('trainer_image_paths.txt is empty');
91
  }
92
 
93
- console.log(`πŸ” TrainerScanService: File content length: ${content.length}`);
94
- console.log(`πŸ” TrainerScanService: First 200 chars: ${content.substring(0, 200)}`);
95
-
96
- const lines = content.trim().split('\n');
97
- console.log(`πŸ” TrainerScanService: Split into ${lines.length} lines`);
98
-
99
- const imagePaths = lines
100
- .map((path, index) => {
101
- console.log(`πŸ” TrainerScanService: Processing line ${index}: "${path}" (type: ${typeof path})`);
102
- if (typeof path !== 'string') {
103
- console.warn(`πŸ” TrainerScanService: Line ${index} is not a string:`, path);
104
- return '';
105
- }
106
- const trimmed = path.trim();
107
- console.log(`πŸ” TrainerScanService: Line ${index} trimmed: "${trimmed}"`);
108
- return trimmed;
109
- })
110
- .filter((path, index) => {
111
- const isValid = path.length > 0;
112
- console.log(`πŸ” TrainerScanService: Line ${index} valid: ${isValid}`);
113
- return isValid;
114
- });
115
 
116
- console.log(`πŸ” TrainerScanService: Loaded ${imagePaths.length} trainer image paths`);
117
- console.log(`πŸ” TrainerScanService: First 5 paths:`, imagePaths.slice(0, 5));
118
 
119
  if (imagePaths.length === 0) {
120
  throw new Error('No valid image paths found in trainer_image_paths.txt');
121
  }
122
 
123
- console.log('πŸ” TrainerScanService: About to call initializeTrainerScanProgress...');
124
  await initializeTrainerScanProgress(imagePaths);
125
- console.log('πŸ” TrainerScanService: initializeTrainerScanProgress completed successfully');
126
-
127
- console.log('πŸ” TrainerScanService: About to get current state...');
128
- const currentState = await this.getCurrentState();
129
- console.log('πŸ” TrainerScanService: Got current state:', currentState);
130
-
131
- this.notifyStateChange(currentState);
132
- console.log('πŸ” TrainerScanService: initializeFromFile completed successfully');
133
  } catch (error) {
134
- console.error('❌ TrainerScanService: Failed to initialize trainer scan progress:', error);
135
- console.error('❌ TrainerScanService: Error stack:', error instanceof Error ? error.stack : 'No stack trace');
136
  throw new Error('Failed to load trainer image paths');
137
  }
138
  }
 
76
  // Initialize scanning database with image paths from file
77
  async initializeFromFile(): Promise<void> {
78
  try {
 
 
79
  const response = await fetch('/trainer_image_paths.txt');
80
  if (!response.ok) {
81
  throw new Error(`Failed to fetch trainer_image_paths.txt: ${response.statusText}`);
82
  }
83
 
 
 
84
  const content = await response.text();
85
  if (!content) {
86
  throw new Error('trainer_image_paths.txt is empty');
87
  }
88
 
89
+ const imagePaths = content.trim().split('\n')
90
+ .map(path => typeof path === 'string' ? path.trim() : '')
91
+ .filter(path => path.length > 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ console.log(`Loaded ${imagePaths.length} trainer image paths`);
 
94
 
95
  if (imagePaths.length === 0) {
96
  throw new Error('No valid image paths found in trainer_image_paths.txt');
97
  }
98
 
 
99
  await initializeTrainerScanProgress(imagePaths);
100
+ this.notifyStateChange(await this.getCurrentState());
 
 
 
 
 
 
 
101
  } catch (error) {
102
+ console.error('Failed to initialize trainer scan progress:', error);
 
103
  throw new Error('Failed to load trainer image paths');
104
  }
105
  }