offcourse 1.0.0 → 1.1.0
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.
- package/README.md +107 -8
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/inspect.js +1 -1
- package/dist/cli/commands/inspect.js.map +1 -1
- package/dist/cli/commands/sync.d.ts +1 -2
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +17 -15
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/commands/syncHighLevel.d.ts +1 -2
- package/dist/cli/commands/syncHighLevel.d.ts.map +1 -1
- package/dist/cli/commands/syncHighLevel.js +8 -9
- package/dist/cli/commands/syncHighLevel.js.map +1 -1
- package/dist/cli/commands/syncLearningSuite.d.ts +35 -0
- package/dist/cli/commands/syncLearningSuite.d.ts.map +1 -0
- package/dist/cli/commands/syncLearningSuite.js +765 -0
- package/dist/cli/commands/syncLearningSuite.js.map +1 -0
- package/dist/cli/index.js +39 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/configManager.d.ts.map +1 -1
- package/dist/config/configManager.js +4 -0
- package/dist/config/configManager.js.map +1 -1
- package/dist/downloader/hlsDownloader.d.ts +10 -4
- package/dist/downloader/hlsDownloader.d.ts.map +1 -1
- package/dist/downloader/hlsDownloader.js +60 -29
- package/dist/downloader/hlsDownloader.js.map +1 -1
- package/dist/downloader/hlsValidator.d.ts.map +1 -1
- package/dist/downloader/hlsValidator.js +6 -2
- package/dist/downloader/hlsValidator.js.map +1 -1
- package/dist/downloader/index.d.ts +7 -0
- package/dist/downloader/index.d.ts.map +1 -1
- package/dist/downloader/index.js +9 -6
- package/dist/downloader/index.js.map +1 -1
- package/dist/downloader/loomDownloader.d.ts +1 -1
- package/dist/downloader/loomDownloader.d.ts.map +1 -1
- package/dist/downloader/loomDownloader.js +32 -27
- package/dist/downloader/loomDownloader.js.map +1 -1
- package/dist/downloader/queue.d.ts +4 -4
- package/dist/downloader/queue.d.ts.map +1 -1
- package/dist/downloader/queue.js.map +1 -1
- package/dist/downloader/vimeoDownloader.d.ts.map +1 -1
- package/dist/downloader/vimeoDownloader.js +7 -3
- package/dist/downloader/vimeoDownloader.js.map +1 -1
- package/dist/scraper/extractor.d.ts +4 -0
- package/dist/scraper/extractor.d.ts.map +1 -1
- package/dist/scraper/extractor.js +79 -79
- package/dist/scraper/extractor.js.map +1 -1
- package/dist/scraper/highlevel/extractor.d.ts +11 -19
- package/dist/scraper/highlevel/extractor.d.ts.map +1 -1
- package/dist/scraper/highlevel/extractor.js +72 -85
- package/dist/scraper/highlevel/extractor.js.map +1 -1
- package/dist/scraper/highlevel/navigator.d.ts +3 -10
- package/dist/scraper/highlevel/navigator.d.ts.map +1 -1
- package/dist/scraper/highlevel/navigator.js +140 -127
- package/dist/scraper/highlevel/navigator.js.map +1 -1
- package/dist/scraper/highlevel/schemas.d.ts +188 -0
- package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
- package/dist/scraper/highlevel/schemas.js +139 -0
- package/dist/scraper/highlevel/schemas.js.map +1 -0
- package/dist/scraper/learningsuite/extractor.d.ts +50 -0
- package/dist/scraper/learningsuite/extractor.d.ts.map +1 -0
- package/dist/scraper/learningsuite/extractor.js +429 -0
- package/dist/scraper/learningsuite/extractor.js.map +1 -0
- package/dist/scraper/learningsuite/index.d.ts +4 -0
- package/dist/scraper/learningsuite/index.d.ts.map +1 -0
- package/dist/scraper/{ghl → learningsuite}/index.js +1 -1
- package/dist/scraper/learningsuite/index.js.map +1 -0
- package/dist/scraper/learningsuite/navigator.d.ts +122 -0
- package/dist/scraper/learningsuite/navigator.d.ts.map +1 -0
- package/dist/scraper/learningsuite/navigator.js +736 -0
- package/dist/scraper/learningsuite/navigator.js.map +1 -0
- package/dist/scraper/learningsuite/schemas.d.ts +270 -0
- package/dist/scraper/learningsuite/schemas.d.ts.map +1 -0
- package/dist/scraper/learningsuite/schemas.js +147 -0
- package/dist/scraper/learningsuite/schemas.js.map +1 -0
- package/dist/scraper/navigator.d.ts +14 -11
- package/dist/scraper/navigator.d.ts.map +1 -1
- package/dist/scraper/navigator.js +61 -104
- package/dist/scraper/navigator.js.map +1 -1
- package/dist/scraper/schemas.d.ts +57 -0
- package/dist/scraper/schemas.d.ts.map +1 -0
- package/dist/scraper/schemas.js +135 -0
- package/dist/scraper/schemas.js.map +1 -0
- package/dist/scraper/videoInterceptor.d.ts +4 -0
- package/dist/scraper/videoInterceptor.d.ts.map +1 -1
- package/dist/scraper/videoInterceptor.js +66 -51
- package/dist/scraper/videoInterceptor.js.map +1 -1
- package/dist/shared/auth.d.ts +9 -9
- package/dist/shared/auth.d.ts.map +1 -1
- package/dist/shared/auth.js +24 -38
- package/dist/shared/auth.js.map +1 -1
- package/dist/shared/firebase.d.ts +60 -0
- package/dist/shared/firebase.d.ts.map +1 -0
- package/dist/shared/firebase.js +102 -0
- package/dist/shared/firebase.js.map +1 -0
- package/dist/shared/fs.d.ts.map +1 -1
- package/dist/shared/fs.js +4 -0
- package/dist/shared/fs.js.map +1 -1
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +3 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/slug.d.ts +11 -0
- package/dist/shared/slug.d.ts.map +1 -0
- package/{src/shared/slug.ts → dist/shared/slug.js} +10 -11
- package/dist/shared/slug.js.map +1 -0
- package/dist/shared/url.d.ts +43 -0
- package/dist/shared/url.d.ts.map +1 -0
- package/{src/shared/url.ts → dist/shared/url.js} +12 -15
- package/dist/shared/url.js.map +1 -0
- package/dist/state/database.d.ts +1 -0
- package/dist/state/database.d.ts.map +1 -1
- package/dist/state/database.js +3 -0
- package/dist/state/database.js.map +1 -1
- package/dist/storage/fileSystem.d.ts +17 -17
- package/dist/storage/fileSystem.d.ts.map +1 -1
- package/dist/storage/fileSystem.js +39 -31
- package/dist/storage/fileSystem.js.map +1 -1
- package/package.json +5 -2
- package/.github/workflows/ci.yml +0 -50
- package/.husky/commit-msg +0 -2
- package/.husky/pre-commit +0 -1
- package/.husky/pre-push +0 -3
- package/.prettierrc +0 -8
- package/.release-it.json +0 -23
- package/ARCHITECTURE.md +0 -233
- package/CHANGELOG.md +0 -78
- package/commitlint.config.js +0 -4
- package/dist/ai/openRouter.d.ts +0 -47
- package/dist/ai/openRouter.d.ts.map +0 -1
- package/dist/ai/openRouter.js +0 -116
- package/dist/ai/openRouter.js.map +0 -1
- package/dist/ai/transcriptPolisher.d.ts +0 -24
- package/dist/ai/transcriptPolisher.d.ts.map +0 -1
- package/dist/ai/transcriptPolisher.js +0 -89
- package/dist/ai/transcriptPolisher.js.map +0 -1
- package/dist/cli/commands/enrich.d.ts +0 -14
- package/dist/cli/commands/enrich.d.ts.map +0 -1
- package/dist/cli/commands/enrich.js +0 -271
- package/dist/cli/commands/enrich.js.map +0 -1
- package/dist/cli/commands/syncGhl.d.ts +0 -20
- package/dist/cli/commands/syncGhl.d.ts.map +0 -1
- package/dist/cli/commands/syncGhl.js +0 -483
- package/dist/cli/commands/syncGhl.js.map +0 -1
- package/dist/cli/commands/syncHighLevel.test.d.ts +0 -2
- package/dist/cli/commands/syncHighLevel.test.d.ts.map +0 -1
- package/dist/cli/commands/syncHighLevel.test.js +0 -102
- package/dist/cli/commands/syncHighLevel.test.js.map +0 -1
- package/dist/config/paths.test.d.ts +0 -2
- package/dist/config/paths.test.d.ts.map +0 -1
- package/dist/config/paths.test.js +0 -70
- package/dist/config/paths.test.js.map +0 -1
- package/dist/config/schema.test.d.ts +0 -2
- package/dist/config/schema.test.d.ts.map +0 -1
- package/dist/config/schema.test.js +0 -151
- package/dist/config/schema.test.js.map +0 -1
- package/dist/downloader/hlsDownloader.test.d.ts +0 -2
- package/dist/downloader/hlsDownloader.test.d.ts.map +0 -1
- package/dist/downloader/hlsDownloader.test.js +0 -116
- package/dist/downloader/hlsDownloader.test.js.map +0 -1
- package/dist/downloader/loomDownloader.test.d.ts +0 -2
- package/dist/downloader/loomDownloader.test.d.ts.map +0 -1
- package/dist/downloader/loomDownloader.test.js +0 -36
- package/dist/downloader/loomDownloader.test.js.map +0 -1
- package/dist/downloader/queue.test.d.ts +0 -2
- package/dist/downloader/queue.test.d.ts.map +0 -1
- package/dist/downloader/queue.test.js +0 -158
- package/dist/downloader/queue.test.js.map +0 -1
- package/dist/downloader/videoDownloader.d.ts +0 -32
- package/dist/downloader/videoDownloader.d.ts.map +0 -1
- package/dist/downloader/videoDownloader.js +0 -173
- package/dist/downloader/videoDownloader.js.map +0 -1
- package/dist/downloader/vimeoDownloader.test.d.ts +0 -2
- package/dist/downloader/vimeoDownloader.test.d.ts.map +0 -1
- package/dist/downloader/vimeoDownloader.test.js +0 -51
- package/dist/downloader/vimeoDownloader.test.js.map +0 -1
- package/dist/scraper/auth.d.ts +0 -29
- package/dist/scraper/auth.d.ts.map +0 -1
- package/dist/scraper/auth.js +0 -115
- package/dist/scraper/auth.js.map +0 -1
- package/dist/scraper/extractor.test.d.ts +0 -2
- package/dist/scraper/extractor.test.d.ts.map +0 -1
- package/dist/scraper/extractor.test.js +0 -65
- package/dist/scraper/extractor.test.js.map +0 -1
- package/dist/scraper/ghl/auth.d.ts +0 -25
- package/dist/scraper/ghl/auth.d.ts.map +0 -1
- package/dist/scraper/ghl/auth.js +0 -187
- package/dist/scraper/ghl/auth.js.map +0 -1
- package/dist/scraper/ghl/extractor.d.ts +0 -96
- package/dist/scraper/ghl/extractor.d.ts.map +0 -1
- package/dist/scraper/ghl/extractor.js +0 -345
- package/dist/scraper/ghl/extractor.js.map +0 -1
- package/dist/scraper/ghl/index.d.ts +0 -4
- package/dist/scraper/ghl/index.d.ts.map +0 -1
- package/dist/scraper/ghl/index.js.map +0 -1
- package/dist/scraper/ghl/navigator.d.ts +0 -93
- package/dist/scraper/ghl/navigator.d.ts.map +0 -1
- package/dist/scraper/ghl/navigator.js +0 -447
- package/dist/scraper/ghl/navigator.js.map +0 -1
- package/dist/scraper/highlevel/auth.d.ts +0 -25
- package/dist/scraper/highlevel/auth.d.ts.map +0 -1
- package/dist/scraper/highlevel/auth.js +0 -189
- package/dist/scraper/highlevel/auth.js.map +0 -1
- package/dist/scraper/highlevel/extractor.test.d.ts +0 -2
- package/dist/scraper/highlevel/extractor.test.d.ts.map +0 -1
- package/dist/scraper/highlevel/extractor.test.js +0 -101
- package/dist/scraper/highlevel/extractor.test.js.map +0 -1
- package/dist/scraper/highlevel/navigator.test.d.ts +0 -2
- package/dist/scraper/highlevel/navigator.test.d.ts.map +0 -1
- package/dist/scraper/highlevel/navigator.test.js +0 -78
- package/dist/scraper/highlevel/navigator.test.js.map +0 -1
- package/dist/scraper/navigator.test.d.ts +0 -2
- package/dist/scraper/navigator.test.d.ts.map +0 -1
- package/dist/scraper/navigator.test.js +0 -63
- package/dist/scraper/navigator.test.js.map +0 -1
- package/dist/scraper/skoolApi.d.ts +0 -17
- package/dist/scraper/skoolApi.d.ts.map +0 -1
- package/dist/scraper/skoolApi.js +0 -72
- package/dist/scraper/skoolApi.js.map +0 -1
- package/dist/state/database.test.d.ts +0 -2
- package/dist/state/database.test.d.ts.map +0 -1
- package/dist/state/database.test.js +0 -34
- package/dist/state/database.test.js.map +0 -1
- package/dist/transcription/whisperService.d.ts +0 -27
- package/dist/transcription/whisperService.d.ts.map +0 -1
- package/dist/transcription/whisperService.js +0 -102
- package/dist/transcription/whisperService.js.map +0 -1
- package/eslint.config.js +0 -55
- package/src/__fixtures__/highlevel-post-response.json +0 -68
- package/src/__fixtures__/hls-master-playlist.m3u8 +0 -24
- package/src/cli/commands/__snapshots__/syncHighLevel.test.ts.snap +0 -38
- package/src/cli/commands/config.ts +0 -74
- package/src/cli/commands/inspect.ts +0 -441
- package/src/cli/commands/login.ts +0 -68
- package/src/cli/commands/status.ts +0 -147
- package/src/cli/commands/sync.ts +0 -1235
- package/src/cli/commands/syncHighLevel.test.ts +0 -144
- package/src/cli/commands/syncHighLevel.ts +0 -639
- package/src/cli/index.ts +0 -121
- package/src/config/configManager.ts +0 -75
- package/src/config/paths.test.ts +0 -83
- package/src/config/paths.ts +0 -36
- package/src/config/schema.test.ts +0 -173
- package/src/config/schema.ts +0 -65
- package/src/downloader/hlsDownloader.test.ts +0 -148
- package/src/downloader/hlsDownloader.ts +0 -327
- package/src/downloader/hlsValidator.ts +0 -196
- package/src/downloader/index.ts +0 -122
- package/src/downloader/loomDownloader.test.ts +0 -43
- package/src/downloader/loomDownloader.ts +0 -742
- package/src/downloader/queue.test.ts +0 -199
- package/src/downloader/queue.ts +0 -118
- package/src/downloader/vimeoDownloader.test.ts +0 -62
- package/src/downloader/vimeoDownloader.ts +0 -722
- package/src/scraper/extractor.test.ts +0 -124
- package/src/scraper/extractor.ts +0 -757
- package/src/scraper/highlevel/__snapshots__/extractor.test.ts.snap +0 -41
- package/src/scraper/highlevel/extractor.test.ts +0 -134
- package/src/scraper/highlevel/extractor.ts +0 -537
- package/src/scraper/highlevel/index.ts +0 -2
- package/src/scraper/highlevel/navigator.test.ts +0 -110
- package/src/scraper/highlevel/navigator.ts +0 -668
- package/src/scraper/highlevel/schemas.ts +0 -183
- package/src/scraper/navigator.test.ts +0 -122
- package/src/scraper/navigator.ts +0 -355
- package/src/scraper/schemas.ts +0 -177
- package/src/scraper/videoInterceptor.ts +0 -435
- package/src/shared/auth.test.ts +0 -58
- package/src/shared/auth.ts +0 -251
- package/src/shared/firebase.ts +0 -151
- package/src/shared/fs.ts +0 -80
- package/src/shared/http.ts +0 -34
- package/src/shared/index.ts +0 -6
- package/src/shared/url.test.ts +0 -122
- package/src/state/database.test.ts +0 -49
- package/src/state/database.ts +0 -919
- package/src/state/index.ts +0 -14
- package/src/storage/fileSystem.test.ts +0 -64
- package/src/storage/fileSystem.ts +0 -175
- package/tsconfig.json +0 -28
- package/vitest.config.ts +0 -29
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { parseNextData, extractModulesFromNextData, extractLessonAccessFromNextData, } from "./schemas.js";
|
|
2
|
+
// Browser automation - requires Playwright
|
|
3
|
+
/* v8 ignore start */
|
|
2
4
|
/**
|
|
3
5
|
* Extracts the course/community name from page data.
|
|
4
6
|
*/
|
|
@@ -13,41 +15,29 @@ export async function extractCourseName(page) {
|
|
|
13
15
|
* Skool embeds course structure as JSON in a script tag.
|
|
14
16
|
*/
|
|
15
17
|
export async function extractModulesFromJson(page) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
// Get the raw JSON from the page
|
|
19
|
+
const nextDataJson = await page.evaluate(() => {
|
|
18
20
|
const nextDataScript = document.getElementById("__NEXT_DATA__");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
results.push({
|
|
35
|
-
name: title,
|
|
36
|
-
slug,
|
|
37
|
-
url: "",
|
|
38
|
-
isLocked: !hasAccess,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (results.length > 0)
|
|
43
|
-
return results;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// Fall through to regex approach
|
|
21
|
+
return nextDataScript?.textContent ?? null;
|
|
22
|
+
});
|
|
23
|
+
// Parse and validate with Zod schema (in Node context)
|
|
24
|
+
if (nextDataJson) {
|
|
25
|
+
const parsed = parseNextData(nextDataJson);
|
|
26
|
+
if (parsed) {
|
|
27
|
+
const skoolModules = extractModulesFromNextData(parsed);
|
|
28
|
+
if (skoolModules.length > 0) {
|
|
29
|
+
const baseUrl = page.url().split("/classroom")[0];
|
|
30
|
+
return skoolModules.map((m) => ({
|
|
31
|
+
name: m.title,
|
|
32
|
+
slug: m.slug,
|
|
33
|
+
url: `${baseUrl}/classroom/${m.slug}`,
|
|
34
|
+
isLocked: !m.hasAccess,
|
|
35
|
+
}));
|
|
48
36
|
}
|
|
49
37
|
}
|
|
50
|
-
|
|
38
|
+
}
|
|
39
|
+
// Fallback: Find script tags that contain course data (regex approach)
|
|
40
|
+
const modules = await page.evaluate(() => {
|
|
51
41
|
const scripts = Array.from(document.querySelectorAll("script"));
|
|
52
42
|
const results = [];
|
|
53
43
|
for (const script of scripts) {
|
|
@@ -94,29 +84,22 @@ export async function extractLessons(page, moduleUrl) {
|
|
|
94
84
|
await page.waitForLoadState("domcontentloaded");
|
|
95
85
|
await page.waitForTimeout(2000);
|
|
96
86
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// First try to get hasAccess from __NEXT_DATA__
|
|
100
|
-
const accessMap = new Map();
|
|
87
|
+
// Get __NEXT_DATA__ and parse it in Node context
|
|
88
|
+
const nextDataJson = await page.evaluate(() => {
|
|
101
89
|
const nextDataScript = document.getElementById("__NEXT_DATA__");
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (lessonId && typeof hasAccess === "boolean") {
|
|
111
|
-
accessMap.set(lessonId, hasAccess);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Ignore parse errors
|
|
118
|
-
}
|
|
90
|
+
return nextDataScript?.textContent ?? null;
|
|
91
|
+
});
|
|
92
|
+
// Build access map from validated data
|
|
93
|
+
let accessMap = new Map();
|
|
94
|
+
if (nextDataJson) {
|
|
95
|
+
const parsed = parseNextData(nextDataJson);
|
|
96
|
+
if (parsed) {
|
|
97
|
+
accessMap = extractLessonAccessFromNextData(parsed);
|
|
119
98
|
}
|
|
99
|
+
}
|
|
100
|
+
// Extract lesson links from DOM
|
|
101
|
+
const lessonData = await page.evaluate(() => {
|
|
102
|
+
const results = [];
|
|
120
103
|
// Skool uses styled-components with "ChildrenLink" in the class name
|
|
121
104
|
const lessonLinks = document.querySelectorAll('a[class*="ChildrenLink"]');
|
|
122
105
|
lessonLinks.forEach((link, index) => {
|
|
@@ -126,39 +109,27 @@ export async function extractLessons(page, moduleUrl) {
|
|
|
126
109
|
// Extract lesson ID from URL (?md=...)
|
|
127
110
|
const urlParams = new URL(href).searchParams;
|
|
128
111
|
const lessonId = urlParams.get("md") ?? "";
|
|
129
|
-
// Check hasAccess from JSON data first
|
|
130
|
-
let isLocked = false;
|
|
131
|
-
if (accessMap.has(lessonId)) {
|
|
132
|
-
isLocked = !accessMap.get(lessonId);
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// Fallback: Check for lock icon in DOM
|
|
136
|
-
let parent = anchor;
|
|
137
|
-
for (let i = 0; i < 3 && parent; i++) {
|
|
138
|
-
if (parent.querySelector('[class*="lock"], [class*="Lock"], svg[class*="lock"], svg[class*="Lock"]')) {
|
|
139
|
-
isLocked = true;
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
parent = parent.parentElement;
|
|
143
|
-
}
|
|
144
|
-
// Also check if the link itself has a lock indicator
|
|
145
|
-
if (anchor.querySelector('[class*="lock"], [class*="Lock"]')) {
|
|
146
|
-
isLocked = true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
112
|
if (lessonId && !results.some((l) => l.slug === lessonId)) {
|
|
150
|
-
results.push({
|
|
151
|
-
name,
|
|
152
|
-
slug: lessonId,
|
|
153
|
-
url: href,
|
|
154
|
-
index: results.length,
|
|
155
|
-
isLocked,
|
|
156
|
-
});
|
|
113
|
+
results.push({ name, slug: lessonId, href });
|
|
157
114
|
}
|
|
158
115
|
});
|
|
159
116
|
return results;
|
|
160
117
|
});
|
|
161
|
-
|
|
118
|
+
// Build final lesson list with access info
|
|
119
|
+
return lessonData.map((lesson, index) => {
|
|
120
|
+
let isLocked = false;
|
|
121
|
+
// Check access map from __NEXT_DATA__
|
|
122
|
+
if (accessMap.has(lesson.slug)) {
|
|
123
|
+
isLocked = !accessMap.get(lesson.slug);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
name: lesson.name,
|
|
127
|
+
slug: lesson.slug,
|
|
128
|
+
url: lesson.href,
|
|
129
|
+
index,
|
|
130
|
+
isLocked,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
162
133
|
}
|
|
163
134
|
/**
|
|
164
135
|
* Alternative: Extract modules from the classroom overview page links.
|
|
@@ -198,10 +169,11 @@ export async function extractModulesFromPage(page) {
|
|
|
198
169
|
});
|
|
199
170
|
return modules;
|
|
200
171
|
}
|
|
172
|
+
/* v8 ignore stop */
|
|
201
173
|
/**
|
|
202
174
|
* Checks if a URL points to a specific module (has 8-char hex slug).
|
|
203
175
|
*/
|
|
204
|
-
function isModuleUrl(url) {
|
|
176
|
+
export function isModuleUrl(url) {
|
|
205
177
|
const match = /\/classroom\/([a-f0-9]{8})(?:\?|$)/.exec(url);
|
|
206
178
|
return {
|
|
207
179
|
isModule: !!match,
|
|
@@ -211,10 +183,11 @@ function isModuleUrl(url) {
|
|
|
211
183
|
/**
|
|
212
184
|
* Gets the classroom base URL (without module slug).
|
|
213
185
|
*/
|
|
214
|
-
function getClassroomBaseUrl(url) {
|
|
186
|
+
export function getClassroomBaseUrl(url) {
|
|
215
187
|
// Remove module slug and query params
|
|
216
188
|
return url.replace(/\/classroom\/[a-f0-9]{8}.*$/, "/classroom");
|
|
217
189
|
}
|
|
190
|
+
/* v8 ignore start */
|
|
218
191
|
/**
|
|
219
192
|
* Builds the complete course structure by crawling all modules and lessons.
|
|
220
193
|
*/
|
|
@@ -242,8 +215,7 @@ export async function buildCourseStructure(page, classroomUrl, onProgress) {
|
|
|
242
215
|
}
|
|
243
216
|
onProgress?.({ phase: "modules", totalModules: modules.length });
|
|
244
217
|
const modulesWithLessons = [];
|
|
245
|
-
for (
|
|
246
|
-
const module = modules[i];
|
|
218
|
+
for (const [i, module] of modules.entries()) {
|
|
247
219
|
if (module.isLocked) {
|
|
248
220
|
onProgress?.({
|
|
249
221
|
phase: "lessons",
|
|
@@ -279,22 +251,7 @@ export async function buildCourseStructure(page, classroomUrl, onProgress) {
|
|
|
279
251
|
modules: modulesWithLessons,
|
|
280
252
|
};
|
|
281
253
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
*/
|
|
286
|
-
export function slugify(name) {
|
|
287
|
-
return slugifyLib(name, {
|
|
288
|
-
lowercase: true,
|
|
289
|
-
separator: "-",
|
|
290
|
-
}).substring(0, 100);
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Creates a folder name with index prefix.
|
|
294
|
-
*/
|
|
295
|
-
export function createFolderName(index, name) {
|
|
296
|
-
const prefix = String(index + 1).padStart(2, "0");
|
|
297
|
-
const slug = slugify(name);
|
|
298
|
-
return `${prefix}-${slug}`;
|
|
299
|
-
}
|
|
254
|
+
/* v8 ignore stop */
|
|
255
|
+
// Re-export shared utilities for backwards compatibility
|
|
256
|
+
export { slugify, createFolderName } from "../shared/slug.js";
|
|
300
257
|
//# sourceMappingURL=navigator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigator.js","sourceRoot":"","sources":["../../src/scraper/navigator.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"navigator.js","sourceRoot":"","sources":["../../src/scraper/navigator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for Skool/Next.js __NEXT_DATA__ responses.
|
|
3
|
+
* These validate only the fields we actually use, ignoring everything else.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export declare const SkoolNextDataSchema: z.ZodObject<{
|
|
7
|
+
props: z.ZodOptional<z.ZodObject<{
|
|
8
|
+
pageProps: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
course: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
children: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
11
|
+
course: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
id: z.ZodOptional<z.ZodString>;
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
metadata: z.ZodOptional<z.ZodObject<{
|
|
15
|
+
title: z.ZodOptional<z.ZodString>;
|
|
16
|
+
videoLink: z.ZodOptional<z.ZodString>;
|
|
17
|
+
}, z.core.$loose>>;
|
|
18
|
+
}, z.core.$loose>>;
|
|
19
|
+
hasAccess: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, z.core.$loose>>>;
|
|
21
|
+
}, z.core.$loose>>;
|
|
22
|
+
selectedModule: z.ZodOptional<z.ZodString>;
|
|
23
|
+
}, z.core.$loose>>;
|
|
24
|
+
}, z.core.$loose>>;
|
|
25
|
+
}, z.core.$loose>;
|
|
26
|
+
export type SkoolNextData = z.infer<typeof SkoolNextDataSchema>;
|
|
27
|
+
export interface SkoolModule {
|
|
28
|
+
slug: string;
|
|
29
|
+
title: string;
|
|
30
|
+
hasAccess: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface SkoolLesson {
|
|
33
|
+
id: string;
|
|
34
|
+
hasAccess: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface SkoolVideoInfo {
|
|
37
|
+
url: string;
|
|
38
|
+
type: "loom" | "vimeo" | "youtube" | "wistia" | "unknown";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Safely parses __NEXT_DATA__ JSON from a script element.
|
|
42
|
+
* Returns null if parsing fails.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseNextData(json: string): SkoolNextData | null;
|
|
45
|
+
/**
|
|
46
|
+
* Extracts modules from parsed __NEXT_DATA__.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractModulesFromNextData(data: SkoolNextData): SkoolModule[];
|
|
49
|
+
/**
|
|
50
|
+
* Extracts lesson access info from parsed __NEXT_DATA__.
|
|
51
|
+
*/
|
|
52
|
+
export declare function extractLessonAccessFromNextData(data: SkoolNextData): Map<string, boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* Extracts video URL from parsed __NEXT_DATA__ for a specific module.
|
|
55
|
+
*/
|
|
56
|
+
export declare function extractVideoFromNextData(data: SkoolNextData, selectedModuleId: string): SkoolVideoInfo | null;
|
|
57
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/scraper/schemas.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmCxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;iBAM9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAMhE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC3D;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAYhE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,GAAG,WAAW,EAAE,CAyB7E;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAezF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,aAAa,EACnB,gBAAgB,EAAE,MAAM,GACvB,cAAc,GAAG,IAAI,CA4BvB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for Skool/Next.js __NEXT_DATA__ responses.
|
|
3
|
+
* These validate only the fields we actually use, ignoring everything else.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Skool Course Child (Module/Lesson)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
const CourseMetadataSchema = z.looseObject({
|
|
10
|
+
title: z.string().optional(),
|
|
11
|
+
videoLink: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
const CourseInfoSchema = z.looseObject({
|
|
14
|
+
id: z.string().optional(),
|
|
15
|
+
name: z.string().optional(), // 8-char hex slug
|
|
16
|
+
metadata: CourseMetadataSchema.optional(),
|
|
17
|
+
});
|
|
18
|
+
const CourseChildSchema = z.looseObject({
|
|
19
|
+
course: CourseInfoSchema.optional(),
|
|
20
|
+
hasAccess: z.boolean().optional(),
|
|
21
|
+
});
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Skool __NEXT_DATA__ PageProps
|
|
24
|
+
// ============================================================================
|
|
25
|
+
const SkoolCourseSchema = z.looseObject({
|
|
26
|
+
children: z.array(CourseChildSchema).optional(),
|
|
27
|
+
});
|
|
28
|
+
const SkoolPagePropsSchema = z.looseObject({
|
|
29
|
+
course: SkoolCourseSchema.optional(),
|
|
30
|
+
selectedModule: z.string().optional(),
|
|
31
|
+
});
|
|
32
|
+
export const SkoolNextDataSchema = z.looseObject({
|
|
33
|
+
props: z
|
|
34
|
+
.looseObject({
|
|
35
|
+
pageProps: SkoolPagePropsSchema.optional(),
|
|
36
|
+
})
|
|
37
|
+
.optional(),
|
|
38
|
+
});
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Helper Functions
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Safely parses __NEXT_DATA__ JSON from a script element.
|
|
44
|
+
* Returns null if parsing fails.
|
|
45
|
+
*/
|
|
46
|
+
export function parseNextData(json) {
|
|
47
|
+
try {
|
|
48
|
+
const data = JSON.parse(json);
|
|
49
|
+
const result = SkoolNextDataSchema.safeParse(data);
|
|
50
|
+
if (result.success) {
|
|
51
|
+
return result.data;
|
|
52
|
+
}
|
|
53
|
+
console.warn("[parseNextData] Validation failed:", z.treeifyError(result.error));
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extracts modules from parsed __NEXT_DATA__.
|
|
62
|
+
*/
|
|
63
|
+
export function extractModulesFromNextData(data) {
|
|
64
|
+
const children = data.props?.pageProps?.course?.children;
|
|
65
|
+
if (!Array.isArray(children))
|
|
66
|
+
return [];
|
|
67
|
+
const modules = [];
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
for (const child of children) {
|
|
70
|
+
const course = child.course;
|
|
71
|
+
if (!course?.name)
|
|
72
|
+
continue;
|
|
73
|
+
// Skool module slugs are 8-char hex strings
|
|
74
|
+
if (!/^[a-f0-9]{8}$/.test(course.name))
|
|
75
|
+
continue;
|
|
76
|
+
if (seen.has(course.name))
|
|
77
|
+
continue;
|
|
78
|
+
seen.add(course.name);
|
|
79
|
+
modules.push({
|
|
80
|
+
slug: course.name,
|
|
81
|
+
title: course.metadata?.title ?? `Module ${modules.length + 1}`,
|
|
82
|
+
hasAccess: child.hasAccess !== false,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return modules;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Extracts lesson access info from parsed __NEXT_DATA__.
|
|
89
|
+
*/
|
|
90
|
+
export function extractLessonAccessFromNextData(data) {
|
|
91
|
+
const accessMap = new Map();
|
|
92
|
+
const children = data.props?.pageProps?.course?.children;
|
|
93
|
+
if (!Array.isArray(children))
|
|
94
|
+
return accessMap;
|
|
95
|
+
for (const child of children) {
|
|
96
|
+
const id = child.course?.id;
|
|
97
|
+
const hasAccess = child.hasAccess;
|
|
98
|
+
if (id && typeof hasAccess === "boolean") {
|
|
99
|
+
accessMap.set(id, hasAccess);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return accessMap;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Extracts video URL from parsed __NEXT_DATA__ for a specific module.
|
|
106
|
+
*/
|
|
107
|
+
export function extractVideoFromNextData(data, selectedModuleId) {
|
|
108
|
+
const children = data.props?.pageProps?.course?.children;
|
|
109
|
+
if (!Array.isArray(children))
|
|
110
|
+
return null;
|
|
111
|
+
for (const child of children) {
|
|
112
|
+
if (child.course?.id === selectedModuleId) {
|
|
113
|
+
const videoLink = child.course.metadata?.videoLink;
|
|
114
|
+
if (!videoLink)
|
|
115
|
+
return null;
|
|
116
|
+
// Determine video type
|
|
117
|
+
if (videoLink.includes("loom.com")) {
|
|
118
|
+
const embedUrl = videoLink.replace("/share/", "/embed/").split("?")[0];
|
|
119
|
+
return { url: embedUrl ?? videoLink, type: "loom" };
|
|
120
|
+
}
|
|
121
|
+
if (videoLink.includes("vimeo.com")) {
|
|
122
|
+
return { url: videoLink, type: "vimeo" };
|
|
123
|
+
}
|
|
124
|
+
if (videoLink.includes("youtube.com") || videoLink.includes("youtu.be")) {
|
|
125
|
+
return { url: videoLink, type: "youtube" };
|
|
126
|
+
}
|
|
127
|
+
if (videoLink.includes("wistia")) {
|
|
128
|
+
return { url: videoLink, type: "wistia" };
|
|
129
|
+
}
|
|
130
|
+
return { url: videoLink, type: "unknown" };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/scraper/schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"videoInterceptor.d.ts","sourceRoot":"","sources":["../../src/scraper/videoInterceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"videoInterceptor.d.ts","sourceRoot":"","sources":["../../src/scraper/videoInterceptor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAsDvC;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAQ,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqNnF;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,SAAS,SAAQ,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiJpD"}
|