offcourse 0.0.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +255 -20
  2. package/dist/cli/commands/config.d.ts +13 -0
  3. package/dist/cli/commands/config.d.ts.map +1 -0
  4. package/dist/cli/commands/config.js +66 -0
  5. package/dist/cli/commands/config.js.map +1 -0
  6. package/dist/cli/commands/inspect.d.ts +11 -0
  7. package/dist/cli/commands/inspect.d.ts.map +1 -0
  8. package/dist/cli/commands/inspect.js +365 -0
  9. package/dist/cli/commands/inspect.js.map +1 -0
  10. package/dist/cli/commands/login.d.ts +12 -0
  11. package/dist/cli/commands/login.d.ts.map +1 -0
  12. package/dist/cli/commands/login.js +55 -0
  13. package/dist/cli/commands/login.js.map +1 -0
  14. package/dist/cli/commands/status.d.ts +15 -0
  15. package/dist/cli/commands/status.d.ts.map +1 -0
  16. package/dist/cli/commands/status.js +118 -0
  17. package/dist/cli/commands/status.js.map +1 -0
  18. package/dist/cli/commands/sync.d.ts +15 -0
  19. package/dist/cli/commands/sync.d.ts.map +1 -0
  20. package/dist/cli/commands/sync.js +921 -0
  21. package/dist/cli/commands/sync.js.map +1 -0
  22. package/dist/cli/commands/syncHighLevel.d.ts +23 -0
  23. package/dist/cli/commands/syncHighLevel.d.ts.map +1 -0
  24. package/dist/cli/commands/syncHighLevel.js +479 -0
  25. package/dist/cli/commands/syncHighLevel.js.map +1 -0
  26. package/dist/cli/index.d.ts +3 -0
  27. package/dist/cli/index.d.ts.map +1 -0
  28. package/dist/cli/index.js +106 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/config/configManager.d.ts +31 -0
  31. package/dist/config/configManager.d.ts.map +1 -0
  32. package/dist/config/configManager.js +68 -0
  33. package/dist/config/configManager.js.map +1 -0
  34. package/dist/config/paths.d.ts +21 -0
  35. package/dist/config/paths.d.ts.map +1 -0
  36. package/dist/config/paths.js +33 -0
  37. package/dist/config/paths.js.map +1 -0
  38. package/dist/config/schema.d.ts +60 -0
  39. package/dist/config/schema.d.ts.map +1 -0
  40. package/dist/config/schema.js +50 -0
  41. package/dist/config/schema.js.map +1 -0
  42. package/dist/downloader/hlsDownloader.d.ts +58 -0
  43. package/dist/downloader/hlsDownloader.d.ts.map +1 -0
  44. package/dist/downloader/hlsDownloader.js +263 -0
  45. package/dist/downloader/hlsDownloader.js.map +1 -0
  46. package/dist/downloader/hlsValidator.d.ts +35 -0
  47. package/dist/downloader/hlsValidator.d.ts.map +1 -0
  48. package/dist/downloader/hlsValidator.js +152 -0
  49. package/dist/downloader/hlsValidator.js.map +1 -0
  50. package/dist/downloader/index.d.ts +29 -0
  51. package/dist/downloader/index.d.ts.map +1 -0
  52. package/dist/downloader/index.js +55 -0
  53. package/dist/downloader/index.js.map +1 -0
  54. package/dist/downloader/loomDownloader.d.ts +56 -0
  55. package/dist/downloader/loomDownloader.d.ts.map +1 -0
  56. package/dist/downloader/loomDownloader.js +562 -0
  57. package/dist/downloader/loomDownloader.js.map +1 -0
  58. package/dist/downloader/queue.d.ts +56 -0
  59. package/dist/downloader/queue.d.ts.map +1 -0
  60. package/dist/downloader/queue.js +88 -0
  61. package/dist/downloader/queue.js.map +1 -0
  62. package/dist/downloader/vimeoDownloader.d.ts +52 -0
  63. package/dist/downloader/vimeoDownloader.d.ts.map +1 -0
  64. package/dist/downloader/vimeoDownloader.js +569 -0
  65. package/dist/downloader/vimeoDownloader.js.map +1 -0
  66. package/dist/scraper/extractor.d.ts +53 -0
  67. package/dist/scraper/extractor.d.ts.map +1 -0
  68. package/dist/scraper/extractor.js +627 -0
  69. package/dist/scraper/extractor.js.map +1 -0
  70. package/dist/scraper/highlevel/extractor.d.ts +89 -0
  71. package/dist/scraper/highlevel/extractor.d.ts.map +1 -0
  72. package/dist/scraper/highlevel/extractor.js +373 -0
  73. package/dist/scraper/highlevel/extractor.js.map +1 -0
  74. package/dist/scraper/highlevel/index.d.ts +3 -0
  75. package/dist/scraper/highlevel/index.d.ts.map +1 -0
  76. package/dist/scraper/highlevel/index.js +3 -0
  77. package/dist/scraper/highlevel/index.js.map +1 -0
  78. package/dist/scraper/highlevel/navigator.d.ts +86 -0
  79. package/dist/scraper/highlevel/navigator.d.ts.map +1 -0
  80. package/dist/scraper/highlevel/navigator.js +505 -0
  81. package/dist/scraper/highlevel/navigator.js.map +1 -0
  82. package/dist/scraper/highlevel/schemas.d.ts +188 -0
  83. package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
  84. package/dist/scraper/highlevel/schemas.js +139 -0
  85. package/dist/scraper/highlevel/schemas.js.map +1 -0
  86. package/dist/scraper/navigator.d.ts +68 -0
  87. package/dist/scraper/navigator.d.ts.map +1 -0
  88. package/dist/scraper/navigator.js +257 -0
  89. package/dist/scraper/navigator.js.map +1 -0
  90. package/dist/scraper/schemas.d.ts +57 -0
  91. package/dist/scraper/schemas.d.ts.map +1 -0
  92. package/dist/scraper/schemas.js +135 -0
  93. package/dist/scraper/schemas.js.map +1 -0
  94. package/dist/scraper/videoInterceptor.d.ts +23 -0
  95. package/dist/scraper/videoInterceptor.d.ts.map +1 -0
  96. package/dist/scraper/videoInterceptor.js +330 -0
  97. package/dist/scraper/videoInterceptor.js.map +1 -0
  98. package/dist/shared/auth.d.ts +58 -0
  99. package/dist/shared/auth.d.ts.map +1 -0
  100. package/dist/shared/auth.js +197 -0
  101. package/dist/shared/auth.js.map +1 -0
  102. package/dist/shared/firebase.d.ts +60 -0
  103. package/dist/shared/firebase.d.ts.map +1 -0
  104. package/dist/shared/firebase.js +102 -0
  105. package/dist/shared/firebase.js.map +1 -0
  106. package/dist/shared/fs.d.ts +31 -0
  107. package/dist/shared/fs.d.ts.map +1 -0
  108. package/dist/shared/fs.js +77 -0
  109. package/dist/shared/fs.js.map +1 -0
  110. package/dist/shared/http.d.ts +15 -0
  111. package/dist/shared/http.d.ts.map +1 -0
  112. package/dist/shared/http.js +31 -0
  113. package/dist/shared/http.js.map +1 -0
  114. package/dist/shared/index.d.ts +7 -0
  115. package/dist/shared/index.d.ts.map +1 -0
  116. package/dist/shared/index.js +7 -0
  117. package/dist/shared/index.js.map +1 -0
  118. package/dist/shared/slug.d.ts +11 -0
  119. package/dist/shared/slug.d.ts.map +1 -0
  120. package/dist/shared/slug.js +25 -0
  121. package/dist/shared/slug.js.map +1 -0
  122. package/dist/shared/url.d.ts +43 -0
  123. package/dist/shared/url.d.ts.map +1 -0
  124. package/dist/shared/url.js +54 -0
  125. package/dist/shared/url.js.map +1 -0
  126. package/dist/state/database.d.ts +246 -0
  127. package/dist/state/database.d.ts.map +1 -0
  128. package/dist/state/database.js +679 -0
  129. package/dist/state/database.js.map +1 -0
  130. package/dist/state/index.d.ts +2 -0
  131. package/dist/state/index.d.ts.map +1 -0
  132. package/dist/state/index.js +2 -0
  133. package/dist/state/index.js.map +1 -0
  134. package/dist/storage/fileSystem.d.ts +56 -0
  135. package/dist/storage/fileSystem.d.ts.map +1 -0
  136. package/dist/storage/fileSystem.js +129 -0
  137. package/dist/storage/fileSystem.js.map +1 -0
  138. package/package.json +71 -11
  139. package/cli.js +0 -45
@@ -0,0 +1,679 @@
1
+ import Database from "better-sqlite3";
2
+ import { existsSync, mkdirSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { CACHE_DIR } from "../config/paths.js";
5
+ /**
6
+ * Lesson sync status.
7
+ */
8
+ export const LessonStatus = {
9
+ PENDING: "pending",
10
+ SCANNED: "scanned",
11
+ VALIDATED: "validated",
12
+ DOWNLOADED: "downloaded",
13
+ ERROR: "error",
14
+ SKIPPED: "skipped",
15
+ };
16
+ /**
17
+ * Video types supported by the tool.
18
+ */
19
+ export const VideoType = {
20
+ LOOM: "loom",
21
+ VIMEO: "vimeo",
22
+ YOUTUBE: "youtube",
23
+ WISTIA: "wistia",
24
+ NATIVE: "native",
25
+ UNKNOWN: "unknown",
26
+ };
27
+ /**
28
+ * Get the database directory path.
29
+ */
30
+ export function getDbDir() {
31
+ return CACHE_DIR;
32
+ }
33
+ /**
34
+ * Get the database file path for a course.
35
+ */
36
+ export function getDbPath(communitySlug) {
37
+ const safeSlug = communitySlug.replace(/[^a-zA-Z0-9-]/g, "_");
38
+ return join(getDbDir(), `${safeSlug}.db`);
39
+ }
40
+ /**
41
+ * Extract community slug from a Skool URL.
42
+ */
43
+ export function extractCommunitySlug(url) {
44
+ const match = /skool\.com\/([^/]+)/.exec(url);
45
+ return match?.[1] ?? "unknown";
46
+ }
47
+ /**
48
+ * Database manager for course state persistence.
49
+ * SQLite operations - not unit testable without mocking.
50
+ */
51
+ /* v8 ignore start */
52
+ export class CourseDatabase {
53
+ db;
54
+ constructor(communitySlug) {
55
+ const dbPath = getDbPath(communitySlug);
56
+ // Ensure directory exists
57
+ const dir = dirname(dbPath);
58
+ if (!existsSync(dir)) {
59
+ mkdirSync(dir, { recursive: true });
60
+ }
61
+ this.db = new Database(dbPath);
62
+ this.db.pragma("journal_mode = WAL");
63
+ this.initSchema();
64
+ }
65
+ /**
66
+ * Initialize database schema.
67
+ */
68
+ initSchema() {
69
+ this.db.exec(`
70
+ CREATE TABLE IF NOT EXISTS metadata (
71
+ key TEXT PRIMARY KEY,
72
+ value TEXT NOT NULL
73
+ );
74
+
75
+ CREATE TABLE IF NOT EXISTS modules (
76
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
77
+ slug TEXT UNIQUE NOT NULL,
78
+ name TEXT NOT NULL,
79
+ position INTEGER NOT NULL,
80
+ is_locked INTEGER DEFAULT 0,
81
+ created_at TEXT DEFAULT (datetime('now')),
82
+ updated_at TEXT DEFAULT (datetime('now'))
83
+ );
84
+
85
+ CREATE TABLE IF NOT EXISTS lessons (
86
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
87
+ module_id INTEGER NOT NULL,
88
+ slug TEXT NOT NULL,
89
+ name TEXT NOT NULL,
90
+ url TEXT NOT NULL,
91
+ position INTEGER NOT NULL,
92
+ is_locked INTEGER DEFAULT 0,
93
+ status TEXT DEFAULT 'pending',
94
+ video_type TEXT,
95
+ video_url TEXT,
96
+ hls_url TEXT,
97
+ error_message TEXT,
98
+ error_code TEXT,
99
+ last_scanned_at TEXT,
100
+ last_downloaded_at TEXT,
101
+ video_file_size INTEGER,
102
+ created_at TEXT DEFAULT (datetime('now')),
103
+ updated_at TEXT DEFAULT (datetime('now')),
104
+ FOREIGN KEY (module_id) REFERENCES modules(id),
105
+ UNIQUE(module_id, slug)
106
+ );
107
+
108
+ CREATE INDEX IF NOT EXISTS idx_lessons_status ON lessons(status);
109
+ CREATE INDEX IF NOT EXISTS idx_lessons_module ON lessons(module_id);
110
+ CREATE INDEX IF NOT EXISTS idx_lessons_locked ON lessons(is_locked);
111
+ `);
112
+ // Run migrations for existing databases
113
+ this.runMigrations();
114
+ }
115
+ /**
116
+ * Run database migrations for schema updates.
117
+ */
118
+ runMigrations() {
119
+ const tableInfo = this.db.prepare("PRAGMA table_info(lessons)").all();
120
+ // Migration: Add is_locked column if it doesn't exist
121
+ const hasIsLocked = tableInfo.some((col) => col.name === "is_locked");
122
+ if (!hasIsLocked) {
123
+ this.db.exec("ALTER TABLE lessons ADD COLUMN is_locked INTEGER DEFAULT 0");
124
+ }
125
+ // Migration: Add retry_count column if it doesn't exist
126
+ const hasRetryCount = tableInfo.some((col) => col.name === "retry_count");
127
+ if (!hasRetryCount) {
128
+ this.db.exec("ALTER TABLE lessons ADD COLUMN retry_count INTEGER DEFAULT 0");
129
+ }
130
+ }
131
+ /**
132
+ * Close the database connection.
133
+ */
134
+ close() {
135
+ this.db.close();
136
+ }
137
+ // ============================================
138
+ // Metadata Operations
139
+ // ============================================
140
+ /**
141
+ * Set a metadata value.
142
+ */
143
+ setMetadata(key, value) {
144
+ const stmt = this.db.prepare(`
145
+ INSERT INTO metadata (key, value) VALUES (?, ?)
146
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value
147
+ `);
148
+ stmt.run(key, value);
149
+ }
150
+ /**
151
+ * Get a metadata value.
152
+ */
153
+ getMetadata(key) {
154
+ const stmt = this.db.prepare("SELECT value FROM metadata WHERE key = ?");
155
+ const row = stmt.get(key);
156
+ return row?.value ?? null;
157
+ }
158
+ /**
159
+ * Get all course metadata.
160
+ */
161
+ getCourseMetadata() {
162
+ return {
163
+ name: this.getMetadata("course_name") ?? "Unknown Course",
164
+ url: this.getMetadata("course_url") ?? "",
165
+ lastSyncAt: this.getMetadata("last_sync_at"),
166
+ totalModules: this.getModuleCount(),
167
+ totalLessons: this.getLessonCount(),
168
+ };
169
+ }
170
+ /**
171
+ * Update course metadata after sync.
172
+ */
173
+ updateCourseMetadata(name, url) {
174
+ this.setMetadata("course_name", name);
175
+ this.setMetadata("course_url", url);
176
+ this.setMetadata("last_sync_at", new Date().toISOString());
177
+ }
178
+ // ============================================
179
+ // Module Operations
180
+ // ============================================
181
+ /**
182
+ * Upsert a module (insert or update).
183
+ */
184
+ upsertModule(slug, name, position, isLocked = false) {
185
+ const stmt = this.db.prepare(`
186
+ INSERT INTO modules (slug, name, position, is_locked, updated_at)
187
+ VALUES (?, ?, ?, ?, datetime('now'))
188
+ ON CONFLICT(slug) DO UPDATE SET
189
+ name = excluded.name,
190
+ position = excluded.position,
191
+ is_locked = excluded.is_locked,
192
+ updated_at = datetime('now')
193
+ RETURNING *
194
+ `);
195
+ const row = stmt.get(slug, name, position, isLocked ? 1 : 0);
196
+ return this.mapModuleRow(row);
197
+ }
198
+ /**
199
+ * Get all modules.
200
+ */
201
+ getModules() {
202
+ const stmt = this.db.prepare("SELECT * FROM modules ORDER BY position");
203
+ const rows = stmt.all();
204
+ return rows.map((row) => this.mapModuleRow(row));
205
+ }
206
+ /**
207
+ * Get module count.
208
+ */
209
+ getModuleCount() {
210
+ const stmt = this.db.prepare("SELECT COUNT(*) as count FROM modules");
211
+ const row = stmt.get();
212
+ return row.count;
213
+ }
214
+ /**
215
+ * Get module by slug.
216
+ */
217
+ getModuleBySlug(slug) {
218
+ const stmt = this.db.prepare("SELECT * FROM modules WHERE slug = ?");
219
+ const row = stmt.get(slug);
220
+ return row ? this.mapModuleRow(row) : null;
221
+ }
222
+ mapModuleRow(row) {
223
+ return {
224
+ id: row.id,
225
+ slug: row.slug,
226
+ name: row.name,
227
+ position: row.position,
228
+ isLocked: row.is_locked === 1,
229
+ createdAt: row.created_at,
230
+ updatedAt: row.updated_at,
231
+ };
232
+ }
233
+ // ============================================
234
+ // Lesson Operations
235
+ // ============================================
236
+ /**
237
+ * Upsert a lesson (insert or update).
238
+ */
239
+ upsertLesson(moduleId, slug, name, url, position, isLocked = false) {
240
+ const stmt = this.db.prepare(`
241
+ INSERT INTO lessons (module_id, slug, name, url, position, is_locked, updated_at)
242
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
243
+ ON CONFLICT(module_id, slug) DO UPDATE SET
244
+ name = excluded.name,
245
+ url = excluded.url,
246
+ position = excluded.position,
247
+ is_locked = excluded.is_locked,
248
+ updated_at = datetime('now')
249
+ RETURNING *
250
+ `);
251
+ const row = stmt.get(moduleId, slug, name, url, position, isLocked ? 1 : 0);
252
+ return this.mapLessonRow(row);
253
+ }
254
+ /**
255
+ * Update lesson scan results.
256
+ */
257
+ updateLessonScan(lessonId, videoType, videoUrl, hlsUrl, status, errorMessage, errorCode) {
258
+ const stmt = this.db.prepare(`
259
+ UPDATE lessons SET
260
+ video_type = ?,
261
+ video_url = ?,
262
+ hls_url = ?,
263
+ status = ?,
264
+ error_message = ?,
265
+ error_code = ?,
266
+ last_scanned_at = datetime('now'),
267
+ updated_at = datetime('now')
268
+ WHERE id = ?
269
+ `);
270
+ stmt.run(videoType, videoUrl, hlsUrl, status, errorMessage ?? null, errorCode ?? null, lessonId);
271
+ }
272
+ /**
273
+ * Mark lesson as downloaded.
274
+ */
275
+ markLessonDownloaded(lessonId, fileSize) {
276
+ const stmt = this.db.prepare(`
277
+ UPDATE lessons SET
278
+ status = 'downloaded',
279
+ last_downloaded_at = datetime('now'),
280
+ video_file_size = ?,
281
+ error_message = NULL,
282
+ error_code = NULL,
283
+ updated_at = datetime('now')
284
+ WHERE id = ?
285
+ `);
286
+ stmt.run(fileSize ?? null, lessonId);
287
+ }
288
+ /**
289
+ * Mark lesson as error.
290
+ */
291
+ markLessonError(lessonId, errorMessage, errorCode) {
292
+ const stmt = this.db.prepare(`
293
+ UPDATE lessons SET
294
+ status = 'error',
295
+ error_message = ?,
296
+ error_code = ?,
297
+ updated_at = datetime('now')
298
+ WHERE id = ?
299
+ `);
300
+ stmt.run(errorMessage, errorCode ?? null, lessonId);
301
+ }
302
+ /**
303
+ * Mark lesson as skipped (no video).
304
+ */
305
+ markLessonSkipped(lessonId, reason) {
306
+ const stmt = this.db.prepare(`
307
+ UPDATE lessons SET
308
+ status = 'skipped',
309
+ error_message = ?,
310
+ error_code = NULL,
311
+ updated_at = datetime('now')
312
+ WHERE id = ?
313
+ `);
314
+ stmt.run(reason ?? null, lessonId);
315
+ }
316
+ /**
317
+ * Update lesson video type.
318
+ */
319
+ updateLessonVideoType(lessonId, videoType) {
320
+ const stmt = this.db.prepare(`
321
+ UPDATE lessons SET
322
+ video_type = ?,
323
+ updated_at = datetime('now')
324
+ WHERE id = ?
325
+ `);
326
+ stmt.run(videoType, lessonId);
327
+ }
328
+ /**
329
+ * Increment retry count for a lesson.
330
+ */
331
+ incrementRetryCount(lessonId) {
332
+ const stmt = this.db.prepare(`
333
+ UPDATE lessons SET
334
+ retry_count = retry_count + 1,
335
+ updated_at = datetime('now')
336
+ WHERE id = ?
337
+ `);
338
+ stmt.run(lessonId);
339
+ // Return the new retry count
340
+ const getStmt = this.db.prepare("SELECT retry_count FROM lessons WHERE id = ?");
341
+ const row = getStmt.get(lessonId);
342
+ return row?.retry_count ?? 0;
343
+ }
344
+ /**
345
+ * Reset retry count for a lesson.
346
+ */
347
+ resetRetryCount(lessonId) {
348
+ const stmt = this.db.prepare(`
349
+ UPDATE lessons SET
350
+ retry_count = 0,
351
+ updated_at = datetime('now')
352
+ WHERE id = ?
353
+ `);
354
+ stmt.run(lessonId);
355
+ }
356
+ /**
357
+ * Get lessons that failed but can still be retried (retry_count < maxRetries).
358
+ * Only returns retryable errors (not UNSUPPORTED_PROVIDER).
359
+ */
360
+ getLessonsToRetry(maxRetries = 3) {
361
+ const stmt = this.db.prepare(`
362
+ SELECT
363
+ l.*,
364
+ m.name as module_name,
365
+ m.slug as module_slug,
366
+ m.position as module_position
367
+ FROM lessons l
368
+ JOIN modules m ON l.module_id = m.id
369
+ WHERE l.status = 'error'
370
+ AND l.retry_count < ?
371
+ AND (l.error_code IS NULL OR l.error_code NOT IN ('UNSUPPORTED_PROVIDER'))
372
+ ORDER BY m.position, l.position
373
+ `);
374
+ const rows = stmt.all(maxRetries);
375
+ return rows.map((row) => ({
376
+ ...this.mapLessonRow(row),
377
+ moduleName: row.module_name,
378
+ moduleSlug: row.module_slug,
379
+ modulePosition: row.module_position,
380
+ }));
381
+ }
382
+ /**
383
+ * Mark a lesson for retry by setting it back to pending/validated status.
384
+ */
385
+ queueForRetry(lessonId, targetStatus = LessonStatus.PENDING) {
386
+ const stmt = this.db.prepare(`
387
+ UPDATE lessons SET
388
+ status = ?,
389
+ error_message = NULL,
390
+ error_code = NULL,
391
+ updated_at = datetime('now')
392
+ WHERE id = ?
393
+ `);
394
+ stmt.run(targetStatus, lessonId);
395
+ }
396
+ /**
397
+ * Get all lessons.
398
+ */
399
+ getLessons() {
400
+ const stmt = this.db.prepare("SELECT * FROM lessons ORDER BY module_id, position");
401
+ const rows = stmt.all();
402
+ return rows.map((row) => this.mapLessonRow(row));
403
+ }
404
+ /**
405
+ * Get lessons with module info.
406
+ */
407
+ getLessonsWithModules() {
408
+ const stmt = this.db.prepare(`
409
+ SELECT
410
+ l.*,
411
+ m.name as module_name,
412
+ m.slug as module_slug,
413
+ m.position as module_position
414
+ FROM lessons l
415
+ JOIN modules m ON l.module_id = m.id
416
+ ORDER BY m.position, l.position
417
+ `);
418
+ const rows = stmt.all();
419
+ return rows.map((row) => ({
420
+ ...this.mapLessonRow(row),
421
+ moduleName: row.module_name,
422
+ moduleSlug: row.module_slug,
423
+ modulePosition: row.module_position,
424
+ }));
425
+ }
426
+ /**
427
+ * Get lessons by status.
428
+ */
429
+ getLessonsByStatus(status) {
430
+ const stmt = this.db.prepare(`
431
+ SELECT
432
+ l.*,
433
+ m.name as module_name,
434
+ m.slug as module_slug,
435
+ m.position as module_position
436
+ FROM lessons l
437
+ JOIN modules m ON l.module_id = m.id
438
+ WHERE l.status = ?
439
+ ORDER BY m.position, l.position
440
+ `);
441
+ const rows = stmt.all(status);
442
+ return rows.map((row) => ({
443
+ ...this.mapLessonRow(row),
444
+ moduleName: row.module_name,
445
+ moduleSlug: row.module_slug,
446
+ modulePosition: row.module_position,
447
+ }));
448
+ }
449
+ /**
450
+ * Get lessons that need scanning (pending or never scanned).
451
+ */
452
+ getLessonsToScan() {
453
+ const stmt = this.db.prepare(`
454
+ SELECT
455
+ l.*,
456
+ m.name as module_name,
457
+ m.slug as module_slug,
458
+ m.position as module_position
459
+ FROM lessons l
460
+ JOIN modules m ON l.module_id = m.id
461
+ WHERE (l.status = 'pending' OR l.last_scanned_at IS NULL)
462
+ AND l.is_locked = 0
463
+ ORDER BY m.position, l.position
464
+ `);
465
+ const rows = stmt.all();
466
+ return rows.map((row) => ({
467
+ ...this.mapLessonRow(row),
468
+ moduleName: row.module_name,
469
+ moduleSlug: row.module_slug,
470
+ modulePosition: row.module_position,
471
+ }));
472
+ }
473
+ /**
474
+ * Get lessons that need validation (scanned but not validated, with video).
475
+ */
476
+ getLessonsToValidate() {
477
+ const stmt = this.db.prepare(`
478
+ SELECT
479
+ l.*,
480
+ m.name as module_name,
481
+ m.slug as module_slug,
482
+ m.position as module_position
483
+ FROM lessons l
484
+ JOIN modules m ON l.module_id = m.id
485
+ WHERE l.status = 'scanned'
486
+ AND l.video_url IS NOT NULL
487
+ AND l.is_locked = 0
488
+ ORDER BY m.position, l.position
489
+ `);
490
+ const rows = stmt.all();
491
+ return rows.map((row) => ({
492
+ ...this.mapLessonRow(row),
493
+ moduleName: row.module_name,
494
+ moduleSlug: row.module_slug,
495
+ modulePosition: row.module_position,
496
+ }));
497
+ }
498
+ /**
499
+ * Get lessons that are ready for download (validated with HLS URL).
500
+ */
501
+ getLessonsToDownload() {
502
+ const stmt = this.db.prepare(`
503
+ SELECT
504
+ l.*,
505
+ m.name as module_name,
506
+ m.slug as module_slug,
507
+ m.position as module_position
508
+ FROM lessons l
509
+ JOIN modules m ON l.module_id = m.id
510
+ WHERE l.status = 'validated' AND l.hls_url IS NOT NULL
511
+ ORDER BY m.position, l.position
512
+ `);
513
+ const rows = stmt.all();
514
+ return rows.map((row) => ({
515
+ ...this.mapLessonRow(row),
516
+ moduleName: row.module_name,
517
+ moduleSlug: row.module_slug,
518
+ modulePosition: row.module_position,
519
+ }));
520
+ }
521
+ /**
522
+ * Get lesson count.
523
+ */
524
+ getLessonCount() {
525
+ const stmt = this.db.prepare("SELECT COUNT(*) as count FROM lessons");
526
+ const row = stmt.get();
527
+ return row.count;
528
+ }
529
+ /**
530
+ * Get lesson by URL.
531
+ */
532
+ getLessonByUrl(url) {
533
+ const stmt = this.db.prepare("SELECT * FROM lessons WHERE url = ?");
534
+ const row = stmt.get(url);
535
+ return row ? this.mapLessonRow(row) : null;
536
+ }
537
+ /**
538
+ * Get status summary.
539
+ */
540
+ getStatusSummary() {
541
+ const stmt = this.db.prepare(`
542
+ SELECT status, COUNT(*) as count FROM lessons GROUP BY status
543
+ `);
544
+ const rows = stmt.all();
545
+ const summary = {
546
+ pending: 0,
547
+ scanned: 0,
548
+ validated: 0,
549
+ downloaded: 0,
550
+ error: 0,
551
+ skipped: 0,
552
+ locked: 0,
553
+ };
554
+ for (const row of rows) {
555
+ summary[row.status] = row.count;
556
+ }
557
+ // Count locked lessons separately
558
+ const lockedStmt = this.db.prepare(`SELECT COUNT(*) as count FROM lessons WHERE is_locked = 1`);
559
+ const lockedRow = lockedStmt.get();
560
+ summary.locked = lockedRow.count;
561
+ return summary;
562
+ }
563
+ /**
564
+ * Reset all error lessons to pending for retry.
565
+ */
566
+ resetErrorLessons() {
567
+ const stmt = this.db.prepare(`
568
+ UPDATE lessons SET
569
+ status = 'pending',
570
+ error_message = NULL,
571
+ error_code = NULL,
572
+ updated_at = datetime('now')
573
+ WHERE status = 'error'
574
+ `);
575
+ const result = stmt.run();
576
+ return result.changes;
577
+ }
578
+ /**
579
+ * Reset ALL lessons to pending (for --force full rescan).
580
+ * Preserves locked status.
581
+ */
582
+ resetAllLessonsToPending() {
583
+ const stmt = this.db.prepare(`
584
+ UPDATE lessons SET
585
+ status = 'pending',
586
+ video_type = NULL,
587
+ video_url = NULL,
588
+ hls_url = NULL,
589
+ error_message = NULL,
590
+ error_code = NULL,
591
+ retry_count = 0,
592
+ updated_at = datetime('now')
593
+ WHERE is_locked = 0
594
+ `);
595
+ const result = stmt.run();
596
+ return result.changes;
597
+ }
598
+ /**
599
+ * Reset error lessons to validated (for --resume --retry-errors).
600
+ * Only resets lessons that already have an HLS URL.
601
+ */
602
+ resetErrorLessonsForResume() {
603
+ const stmt = this.db.prepare(`
604
+ UPDATE lessons SET
605
+ status = 'validated',
606
+ error_message = NULL,
607
+ error_code = NULL,
608
+ updated_at = datetime('now')
609
+ WHERE status = 'error' AND hls_url IS NOT NULL
610
+ `);
611
+ const result = stmt.run();
612
+ return result.changes;
613
+ }
614
+ /**
615
+ * Get lessons by error code.
616
+ */
617
+ getLessonsByErrorCode(errorCode) {
618
+ const stmt = this.db.prepare(`
619
+ SELECT
620
+ l.*,
621
+ m.name as module_name,
622
+ m.slug as module_slug,
623
+ m.position as module_position
624
+ FROM lessons l
625
+ JOIN modules m ON l.module_id = m.id
626
+ WHERE l.error_code = ?
627
+ ORDER BY m.position, l.position
628
+ `);
629
+ const rows = stmt.all(errorCode);
630
+ return rows.map((row) => ({
631
+ ...this.mapLessonRow(row),
632
+ moduleName: row.module_name,
633
+ moduleSlug: row.module_slug,
634
+ modulePosition: row.module_position,
635
+ }));
636
+ }
637
+ /**
638
+ * Get count of lessons grouped by video type.
639
+ */
640
+ getVideoTypeSummary() {
641
+ const stmt = this.db.prepare(`
642
+ SELECT video_type, COUNT(*) as count
643
+ FROM lessons
644
+ WHERE video_type IS NOT NULL
645
+ GROUP BY video_type
646
+ `);
647
+ const rows = stmt.all();
648
+ const summary = {};
649
+ for (const row of rows) {
650
+ summary[row.video_type] = row.count;
651
+ }
652
+ return summary;
653
+ }
654
+ mapLessonRow(row) {
655
+ return {
656
+ id: row.id,
657
+ moduleId: row.module_id,
658
+ slug: row.slug,
659
+ name: row.name,
660
+ url: row.url,
661
+ position: row.position,
662
+ isLocked: row.is_locked === 1,
663
+ status: row.status,
664
+ videoType: row.video_type,
665
+ videoUrl: row.video_url,
666
+ hlsUrl: row.hls_url,
667
+ errorMessage: row.error_message,
668
+ errorCode: row.error_code,
669
+ retryCount: row.retry_count ?? 0,
670
+ lastScannedAt: row.last_scanned_at,
671
+ lastDownloadedAt: row.last_downloaded_at,
672
+ videoFileSize: row.video_file_size,
673
+ createdAt: row.created_at,
674
+ updatedAt: row.updated_at,
675
+ };
676
+ }
677
+ }
678
+ /* v8 ignore stop */
679
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/state/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { CourseDatabase, LessonStatus, VideoType, extractCommunitySlug, getDbDir, getDbPath, type CourseMetadata, type LessonRecord, type LessonStatusType, type LessonWithModule, type ModuleRecord, type VideoTypeValue, } from "./database.js";
2
+ //# sourceMappingURL=index.d.ts.map