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,505 @@
1
+ import { FirebaseAuthTokenSchema, PortalSettingsResponseSchema, ProductResponseSchema, CategoriesResponseSchema, PostsResponseSchema, safeParse, } from "./schemas.js";
2
+ // Browser/API automation - requires Playwright
3
+ /* v8 ignore start */
4
+ /**
5
+ * Extracts the location ID from the HighLevel portal.
6
+ * The location ID is used in all API calls.
7
+ */
8
+ export async function extractLocationId(page) {
9
+ // Wait for API calls that contain the location ID
10
+ const locationId = await page.evaluate(() => {
11
+ // Try to find it in the URL of any API call
12
+ const scripts = Array.from(document.querySelectorAll("script"));
13
+ for (const script of scripts) {
14
+ const content = script.textContent ?? "";
15
+ // Look for location ID pattern in HighLevel (typically in API URLs)
16
+ const match = /locations\/([A-Za-z0-9]+)/.exec(content);
17
+ if (match?.[1] && match[1].length > 10) {
18
+ return match[1];
19
+ }
20
+ }
21
+ // Try to find it in localStorage or sessionStorage
22
+ for (const storage of [localStorage, sessionStorage]) {
23
+ for (let i = 0; i < storage.length; i++) {
24
+ const key = storage.key(i);
25
+ if (key) {
26
+ const value = storage.getItem(key);
27
+ if (value) {
28
+ const match = /"locationId":\s*"([A-Za-z0-9]+)"/.exec(value);
29
+ if (match?.[1])
30
+ return match[1];
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return null;
36
+ });
37
+ return locationId;
38
+ }
39
+ /**
40
+ * Extracts portal settings including location ID from the API.
41
+ */
42
+ export async function extractPortalSettings(page, domain) {
43
+ try {
44
+ // Use page.request to make the API call
45
+ const response = await page.request.get(`https://services.leadconnectorhq.com/clientclub/portal-settings?domain=${domain}`);
46
+ if (!response.ok())
47
+ return null;
48
+ const data = await response.json();
49
+ // Validate response with Zod schema
50
+ const parsed = safeParse(PortalSettingsResponseSchema, data, "extractPortalSettings");
51
+ if (!parsed)
52
+ return null;
53
+ return {
54
+ locationId: parsed.locationId,
55
+ portalName: parsed.portalName ?? parsed.name ?? "HighLevel Course",
56
+ };
57
+ }
58
+ catch {
59
+ // Fall through
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Extracts course list from the courses library page.
65
+ */
66
+ export async function extractCourses(page) {
67
+ // Wait for the course cards to load
68
+ await page.waitForTimeout(2000);
69
+ const courses = await page.evaluate(() => {
70
+ const results = [];
71
+ // Find course cards - HighLevel uses various patterns
72
+ const courseCards = document.querySelectorAll('[class*="course-card"], [class*="CourseCard"], [data-product-id], [class*="product-card"]');
73
+ // If no specific cards found, try to find links to course pages
74
+ if (courseCards.length === 0) {
75
+ const courseLinks = document.querySelectorAll('a[href*="/courses/products/"]');
76
+ const seen = new Set();
77
+ courseLinks.forEach((link) => {
78
+ const href = link.href;
79
+ const match = /\/courses\/products\/([a-f0-9-]+)/.exec(href);
80
+ if (match?.[1] && !seen.has(match[1])) {
81
+ seen.add(match[1]);
82
+ const title = link.querySelector("h3, h4, [class*='title']")?.textContent?.trim() ??
83
+ link.textContent?.trim() ??
84
+ `Course ${results.length + 1}`;
85
+ results.push({
86
+ id: match[1],
87
+ title,
88
+ description: "",
89
+ slug: match[1],
90
+ thumbnailUrl: link.querySelector("img")?.src ?? null,
91
+ instructor: null,
92
+ totalLessons: 0,
93
+ progress: 0,
94
+ });
95
+ }
96
+ });
97
+ }
98
+ return results;
99
+ });
100
+ return courses;
101
+ }
102
+ /**
103
+ * Extracts course details from the course overview page via API.
104
+ */
105
+ export async function extractCourseDetails(page, courseUrl, locationId) {
106
+ // Extract product ID from provided courseUrl first
107
+ let productId;
108
+ const courseUrlMatch = /\/courses\/products\/([a-f0-9-]+)/.exec(courseUrl);
109
+ if (courseUrlMatch?.[1]) {
110
+ productId = courseUrlMatch[1];
111
+ }
112
+ // Fallback: try from current page URL
113
+ if (!productId) {
114
+ const pageUrlMatch = /\/courses\/products\/([a-f0-9-]+)/.exec(page.url());
115
+ productId = pageUrlMatch?.[1];
116
+ }
117
+ if (!productId) {
118
+ console.error("Could not extract product ID from URL:", courseUrl, "page:", page.url());
119
+ return null;
120
+ }
121
+ // Try direct API call first (most reliable)
122
+ if (locationId) {
123
+ try {
124
+ const apiUrl = `https://services.leadconnectorhq.com/membership/locations/${locationId}/products/${productId}`;
125
+ // Get auth token from the page context
126
+ const rawTokenData = await page.evaluate(() => {
127
+ const tokenKey = Object.keys(localStorage).find((k) => k.includes("firebase:authUser"));
128
+ if (!tokenKey)
129
+ return null;
130
+ try {
131
+ return JSON.parse(localStorage.getItem(tokenKey) ?? "{}");
132
+ }
133
+ catch {
134
+ return null;
135
+ }
136
+ });
137
+ const tokenParsed = rawTokenData
138
+ ? safeParse(FirebaseAuthTokenSchema, rawTokenData, "extractCourseDetails.token")
139
+ : null;
140
+ const authToken = tokenParsed?.stsTokenManager.accessToken;
141
+ if (authToken) {
142
+ // Use page.request to make the API call (bypasses CORS)
143
+ const response = await page.request.get(apiUrl, {
144
+ headers: {
145
+ Authorization: `Bearer ${authToken}`,
146
+ },
147
+ });
148
+ if (response.ok()) {
149
+ const data = await response.json();
150
+ // Validate response with Zod schema
151
+ const parsed = safeParse(ProductResponseSchema, data, "extractCourseDetails");
152
+ if (parsed) {
153
+ // The API returns the product directly, not wrapped in a "product" property
154
+ const product = parsed.product ?? parsed;
155
+ const title = product.title;
156
+ if (title && title !== "Unknown Course") {
157
+ return {
158
+ id: product.id ?? productId,
159
+ title,
160
+ description: product.description ?? "",
161
+ slug: product.id ?? productId,
162
+ thumbnailUrl: product.posterImage ?? null,
163
+ instructor: product.instructor ?? null,
164
+ totalLessons: product.postCount ?? 0,
165
+ progress: 0,
166
+ };
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ catch {
173
+ // Continue to DOM fallback silently
174
+ }
175
+ }
176
+ // Fallback to DOM extraction if API fails
177
+ const domCourse = await page.evaluate(() => {
178
+ const urlMatch = /\/courses\/products\/([a-f0-9-]+)/.exec(window.location.href);
179
+ const id = urlMatch?.[1] ?? "";
180
+ // Look for the course title in various places
181
+ let title = "";
182
+ // Method 1: Look for a large heading that's not navigation
183
+ const headings = Array.from(document.querySelectorAll("h1, h2, h3"));
184
+ for (const h of headings) {
185
+ const text = h.textContent?.trim() ?? "";
186
+ const parent = h.closest("nav, header, [class*='nav'], [class*='Nav']");
187
+ // Skip if in navigation, or if it's a generic title
188
+ if (parent)
189
+ continue;
190
+ if (text.length < 4)
191
+ continue;
192
+ if (text.toLowerCase().includes("menu"))
193
+ continue;
194
+ if (text.toLowerCase().includes("login"))
195
+ continue;
196
+ if (text === "HighLevel")
197
+ continue;
198
+ if (text === "Courses")
199
+ continue;
200
+ // Found a good candidate
201
+ title = text;
202
+ break;
203
+ }
204
+ // Method 2: Look for text with "lesson" count indicator nearby
205
+ if (!title) {
206
+ const lessonIndicators = Array.from(document.querySelectorAll("[class*='lesson'], [class*='Lesson']"));
207
+ for (const indicator of lessonIndicators) {
208
+ const parent = indicator.closest("[class*='card'], [class*='Card'], [class*='product'], [class*='Product']");
209
+ if (parent) {
210
+ const heading = parent.querySelector("h1, h2, h3, h4");
211
+ if (heading?.textContent?.trim()) {
212
+ title = heading.textContent.trim();
213
+ break;
214
+ }
215
+ }
216
+ }
217
+ }
218
+ if (!title || title.length < 3) {
219
+ title = "Unknown Course";
220
+ }
221
+ return {
222
+ id,
223
+ title,
224
+ description: "",
225
+ slug: id,
226
+ thumbnailUrl: null,
227
+ instructor: null,
228
+ totalLessons: 0,
229
+ progress: 0,
230
+ };
231
+ });
232
+ return domCourse.id ? domCourse : null;
233
+ }
234
+ /**
235
+ * Extracts categories (modules) from a course page.
236
+ */
237
+ export async function extractCategories(page, productId, locationId) {
238
+ try {
239
+ // Get auth token from the page context
240
+ const rawTokenData = await page.evaluate(() => {
241
+ const tokenKey = Object.keys(localStorage).find((k) => k.includes("firebase:authUser"));
242
+ if (!tokenKey)
243
+ return null;
244
+ try {
245
+ return JSON.parse(localStorage.getItem(tokenKey) ?? "{}");
246
+ }
247
+ catch {
248
+ return null;
249
+ }
250
+ });
251
+ const tokenParsed = rawTokenData
252
+ ? safeParse(FirebaseAuthTokenSchema, rawTokenData, "extractCategories.token")
253
+ : null;
254
+ const authToken = tokenParsed?.stsTokenManager.accessToken;
255
+ if (!authToken) {
256
+ console.warn("No auth token found");
257
+ return [];
258
+ }
259
+ // Use page.request to make the API call
260
+ const response = await page.request.get(`https://services.leadconnectorhq.com/membership/locations/${locationId}/user-purchase/categories?product_id=${productId}&source=courses`, {
261
+ headers: {
262
+ Authorization: `Bearer ${authToken}`,
263
+ },
264
+ });
265
+ if (!response.ok()) {
266
+ console.warn("Categories API returned", response.status());
267
+ return [];
268
+ }
269
+ const data = await response.json();
270
+ // Validate response with Zod schema
271
+ const parsed = safeParse(CategoriesResponseSchema, data, "extractCategories");
272
+ if (!parsed)
273
+ return [];
274
+ return parsed.categories.map((cat) => ({
275
+ id: cat.id,
276
+ title: cat.title,
277
+ description: cat.description ?? null,
278
+ position: cat.position ?? 0,
279
+ postCount: cat.postCount ?? 0,
280
+ isLocked: cat.visibility === "locked",
281
+ }));
282
+ }
283
+ catch (error) {
284
+ console.error("Failed to fetch categories:", error);
285
+ return [];
286
+ }
287
+ }
288
+ /**
289
+ * Extracts posts (lessons) from a category.
290
+ */
291
+ export async function extractPosts(page, productId, categoryId, locationId) {
292
+ try {
293
+ // Get auth token from the page context
294
+ const rawTokenData = await page.evaluate(() => {
295
+ const tokenKey = Object.keys(localStorage).find((k) => k.includes("firebase:authUser"));
296
+ if (!tokenKey)
297
+ return null;
298
+ try {
299
+ return JSON.parse(localStorage.getItem(tokenKey) ?? "{}");
300
+ }
301
+ catch {
302
+ return null;
303
+ }
304
+ });
305
+ const tokenParsed = rawTokenData
306
+ ? safeParse(FirebaseAuthTokenSchema, rawTokenData, "extractPosts.token")
307
+ : null;
308
+ const authToken = tokenParsed?.stsTokenManager.accessToken;
309
+ if (!authToken) {
310
+ return [];
311
+ }
312
+ // Use page.request to make the API call
313
+ const response = await page.request.get(`https://services.leadconnectorhq.com/membership/locations/${locationId}/user-purchase/categories/${categoryId}?product_id=${productId}&visibility=published&published_posts=true&source=courses`, {
314
+ headers: {
315
+ Authorization: `Bearer ${authToken}`,
316
+ },
317
+ });
318
+ if (!response.ok()) {
319
+ return [];
320
+ }
321
+ const data = await response.json();
322
+ // Validate response with Zod schema
323
+ const parsed = safeParse(PostsResponseSchema, data, "extractPosts");
324
+ if (!parsed?.category?.posts)
325
+ return [];
326
+ return parsed.category.posts.map((post, index) => ({
327
+ id: post.id,
328
+ title: post.title,
329
+ position: post.indexPosition ?? index,
330
+ categoryId,
331
+ isLocked: post.visibility === "locked",
332
+ isCompleted: false,
333
+ }));
334
+ }
335
+ catch (error) {
336
+ console.error("Failed to fetch posts:", error);
337
+ return [];
338
+ }
339
+ }
340
+ /**
341
+ * Builds the complete course structure.
342
+ */
343
+ export async function buildHighLevelCourseStructure(page, courseUrl, onProgress) {
344
+ // Extract domain and product ID from URL
345
+ const urlObj = new URL(courseUrl);
346
+ const domain = urlObj.hostname;
347
+ const productMatch = /\/courses\/products\/([a-f0-9-]+)/.exec(courseUrl);
348
+ const productId = productMatch?.[1];
349
+ // Get portal settings (includes location ID)
350
+ onProgress?.({ phase: "init" });
351
+ let locationId = null;
352
+ // Try to get location ID from portal settings API
353
+ const settings = await extractPortalSettings(page, domain);
354
+ if (settings) {
355
+ locationId = settings.locationId;
356
+ }
357
+ // Fallback: try to extract from page
358
+ locationId ??= await extractLocationId(page);
359
+ if (!locationId) {
360
+ console.error("Could not determine location ID");
361
+ return null;
362
+ }
363
+ // Set up response interception to capture product data BEFORE navigation
364
+ let capturedCourseTitle = null;
365
+ const responseHandler = async (response) => {
366
+ const url = response.url();
367
+ if (productId &&
368
+ url.includes(`/products/${productId}`) &&
369
+ url.includes("leadconnectorhq.com")) {
370
+ try {
371
+ const data = await response.json();
372
+ const parsed = safeParse(ProductResponseSchema, data, "responseHandler");
373
+ const title = parsed?.product?.title ?? parsed?.title;
374
+ if (title) {
375
+ capturedCourseTitle = title;
376
+ }
377
+ }
378
+ catch {
379
+ // Ignore JSON parse errors
380
+ }
381
+ }
382
+ };
383
+ page.on("response", responseHandler);
384
+ // Navigate to course page (force reload to ensure we capture API responses)
385
+ // Using waitUntil: "networkidle" to ensure all API calls complete
386
+ await page.goto(courseUrl, {
387
+ timeout: 30000,
388
+ waitUntil: "networkidle",
389
+ });
390
+ await page.waitForTimeout(1000);
391
+ // Remove the handler
392
+ page.off("response", responseHandler);
393
+ // Extract course details
394
+ onProgress?.({ phase: "course" });
395
+ const course = await extractCourseDetails(page, courseUrl, locationId);
396
+ if (!course) {
397
+ console.error("Could not extract course details");
398
+ return null;
399
+ }
400
+ // Use captured title if available and course title is unknown
401
+ if (capturedCourseTitle && (course.title === "Unknown Course" || !course.title)) {
402
+ course.title = capturedCourseTitle;
403
+ }
404
+ // Fallback: Try to get title from DOM after page is fully loaded
405
+ if (course.title === "Unknown Course" || !course.title) {
406
+ const domTitle = await page.evaluate(() => {
407
+ // Look for product title in common HighLevel selectors
408
+ const selectors = [
409
+ "[class*='product-title']",
410
+ "[class*='ProductTitle']",
411
+ "[class*='course-title']",
412
+ "[class*='CourseTitle']",
413
+ "h1.title",
414
+ "h2.title",
415
+ "[data-testid='product-title']",
416
+ ".product-header h1",
417
+ ".product-header h2",
418
+ ];
419
+ for (const selector of selectors) {
420
+ const el = document.querySelector(selector);
421
+ const text = el?.textContent?.trim();
422
+ if (text && text.length > 2 && text.length < 200) {
423
+ return text;
424
+ }
425
+ }
426
+ // Try to find a heading that's not generic
427
+ const headings = Array.from(document.querySelectorAll("h1, h2, h3"));
428
+ for (const h of headings) {
429
+ const text = h.textContent?.trim() ?? "";
430
+ if (text.length > 3 &&
431
+ text.length < 150 &&
432
+ !text.toLowerCase().includes("menu") &&
433
+ !text.toLowerCase().includes("login") &&
434
+ text !== "Memberships" &&
435
+ text !== "Courses" &&
436
+ text !== "Unknown Course") {
437
+ return text;
438
+ }
439
+ }
440
+ return null;
441
+ });
442
+ if (domTitle) {
443
+ course.title = domTitle;
444
+ }
445
+ }
446
+ onProgress?.({ phase: "course", courseName: course.title });
447
+ // Extract categories
448
+ onProgress?.({ phase: "categories" });
449
+ const categories = await extractCategories(page, course.id, locationId);
450
+ onProgress?.({ phase: "categories", totalCategories: categories.length });
451
+ // Extract posts for each category
452
+ const categoriesWithPosts = [];
453
+ for (const [i, category] of categories.entries()) {
454
+ if (category.isLocked) {
455
+ onProgress?.({
456
+ phase: "posts",
457
+ currentCategory: category.title,
458
+ currentCategoryIndex: i,
459
+ skippedLocked: true,
460
+ });
461
+ continue;
462
+ }
463
+ onProgress?.({
464
+ phase: "posts",
465
+ currentCategory: category.title,
466
+ currentCategoryIndex: i,
467
+ });
468
+ const posts = await extractPosts(page, course.id, category.id, locationId);
469
+ onProgress?.({
470
+ phase: "posts",
471
+ currentCategory: category.title,
472
+ currentCategoryIndex: i,
473
+ postsFound: posts.length,
474
+ });
475
+ categoriesWithPosts.push({
476
+ ...category,
477
+ posts,
478
+ });
479
+ }
480
+ onProgress?.({ phase: "done" });
481
+ // Update total lessons count
482
+ course.totalLessons = categoriesWithPosts.reduce((total, cat) => total + cat.posts.length, 0);
483
+ return {
484
+ course,
485
+ categories: categoriesWithPosts,
486
+ locationId,
487
+ domain,
488
+ };
489
+ }
490
+ /* v8 ignore stop */
491
+ // Re-export shared utilities for backwards compatibility
492
+ export { slugify, createFolderName } from "../../shared/slug.js";
493
+ /**
494
+ * Constructs the URL for a HighLevel course page.
495
+ */
496
+ export function getHighLevelCourseUrl(domain, productId) {
497
+ return `https://${domain}/courses/products/${productId}?source=courses`;
498
+ }
499
+ /**
500
+ * Constructs the URL for a HighLevel lesson (post) page.
501
+ */
502
+ export function getHighLevelPostUrl(domain, productId, categoryId, postId) {
503
+ return `https://${domain}/courses/products/${productId}/categories/${categoryId}/posts/${postId}?source=courses`;
504
+ }
505
+ //# sourceMappingURL=navigator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigator.js","sourceRoot":"","sources":["../../../src/scraper/highlevel/navigator.ts"],"names":[],"mappings":"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"}