offcourse 0.0.2 → 1.0.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/.github/workflows/ci.yml +50 -0
- package/.husky/commit-msg +2 -0
- package/.husky/pre-commit +1 -0
- package/.husky/pre-push +3 -0
- package/.prettierrc +8 -0
- package/.release-it.json +23 -0
- package/ARCHITECTURE.md +233 -0
- package/CHANGELOG.md +78 -0
- package/README.md +255 -20
- package/commitlint.config.js +4 -0
- package/dist/ai/openRouter.d.ts +47 -0
- package/dist/ai/openRouter.d.ts.map +1 -0
- package/dist/ai/openRouter.js +116 -0
- package/dist/ai/openRouter.js.map +1 -0
- package/dist/ai/transcriptPolisher.d.ts +24 -0
- package/dist/ai/transcriptPolisher.d.ts.map +1 -0
- package/dist/ai/transcriptPolisher.js +89 -0
- package/dist/ai/transcriptPolisher.js.map +1 -0
- package/dist/cli/commands/config.d.ts +13 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +66 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/enrich.d.ts +14 -0
- package/dist/cli/commands/enrich.d.ts.map +1 -0
- package/dist/cli/commands/enrich.js +271 -0
- package/dist/cli/commands/enrich.js.map +1 -0
- package/dist/cli/commands/inspect.d.ts +11 -0
- package/dist/cli/commands/inspect.d.ts.map +1 -0
- package/dist/cli/commands/inspect.js +365 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/login.d.ts +12 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +55 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/status.d.ts +15 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +118 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +16 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +922 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/syncGhl.d.ts +20 -0
- package/dist/cli/commands/syncGhl.d.ts.map +1 -0
- package/dist/cli/commands/syncGhl.js +483 -0
- package/dist/cli/commands/syncGhl.js.map +1 -0
- package/dist/cli/commands/syncHighLevel.d.ts +24 -0
- package/dist/cli/commands/syncHighLevel.d.ts.map +1 -0
- package/dist/cli/commands/syncHighLevel.js +483 -0
- package/dist/cli/commands/syncHighLevel.js.map +1 -0
- package/dist/cli/commands/syncHighLevel.test.d.ts +2 -0
- package/dist/cli/commands/syncHighLevel.test.d.ts.map +1 -0
- package/dist/cli/commands/syncHighLevel.test.js +102 -0
- package/dist/cli/commands/syncHighLevel.test.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +106 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/configManager.d.ts +31 -0
- package/dist/config/configManager.d.ts.map +1 -0
- package/dist/config/configManager.js +64 -0
- package/dist/config/configManager.js.map +1 -0
- package/dist/config/paths.d.ts +21 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +33 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/paths.test.d.ts +2 -0
- package/dist/config/paths.test.d.ts.map +1 -0
- package/dist/config/paths.test.js +70 -0
- package/dist/config/paths.test.js.map +1 -0
- package/dist/config/schema.d.ts +60 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +50 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/schema.test.d.ts +2 -0
- package/dist/config/schema.test.d.ts.map +1 -0
- package/dist/config/schema.test.js +151 -0
- package/dist/config/schema.test.js.map +1 -0
- package/dist/downloader/hlsDownloader.d.ts +58 -0
- package/dist/downloader/hlsDownloader.d.ts.map +1 -0
- package/dist/downloader/hlsDownloader.js +254 -0
- package/dist/downloader/hlsDownloader.js.map +1 -0
- package/dist/downloader/hlsDownloader.test.d.ts +2 -0
- package/dist/downloader/hlsDownloader.test.d.ts.map +1 -0
- package/dist/downloader/hlsDownloader.test.js +116 -0
- package/dist/downloader/hlsDownloader.test.js.map +1 -0
- package/dist/downloader/hlsValidator.d.ts +35 -0
- package/dist/downloader/hlsValidator.d.ts.map +1 -0
- package/dist/downloader/hlsValidator.js +148 -0
- package/dist/downloader/hlsValidator.js.map +1 -0
- package/dist/downloader/index.d.ts +26 -0
- package/dist/downloader/index.d.ts.map +1 -0
- package/dist/downloader/index.js +52 -0
- package/dist/downloader/index.js.map +1 -0
- package/dist/downloader/loomDownloader.d.ts +56 -0
- package/dist/downloader/loomDownloader.d.ts.map +1 -0
- package/dist/downloader/loomDownloader.js +559 -0
- package/dist/downloader/loomDownloader.js.map +1 -0
- package/dist/downloader/loomDownloader.test.d.ts +2 -0
- package/dist/downloader/loomDownloader.test.d.ts.map +1 -0
- package/dist/downloader/loomDownloader.test.js +36 -0
- package/dist/downloader/loomDownloader.test.js.map +1 -0
- package/dist/downloader/queue.d.ts +56 -0
- package/dist/downloader/queue.d.ts.map +1 -0
- package/dist/downloader/queue.js +88 -0
- package/dist/downloader/queue.js.map +1 -0
- package/dist/downloader/queue.test.d.ts +2 -0
- package/dist/downloader/queue.test.d.ts.map +1 -0
- package/dist/downloader/queue.test.js +158 -0
- package/dist/downloader/queue.test.js.map +1 -0
- package/dist/downloader/videoDownloader.d.ts +32 -0
- package/dist/downloader/videoDownloader.d.ts.map +1 -0
- package/dist/downloader/videoDownloader.js +173 -0
- package/dist/downloader/videoDownloader.js.map +1 -0
- package/dist/downloader/vimeoDownloader.d.ts +52 -0
- package/dist/downloader/vimeoDownloader.d.ts.map +1 -0
- package/dist/downloader/vimeoDownloader.js +565 -0
- package/dist/downloader/vimeoDownloader.js.map +1 -0
- package/dist/downloader/vimeoDownloader.test.d.ts +2 -0
- package/dist/downloader/vimeoDownloader.test.d.ts.map +1 -0
- package/dist/downloader/vimeoDownloader.test.js +51 -0
- package/dist/downloader/vimeoDownloader.test.js.map +1 -0
- package/dist/scraper/auth.d.ts +29 -0
- package/dist/scraper/auth.d.ts.map +1 -0
- package/dist/scraper/auth.js +115 -0
- package/dist/scraper/auth.js.map +1 -0
- package/dist/scraper/extractor.d.ts +49 -0
- package/dist/scraper/extractor.d.ts.map +1 -0
- package/dist/scraper/extractor.js +627 -0
- package/dist/scraper/extractor.js.map +1 -0
- package/dist/scraper/extractor.test.d.ts +2 -0
- package/dist/scraper/extractor.test.d.ts.map +1 -0
- package/dist/scraper/extractor.test.js +65 -0
- package/dist/scraper/extractor.test.js.map +1 -0
- package/dist/scraper/ghl/auth.d.ts +25 -0
- package/dist/scraper/ghl/auth.d.ts.map +1 -0
- package/dist/scraper/ghl/auth.js +187 -0
- package/dist/scraper/ghl/auth.js.map +1 -0
- package/dist/scraper/ghl/extractor.d.ts +96 -0
- package/dist/scraper/ghl/extractor.d.ts.map +1 -0
- package/dist/scraper/ghl/extractor.js +345 -0
- package/dist/scraper/ghl/extractor.js.map +1 -0
- package/dist/scraper/ghl/index.d.ts +4 -0
- package/dist/scraper/ghl/index.d.ts.map +1 -0
- package/dist/scraper/ghl/index.js +4 -0
- package/dist/scraper/ghl/index.js.map +1 -0
- package/dist/scraper/ghl/navigator.d.ts +93 -0
- package/dist/scraper/ghl/navigator.d.ts.map +1 -0
- package/dist/scraper/ghl/navigator.js +447 -0
- package/dist/scraper/ghl/navigator.js.map +1 -0
- package/dist/scraper/highlevel/auth.d.ts +25 -0
- package/dist/scraper/highlevel/auth.d.ts.map +1 -0
- package/dist/scraper/highlevel/auth.js +189 -0
- package/dist/scraper/highlevel/auth.js.map +1 -0
- package/dist/scraper/highlevel/extractor.d.ts +97 -0
- package/dist/scraper/highlevel/extractor.d.ts.map +1 -0
- package/dist/scraper/highlevel/extractor.js +386 -0
- package/dist/scraper/highlevel/extractor.js.map +1 -0
- package/dist/scraper/highlevel/extractor.test.d.ts +2 -0
- package/dist/scraper/highlevel/extractor.test.d.ts.map +1 -0
- package/dist/scraper/highlevel/extractor.test.js +101 -0
- package/dist/scraper/highlevel/extractor.test.js.map +1 -0
- package/dist/scraper/highlevel/index.d.ts +3 -0
- package/dist/scraper/highlevel/index.d.ts.map +1 -0
- package/dist/scraper/highlevel/index.js +3 -0
- package/dist/scraper/highlevel/index.js.map +1 -0
- package/dist/scraper/highlevel/navigator.d.ts +93 -0
- package/dist/scraper/highlevel/navigator.d.ts.map +1 -0
- package/dist/scraper/highlevel/navigator.js +492 -0
- package/dist/scraper/highlevel/navigator.js.map +1 -0
- package/dist/scraper/highlevel/navigator.test.d.ts +2 -0
- package/dist/scraper/highlevel/navigator.test.d.ts.map +1 -0
- package/dist/scraper/highlevel/navigator.test.js +78 -0
- package/dist/scraper/highlevel/navigator.test.js.map +1 -0
- package/dist/scraper/navigator.d.ts +65 -0
- package/dist/scraper/navigator.d.ts.map +1 -0
- package/dist/scraper/navigator.js +300 -0
- package/dist/scraper/navigator.js.map +1 -0
- package/dist/scraper/navigator.test.d.ts +2 -0
- package/dist/scraper/navigator.test.d.ts.map +1 -0
- package/dist/scraper/navigator.test.js +63 -0
- package/dist/scraper/navigator.test.js.map +1 -0
- package/dist/scraper/skoolApi.d.ts +17 -0
- package/dist/scraper/skoolApi.d.ts.map +1 -0
- package/dist/scraper/skoolApi.js +72 -0
- package/dist/scraper/skoolApi.js.map +1 -0
- package/dist/scraper/videoInterceptor.d.ts +19 -0
- package/dist/scraper/videoInterceptor.d.ts.map +1 -0
- package/dist/scraper/videoInterceptor.js +315 -0
- package/dist/scraper/videoInterceptor.js.map +1 -0
- package/dist/shared/auth.d.ts +58 -0
- package/dist/shared/auth.d.ts.map +1 -0
- package/dist/shared/auth.js +211 -0
- package/dist/shared/auth.js.map +1 -0
- package/dist/shared/fs.d.ts +31 -0
- package/dist/shared/fs.d.ts.map +1 -0
- package/dist/shared/fs.js +73 -0
- package/dist/shared/fs.js.map +1 -0
- package/dist/shared/http.d.ts +15 -0
- package/dist/shared/http.d.ts.map +1 -0
- package/dist/shared/http.js +31 -0
- package/dist/shared/http.js.map +1 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/state/database.d.ts +245 -0
- package/dist/state/database.d.ts.map +1 -0
- package/dist/state/database.js +676 -0
- package/dist/state/database.js.map +1 -0
- package/dist/state/database.test.d.ts +2 -0
- package/dist/state/database.test.d.ts.map +1 -0
- package/dist/state/database.test.js +34 -0
- package/dist/state/database.test.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +2 -0
- package/dist/state/index.js.map +1 -0
- package/dist/storage/fileSystem.d.ts +56 -0
- package/dist/storage/fileSystem.d.ts.map +1 -0
- package/dist/storage/fileSystem.js +121 -0
- package/dist/storage/fileSystem.js.map +1 -0
- package/dist/transcription/whisperService.d.ts +27 -0
- package/dist/transcription/whisperService.d.ts.map +1 -0
- package/dist/transcription/whisperService.js +102 -0
- package/dist/transcription/whisperService.js.map +1 -0
- package/eslint.config.js +55 -0
- package/package.json +68 -11
- package/src/__fixtures__/highlevel-post-response.json +68 -0
- package/src/__fixtures__/hls-master-playlist.m3u8 +24 -0
- package/src/cli/commands/__snapshots__/syncHighLevel.test.ts.snap +38 -0
- package/src/cli/commands/config.ts +74 -0
- package/src/cli/commands/inspect.ts +441 -0
- package/src/cli/commands/login.ts +68 -0
- package/src/cli/commands/status.ts +147 -0
- package/src/cli/commands/sync.ts +1235 -0
- package/src/cli/commands/syncHighLevel.test.ts +144 -0
- package/src/cli/commands/syncHighLevel.ts +639 -0
- package/src/cli/index.ts +121 -0
- package/src/config/configManager.ts +75 -0
- package/src/config/paths.test.ts +83 -0
- package/src/config/paths.ts +36 -0
- package/src/config/schema.test.ts +173 -0
- package/src/config/schema.ts +65 -0
- package/src/downloader/hlsDownloader.test.ts +148 -0
- package/src/downloader/hlsDownloader.ts +327 -0
- package/src/downloader/hlsValidator.ts +196 -0
- package/src/downloader/index.ts +122 -0
- package/src/downloader/loomDownloader.test.ts +43 -0
- package/src/downloader/loomDownloader.ts +742 -0
- package/src/downloader/queue.test.ts +199 -0
- package/src/downloader/queue.ts +118 -0
- package/src/downloader/vimeoDownloader.test.ts +62 -0
- package/src/downloader/vimeoDownloader.ts +722 -0
- package/src/scraper/extractor.test.ts +124 -0
- package/src/scraper/extractor.ts +757 -0
- package/src/scraper/highlevel/__snapshots__/extractor.test.ts.snap +41 -0
- package/src/scraper/highlevel/extractor.test.ts +134 -0
- package/src/scraper/highlevel/extractor.ts +537 -0
- package/src/scraper/highlevel/index.ts +2 -0
- package/src/scraper/highlevel/navigator.test.ts +110 -0
- package/src/scraper/highlevel/navigator.ts +668 -0
- package/src/scraper/highlevel/schemas.ts +183 -0
- package/src/scraper/navigator.test.ts +122 -0
- package/src/scraper/navigator.ts +355 -0
- package/src/scraper/schemas.ts +177 -0
- package/src/scraper/videoInterceptor.ts +435 -0
- package/src/shared/auth.test.ts +58 -0
- package/src/shared/auth.ts +251 -0
- package/src/shared/firebase.ts +151 -0
- package/src/shared/fs.ts +80 -0
- package/src/shared/http.ts +34 -0
- package/src/shared/index.ts +6 -0
- package/src/shared/slug.ts +26 -0
- package/src/shared/url.test.ts +122 -0
- package/src/shared/url.ts +57 -0
- package/src/state/database.test.ts +49 -0
- package/src/state/database.ts +919 -0
- package/src/state/index.ts +14 -0
- package/src/storage/fileSystem.test.ts +64 -0
- package/src/storage/fileSystem.ts +175 -0
- package/tsconfig.json +28 -0
- package/vitest.config.ts +29 -0
- package/cli.js +0 -45
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
import TurndownService from "turndown";
|
|
2
|
+
// Initialize Turndown for HTML to Markdown conversion
|
|
3
|
+
const turndown = new TurndownService({
|
|
4
|
+
headingStyle: "atx",
|
|
5
|
+
codeBlockStyle: "fenced",
|
|
6
|
+
bulletListMarker: "-",
|
|
7
|
+
});
|
|
8
|
+
// Custom rule to handle images
|
|
9
|
+
turndown.addRule("images", {
|
|
10
|
+
filter: "img",
|
|
11
|
+
replacement: (_content, node) => {
|
|
12
|
+
const img = node;
|
|
13
|
+
const alt = img.alt || "";
|
|
14
|
+
const src = img.src || "";
|
|
15
|
+
return ``;
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
// Custom rule to preserve links
|
|
19
|
+
turndown.addRule("links", {
|
|
20
|
+
filter: "a",
|
|
21
|
+
replacement: (content, node) => {
|
|
22
|
+
const anchor = node;
|
|
23
|
+
const href = anchor.href || "";
|
|
24
|
+
if (!href || href === content) {
|
|
25
|
+
return content;
|
|
26
|
+
}
|
|
27
|
+
return `[${content}](${href})`;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Checks if there's a video preview/thumbnail that needs to be clicked to load the video.
|
|
32
|
+
*/
|
|
33
|
+
async function tryClickVideoPreview(page) {
|
|
34
|
+
// First, try Skool's specific styled-components pattern for video players
|
|
35
|
+
// These have classes like "styled__VideoPlayerWrapper-sc-xxx" and "styled__PlaybackButton-sc-xxx"
|
|
36
|
+
// Strategy 0: Direct click on Skool's VideoPlayerWrapper or PlaybackButton
|
|
37
|
+
const skoolClicked = await page.evaluate(() => {
|
|
38
|
+
// Find the playback button directly
|
|
39
|
+
const playbackButton = document.querySelector('[class*="PlaybackButton"]');
|
|
40
|
+
if (playbackButton && playbackButton instanceof HTMLElement) {
|
|
41
|
+
const rect = playbackButton.getBoundingClientRect();
|
|
42
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
43
|
+
playbackButton.click();
|
|
44
|
+
return { clicked: true, selector: 'PlaybackButton' };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Or try clicking the video player wrapper
|
|
48
|
+
const videoWrapper = document.querySelector('[class*="VideoPlayerWrapper"]');
|
|
49
|
+
if (videoWrapper && videoWrapper instanceof HTMLElement) {
|
|
50
|
+
const rect = videoWrapper.getBoundingClientRect();
|
|
51
|
+
if (rect.width > 200 && rect.height > 100) {
|
|
52
|
+
videoWrapper.click();
|
|
53
|
+
return { clicked: true, selector: 'VideoPlayerWrapper' };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Try styled-components video patterns
|
|
57
|
+
const styledVideo = document.querySelector('[class*="styled__Video"]');
|
|
58
|
+
if (styledVideo && styledVideo instanceof HTMLElement) {
|
|
59
|
+
styledVideo.click();
|
|
60
|
+
return { clicked: true, selector: 'styled__Video' };
|
|
61
|
+
}
|
|
62
|
+
return { clicked: false };
|
|
63
|
+
});
|
|
64
|
+
if (skoolClicked.clicked) {
|
|
65
|
+
// Wait for iframe to appear after click
|
|
66
|
+
try {
|
|
67
|
+
await page.waitForSelector('iframe[src*="loom.com"], iframe[src*="vimeo"], iframe[src*="youtube"], video', {
|
|
68
|
+
timeout: 5000,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Timeout is fine, we'll check for video anyway
|
|
73
|
+
}
|
|
74
|
+
// Extra wait for iframe content to load
|
|
75
|
+
await page.waitForTimeout(1000);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
// Strategy 1: Look for elements that contain "loom" in href or data attributes
|
|
79
|
+
const loomClicked = await page.evaluate(() => {
|
|
80
|
+
// Find any anchor or element with loom URL
|
|
81
|
+
const loomLink = document.querySelector('a[href*="loom.com"]');
|
|
82
|
+
if (loomLink && loomLink instanceof HTMLElement) {
|
|
83
|
+
// Try to find a parent container that might be the video preview
|
|
84
|
+
let container = loomLink.parentElement;
|
|
85
|
+
for (let i = 0; i < 5 && container; i++) {
|
|
86
|
+
const rect = container.getBoundingClientRect();
|
|
87
|
+
if (rect.width > 300 && rect.height > 150) {
|
|
88
|
+
container.click();
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
container = container.parentElement;
|
|
92
|
+
}
|
|
93
|
+
loomLink.click();
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
// Look for elements with loom-related classes or data
|
|
97
|
+
const loomElements = Array.from(document.querySelectorAll('[class*="loom"], [data-loom], [data-video-provider="loom"]'));
|
|
98
|
+
for (const el of loomElements) {
|
|
99
|
+
if (el instanceof HTMLElement) {
|
|
100
|
+
const rect = el.getBoundingClientRect();
|
|
101
|
+
if (rect.width > 100 && rect.height > 50) {
|
|
102
|
+
el.click();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
});
|
|
109
|
+
if (loomClicked) {
|
|
110
|
+
await page.waitForTimeout(2500);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
// Strategy 2: Look for play button overlays on large elements
|
|
114
|
+
const playClicked = await page.evaluate(() => {
|
|
115
|
+
// Find all elements that look like play buttons (including styled-components patterns)
|
|
116
|
+
const playButtons = Array.from(document.querySelectorAll('[class*="play" i], [class*="Play"], [class*="Playback"], svg[class*="play" i], [aria-label*="play" i]'));
|
|
117
|
+
for (const btn of playButtons) {
|
|
118
|
+
if (btn instanceof HTMLElement || btn instanceof SVGElement) {
|
|
119
|
+
const rect = btn.getBoundingClientRect();
|
|
120
|
+
// Play buttons are usually visible and reasonably sized
|
|
121
|
+
if (rect.width > 20 && rect.height > 20 && rect.top > 0 && rect.left > 0) {
|
|
122
|
+
// Check if this is inside a large container (video preview)
|
|
123
|
+
let parent = btn.parentElement;
|
|
124
|
+
for (let i = 0; i < 5 && parent; i++) {
|
|
125
|
+
const parentRect = parent.getBoundingClientRect();
|
|
126
|
+
if (parentRect.width > 300 && parentRect.height > 150) {
|
|
127
|
+
// This looks like a video container - click the play button
|
|
128
|
+
if (btn instanceof HTMLElement) {
|
|
129
|
+
btn.click();
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// For SVG, try to click the parent
|
|
133
|
+
(parent).click();
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
parent = parent.parentElement;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
});
|
|
144
|
+
if (playClicked) {
|
|
145
|
+
await page.waitForTimeout(2500);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// Strategy 3: Look for large clickable images/thumbnails
|
|
149
|
+
const thumbnailClicked = await page.evaluate(() => {
|
|
150
|
+
// Find large images that might be video thumbnails
|
|
151
|
+
const images = Array.from(document.querySelectorAll('img'));
|
|
152
|
+
for (const img of images) {
|
|
153
|
+
const rect = img.getBoundingClientRect();
|
|
154
|
+
// Video thumbnails are typically 16:9 or similar aspect ratio
|
|
155
|
+
if (rect.width > 400 && rect.height > 200) {
|
|
156
|
+
// Check if image has a sibling or parent with play icon
|
|
157
|
+
const parent = img.parentElement;
|
|
158
|
+
if (parent) {
|
|
159
|
+
const hasPLay = parent.querySelector('[class*="play" i], svg');
|
|
160
|
+
if (hasPLay || parent.className.toLowerCase().includes('video')) {
|
|
161
|
+
(parent).click();
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
});
|
|
169
|
+
if (thumbnailClicked) {
|
|
170
|
+
await page.waitForTimeout(2500);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
// Strategy 4: Common patterns for video preview overlays
|
|
174
|
+
const previewSelectors = [
|
|
175
|
+
// Skool-specific patterns
|
|
176
|
+
'[class*="VideoWrapper"]',
|
|
177
|
+
'[class*="video-wrapper"]',
|
|
178
|
+
'[class*="video-container"]',
|
|
179
|
+
'[class*="VideoContainer"]',
|
|
180
|
+
'[class*="player-wrapper"]',
|
|
181
|
+
'[class*="embed-wrapper"]',
|
|
182
|
+
// Generic patterns
|
|
183
|
+
'[class*="video-preview"]',
|
|
184
|
+
'[class*="VideoPreview"]',
|
|
185
|
+
'[class*="video-thumbnail"]',
|
|
186
|
+
'[class*="poster"]',
|
|
187
|
+
// Data attribute patterns
|
|
188
|
+
'[data-video-id]',
|
|
189
|
+
'[data-video-url]',
|
|
190
|
+
'[data-embed]',
|
|
191
|
+
];
|
|
192
|
+
for (const selector of previewSelectors) {
|
|
193
|
+
try {
|
|
194
|
+
const element = await page.$(selector);
|
|
195
|
+
if (element) {
|
|
196
|
+
const isVisible = await element.isVisible();
|
|
197
|
+
if (isVisible) {
|
|
198
|
+
const box = await element.boundingBox();
|
|
199
|
+
if (box && box.width > 200 && box.height > 100) {
|
|
200
|
+
await element.click();
|
|
201
|
+
await page.waitForTimeout(2500);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Selector not found or not clickable, try next
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Extracts the video URL from the current lesson page.
|
|
215
|
+
* Supports Loom, Vimeo, YouTube, Wistia, and native video.
|
|
216
|
+
*
|
|
217
|
+
* For Vimeo: Prefers iframe src (has auth params) over __NEXT_DATA__ URL.
|
|
218
|
+
* For others: Uses __NEXT_DATA__ first, then falls back to DOM inspection.
|
|
219
|
+
*/
|
|
220
|
+
export async function extractVideoUrl(page) {
|
|
221
|
+
// First: Check for iframe with full URL (includes auth params for Vimeo)
|
|
222
|
+
const iframeVideo = await extractVideoFromIframe(page);
|
|
223
|
+
// If it's Vimeo, prefer iframe URL as it has the auth hash
|
|
224
|
+
if (iframeVideo.url && iframeVideo.type === "vimeo") {
|
|
225
|
+
return iframeVideo;
|
|
226
|
+
}
|
|
227
|
+
// Try extracting from __NEXT_DATA__ (Skool embeds video URLs here)
|
|
228
|
+
const nextDataVideo = await extractVideoFromNextData(page);
|
|
229
|
+
if (nextDataVideo.url) {
|
|
230
|
+
// For Vimeo, check if iframe has additional params we're missing
|
|
231
|
+
if (nextDataVideo.type === "vimeo" && iframeVideo.url) {
|
|
232
|
+
return iframeVideo; // iframe has the full URL with hash
|
|
233
|
+
}
|
|
234
|
+
return nextDataVideo;
|
|
235
|
+
}
|
|
236
|
+
// Check for already loaded video in DOM
|
|
237
|
+
let videoInfo = await findVideoInPage(page);
|
|
238
|
+
// If no video found, try clicking preview to trigger lazy load
|
|
239
|
+
if (!videoInfo.url) {
|
|
240
|
+
const clicked = await tryClickVideoPreview(page);
|
|
241
|
+
if (clicked) {
|
|
242
|
+
// Re-check for video after clicking
|
|
243
|
+
videoInfo = await findVideoInPage(page);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return videoInfo;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Extracts video URL directly from iframe src attribute.
|
|
250
|
+
* This captures the full URL including auth parameters.
|
|
251
|
+
*/
|
|
252
|
+
async function extractVideoFromIframe(page) {
|
|
253
|
+
return page.evaluate(() => {
|
|
254
|
+
// Check for Vimeo iframe (prioritize this for auth params)
|
|
255
|
+
const vimeoIframe = document.querySelector('iframe[src*="vimeo.com"]');
|
|
256
|
+
if (vimeoIframe) {
|
|
257
|
+
const src = vimeoIframe.src;
|
|
258
|
+
if (src) {
|
|
259
|
+
return { url: src, type: "vimeo" };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Check for Loom iframe
|
|
263
|
+
const loomIframe = document.querySelector('iframe[src*="loom.com"]');
|
|
264
|
+
if (loomIframe) {
|
|
265
|
+
const src = loomIframe.src;
|
|
266
|
+
if (src) {
|
|
267
|
+
return { url: src, type: "loom" };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Check for YouTube iframe
|
|
271
|
+
const ytIframe = document.querySelector('iframe[src*="youtube.com"], iframe[src*="youtu.be"]');
|
|
272
|
+
if (ytIframe) {
|
|
273
|
+
const src = ytIframe.src;
|
|
274
|
+
if (src) {
|
|
275
|
+
return { url: src, type: "youtube" };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return { url: null, type: null };
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Extracts video URL from Skool's __NEXT_DATA__ JSON.
|
|
283
|
+
* This is the most reliable method as Skool stores video metadata here.
|
|
284
|
+
*
|
|
285
|
+
* IMPORTANT: __NEXT_DATA__ is only updated on full page loads, not SPA navigation.
|
|
286
|
+
* Our sync uses page.goto() which triggers full loads, so this is fine.
|
|
287
|
+
* We also verify the module ID matches the current URL to detect stale data.
|
|
288
|
+
*/
|
|
289
|
+
async function extractVideoFromNextData(page) {
|
|
290
|
+
const videoInfo = await page.evaluate(() => {
|
|
291
|
+
const nextDataScript = document.querySelector('#__NEXT_DATA__');
|
|
292
|
+
if (!nextDataScript?.textContent) {
|
|
293
|
+
return { url: null, type: null };
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
const data = JSON.parse(nextDataScript.textContent);
|
|
297
|
+
// Get the selected module ID from __NEXT_DATA__
|
|
298
|
+
const selectedModule = data?.props?.pageProps?.selectedModule;
|
|
299
|
+
if (!selectedModule) {
|
|
300
|
+
return { url: null, type: null };
|
|
301
|
+
}
|
|
302
|
+
// Verify this matches the current URL (detect stale __NEXT_DATA__ from SPA navigation)
|
|
303
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
304
|
+
const urlModuleId = urlParams.get('md');
|
|
305
|
+
if (urlModuleId && urlModuleId !== selectedModule) {
|
|
306
|
+
// __NEXT_DATA__ is stale (SPA navigation happened), don't trust it
|
|
307
|
+
return { url: null, type: null };
|
|
308
|
+
}
|
|
309
|
+
// Find the module in the course children
|
|
310
|
+
const courseData = data?.props?.pageProps?.course;
|
|
311
|
+
const children = courseData?.children ?? [];
|
|
312
|
+
for (const child of children) {
|
|
313
|
+
if (child?.course?.id === selectedModule) {
|
|
314
|
+
const metadata = child.course.metadata;
|
|
315
|
+
const videoLink = metadata?.videoLink;
|
|
316
|
+
if (videoLink) {
|
|
317
|
+
// Determine video type from URL
|
|
318
|
+
if (videoLink.includes('loom.com')) {
|
|
319
|
+
// Convert share URL to embed URL if needed
|
|
320
|
+
const embedUrl = videoLink.replace('/share/', '/embed/').split('?')[0];
|
|
321
|
+
return { url: embedUrl, type: "loom" };
|
|
322
|
+
}
|
|
323
|
+
if (videoLink.includes('vimeo.com')) {
|
|
324
|
+
return { url: videoLink, type: "vimeo" };
|
|
325
|
+
}
|
|
326
|
+
if (videoLink.includes('youtube.com') || videoLink.includes('youtu.be')) {
|
|
327
|
+
return { url: videoLink, type: "youtube" };
|
|
328
|
+
}
|
|
329
|
+
if (videoLink.includes('wistia')) {
|
|
330
|
+
return { url: videoLink, type: "wistia" };
|
|
331
|
+
}
|
|
332
|
+
// Unknown but has video link
|
|
333
|
+
return { url: videoLink, type: "unknown" };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// JSON parse failed
|
|
340
|
+
}
|
|
341
|
+
return { url: null, type: null };
|
|
342
|
+
});
|
|
343
|
+
return videoInfo;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Finds video URL in the current page state.
|
|
347
|
+
*/
|
|
348
|
+
async function findVideoInPage(page) {
|
|
349
|
+
const videoInfo = await page.evaluate(() => {
|
|
350
|
+
// Check for Loom iframe (most common on Skool)
|
|
351
|
+
const loomIframe = document.querySelector('iframe[src*="loom.com"]');
|
|
352
|
+
if (loomIframe) {
|
|
353
|
+
return { url: loomIframe.src, type: "loom" };
|
|
354
|
+
}
|
|
355
|
+
// Check for Loom embed link (sometimes used instead of iframe)
|
|
356
|
+
const loomLinks = Array.from(document.querySelectorAll('a[href*="loom.com"]'));
|
|
357
|
+
for (const link of loomLinks) {
|
|
358
|
+
const href = link.href;
|
|
359
|
+
if (href.includes("/share/") || href.includes("/embed/")) {
|
|
360
|
+
// Convert share URL to embed URL
|
|
361
|
+
const embedUrl = href.replace("/share/", "/embed/");
|
|
362
|
+
return { url: embedUrl, type: "loom" };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Check for Loom data attributes
|
|
366
|
+
const loomEmbed = document.querySelector('[data-loom-id]');
|
|
367
|
+
if (loomEmbed) {
|
|
368
|
+
const loomId = loomEmbed.getAttribute("data-loom-id");
|
|
369
|
+
if (loomId) {
|
|
370
|
+
return { url: `https://www.loom.com/embed/${loomId}`, type: "loom" };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Check for Loom URLs in any element's attributes or content
|
|
374
|
+
const allElements = Array.from(document.querySelectorAll('*'));
|
|
375
|
+
for (const el of allElements) {
|
|
376
|
+
// Check data attributes
|
|
377
|
+
const attrs = Array.from(el.attributes);
|
|
378
|
+
for (const attr of attrs) {
|
|
379
|
+
if (attr.value.includes('loom.com/share/') || attr.value.includes('loom.com/embed/')) {
|
|
380
|
+
const match = /loom\.com\/(share|embed)\/([a-f0-9]+)/.exec(attr.value);
|
|
381
|
+
if (match?.[2]) {
|
|
382
|
+
return { url: `https://www.loom.com/embed/${match[2]}`, type: "loom" };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Check for Vimeo iframe
|
|
388
|
+
const vimeoIframe = document.querySelector('iframe[src*="vimeo.com"]');
|
|
389
|
+
if (vimeoIframe) {
|
|
390
|
+
return { url: vimeoIframe.src, type: "vimeo" };
|
|
391
|
+
}
|
|
392
|
+
// Check for YouTube iframe
|
|
393
|
+
const youtubeIframe = document.querySelector('iframe[src*="youtube.com"], iframe[src*="youtu.be"]');
|
|
394
|
+
if (youtubeIframe) {
|
|
395
|
+
return { url: youtubeIframe.src, type: "youtube" };
|
|
396
|
+
}
|
|
397
|
+
// Check for Wistia
|
|
398
|
+
const wistiaVideo = document.querySelector('[class*="wistia"]');
|
|
399
|
+
if (wistiaVideo) {
|
|
400
|
+
const wistiaId = /wistia_embed wistia_async_(\w+)/.exec(wistiaVideo.className);
|
|
401
|
+
if (wistiaId?.[1]) {
|
|
402
|
+
return {
|
|
403
|
+
url: `https://fast.wistia.net/embed/medias/${wistiaId[1]}`,
|
|
404
|
+
type: "wistia",
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Check for HTML5 video
|
|
409
|
+
const videoElement = document.querySelector("video");
|
|
410
|
+
if (videoElement) {
|
|
411
|
+
const source = videoElement.querySelector("source");
|
|
412
|
+
const src = source?.src ?? videoElement.src;
|
|
413
|
+
if (src) {
|
|
414
|
+
return { url: src, type: "native" };
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Check for any iframe that might be a video player
|
|
418
|
+
const iframes = Array.from(document.querySelectorAll("iframe"));
|
|
419
|
+
for (const iframe of iframes) {
|
|
420
|
+
const src = iframe.src;
|
|
421
|
+
if (src &&
|
|
422
|
+
!src.includes("stripe.com") &&
|
|
423
|
+
(src.includes("embed") || src.includes("player") || src.includes("video"))) {
|
|
424
|
+
return { url: src, type: "unknown" };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Last resort: search page HTML for loom URLs
|
|
428
|
+
const pageHtml = document.documentElement.outerHTML;
|
|
429
|
+
const loomMatch = /loom\.com\/(share|embed)\/([a-f0-9]{32})/.exec(pageHtml);
|
|
430
|
+
if (loomMatch?.[2]) {
|
|
431
|
+
return { url: `https://www.loom.com/embed/${loomMatch[2]}`, type: "loom" };
|
|
432
|
+
}
|
|
433
|
+
// Try to find explicit Loom video IDs in script tags
|
|
434
|
+
const scripts = Array.from(document.querySelectorAll('script'));
|
|
435
|
+
for (const script of scripts) {
|
|
436
|
+
const content = script.textContent ?? '';
|
|
437
|
+
// Look for explicit loom URL patterns in scripts
|
|
438
|
+
const loomUrlMatch = /loom\.com\/(share|embed)\/([a-f0-9]{32})/.exec(content);
|
|
439
|
+
if (loomUrlMatch?.[2]) {
|
|
440
|
+
return { url: `https://www.loom.com/embed/${loomUrlMatch[2]}`, type: "loom" };
|
|
441
|
+
}
|
|
442
|
+
// Look for loom video ID near "loom" keyword
|
|
443
|
+
const loomContextMatch = /["']loom["'][^}]*["']([a-f0-9]{32})["']/i.exec(content);
|
|
444
|
+
if (loomContextMatch?.[1]) {
|
|
445
|
+
return { url: `https://www.loom.com/embed/${loomContextMatch[1]}`, type: "loom" };
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return { url: null, type: null };
|
|
449
|
+
});
|
|
450
|
+
return videoInfo;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Extracts the text content from the lesson page.
|
|
454
|
+
*/
|
|
455
|
+
export async function extractTextContent(page) {
|
|
456
|
+
const html = await page.evaluate(() => {
|
|
457
|
+
// Skool lesson content is typically in a styled div below the video
|
|
458
|
+
// Look for common content container patterns
|
|
459
|
+
const contentSelectors = [
|
|
460
|
+
'[class*="LessonContent"]',
|
|
461
|
+
'[class*="PostContent"]',
|
|
462
|
+
'[class*="ContentWrapper"]',
|
|
463
|
+
'[class*="content-body"]',
|
|
464
|
+
"article",
|
|
465
|
+
'[class*="prose"]',
|
|
466
|
+
];
|
|
467
|
+
for (const selector of contentSelectors) {
|
|
468
|
+
const element = document.querySelector(selector);
|
|
469
|
+
if (element && element.textContent && element.textContent.trim().length > 50) {
|
|
470
|
+
// Clone to avoid modifying the actual page
|
|
471
|
+
const clone = element.cloneNode(true);
|
|
472
|
+
// Remove unwanted elements
|
|
473
|
+
const unwanted = clone.querySelectorAll("script, style, nav, [class*='video'], [class*='Video'], iframe, [class*='player'], [class*='Player']");
|
|
474
|
+
unwanted.forEach((el) => { el.remove(); });
|
|
475
|
+
return clone.innerHTML;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Fallback: Try to find the main content area by structure
|
|
479
|
+
// Skool typically has: Header -> Video -> Content
|
|
480
|
+
const mainContent = document.querySelector("main, [class*='Main']");
|
|
481
|
+
if (mainContent) {
|
|
482
|
+
const clone = mainContent.cloneNode(true);
|
|
483
|
+
// Remove video player and navigation
|
|
484
|
+
const unwanted = clone.querySelectorAll("script, style, nav, header, [class*='video'], [class*='Video'], iframe, [class*='Sidebar'], [class*='sidebar']");
|
|
485
|
+
unwanted.forEach((el) => { el.remove(); });
|
|
486
|
+
// Get remaining text content
|
|
487
|
+
const textContent = clone.innerHTML;
|
|
488
|
+
if (textContent.trim().length > 100) {
|
|
489
|
+
return textContent;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return "";
|
|
493
|
+
});
|
|
494
|
+
const markdown = html ? turndown.turndown(html) : "";
|
|
495
|
+
return { html, markdown };
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* File extensions we want to download.
|
|
499
|
+
*/
|
|
500
|
+
const DOWNLOADABLE_EXTENSIONS = [
|
|
501
|
+
"pdf",
|
|
502
|
+
"doc", "docx",
|
|
503
|
+
"xls", "xlsx",
|
|
504
|
+
"ppt", "pptx",
|
|
505
|
+
"zip", "rar", "7z",
|
|
506
|
+
"txt", "csv",
|
|
507
|
+
"epub", "mobi",
|
|
508
|
+
];
|
|
509
|
+
/**
|
|
510
|
+
* Gets the file type from extension.
|
|
511
|
+
*/
|
|
512
|
+
function getFileType(ext) {
|
|
513
|
+
const lowerExt = ext.toLowerCase();
|
|
514
|
+
if (lowerExt === "pdf")
|
|
515
|
+
return "pdf";
|
|
516
|
+
if (lowerExt === "doc")
|
|
517
|
+
return "doc";
|
|
518
|
+
if (lowerExt === "docx")
|
|
519
|
+
return "docx";
|
|
520
|
+
if (lowerExt === "xls")
|
|
521
|
+
return "xls";
|
|
522
|
+
if (lowerExt === "xlsx")
|
|
523
|
+
return "xlsx";
|
|
524
|
+
if (lowerExt === "ppt")
|
|
525
|
+
return "ppt";
|
|
526
|
+
if (lowerExt === "pptx")
|
|
527
|
+
return "pptx";
|
|
528
|
+
if (["zip", "rar", "7z"].includes(lowerExt))
|
|
529
|
+
return "zip";
|
|
530
|
+
return "other";
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Extracts downloadable file links from the page content.
|
|
534
|
+
*/
|
|
535
|
+
export async function extractDownloadableFiles(page) {
|
|
536
|
+
const files = await page.evaluate((extensions) => {
|
|
537
|
+
const results = [];
|
|
538
|
+
const seen = new Set();
|
|
539
|
+
// Find all links in the page
|
|
540
|
+
const links = document.querySelectorAll("a[href]");
|
|
541
|
+
for (const link of Array.from(links)) {
|
|
542
|
+
const href = link.href;
|
|
543
|
+
if (!href || seen.has(href))
|
|
544
|
+
continue;
|
|
545
|
+
// Check if the URL ends with a downloadable extension
|
|
546
|
+
const urlWithoutQuery = href.split("?")[0] ?? href;
|
|
547
|
+
const ext = urlWithoutQuery.split(".").pop()?.toLowerCase() ?? "";
|
|
548
|
+
if (extensions.includes(ext)) {
|
|
549
|
+
seen.add(href);
|
|
550
|
+
// Try to get filename from link text, download attribute, or URL
|
|
551
|
+
let filename = link.download;
|
|
552
|
+
if (!filename) {
|
|
553
|
+
filename = link.textContent?.trim() ?? "";
|
|
554
|
+
}
|
|
555
|
+
if (!filename || filename.length > 100) {
|
|
556
|
+
// Extract from URL
|
|
557
|
+
const urlParts = urlWithoutQuery.split("/");
|
|
558
|
+
filename = urlParts[urlParts.length - 1] ?? `file.${ext}`;
|
|
559
|
+
}
|
|
560
|
+
// Ensure filename has the correct extension
|
|
561
|
+
if (!filename.toLowerCase().endsWith(`.${ext}`)) {
|
|
562
|
+
filename = `${filename}.${ext}`;
|
|
563
|
+
}
|
|
564
|
+
// Sanitize filename
|
|
565
|
+
filename = filename.replace(/[<>:"/\\|?*]/g, "_").trim();
|
|
566
|
+
results.push({ url: href, filename, ext });
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return results;
|
|
570
|
+
}, DOWNLOADABLE_EXTENSIONS);
|
|
571
|
+
return files.map((f) => ({
|
|
572
|
+
url: f.url,
|
|
573
|
+
filename: f.filename,
|
|
574
|
+
type: getFileType(f.ext),
|
|
575
|
+
}));
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Extracts all content from a lesson page.
|
|
579
|
+
*/
|
|
580
|
+
export async function extractLessonContent(page, lessonUrl) {
|
|
581
|
+
const currentUrl = page.url();
|
|
582
|
+
if (currentUrl !== lessonUrl) {
|
|
583
|
+
await page.goto(lessonUrl, { timeout: 30000 });
|
|
584
|
+
await page.waitForLoadState("domcontentloaded");
|
|
585
|
+
await page.waitForTimeout(2000);
|
|
586
|
+
}
|
|
587
|
+
const title = await page.title();
|
|
588
|
+
const { url: videoUrl, type: videoType } = await extractVideoUrl(page);
|
|
589
|
+
const { html: htmlContent, markdown: markdownContent } = await extractTextContent(page);
|
|
590
|
+
const downloadableFiles = await extractDownloadableFiles(page);
|
|
591
|
+
// Clean up title: "1. Lesson Name - Module Name · Course Name" -> "1. Lesson Name"
|
|
592
|
+
const cleanTitle = title.split(" - ")[0]?.trim() ?? title;
|
|
593
|
+
return {
|
|
594
|
+
title: cleanTitle,
|
|
595
|
+
videoUrl,
|
|
596
|
+
videoType,
|
|
597
|
+
htmlContent,
|
|
598
|
+
markdownContent,
|
|
599
|
+
downloadableFiles,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Extracts the Loom video ID from an embed URL.
|
|
604
|
+
*/
|
|
605
|
+
export function extractLoomVideoId(embedUrl) {
|
|
606
|
+
const match = /loom\.com\/embed\/([a-f0-9]+)/.exec(embedUrl);
|
|
607
|
+
return match?.[1] ?? null;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Cleans and formats the markdown content.
|
|
611
|
+
*/
|
|
612
|
+
export function formatMarkdown(title, content, videoUrl, videoType) {
|
|
613
|
+
const lines = [`# ${title}`, ""];
|
|
614
|
+
if (videoUrl) {
|
|
615
|
+
const videoLabel = videoType ? `${videoType.charAt(0).toUpperCase()}${videoType.slice(1)}` : "Video";
|
|
616
|
+
lines.push(`> 📺 ${videoLabel}: ${videoUrl}`, "");
|
|
617
|
+
}
|
|
618
|
+
if (content.trim()) {
|
|
619
|
+
lines.push(content);
|
|
620
|
+
}
|
|
621
|
+
// Clean up excessive newlines
|
|
622
|
+
return lines
|
|
623
|
+
.join("\n")
|
|
624
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
625
|
+
.trim();
|
|
626
|
+
}
|
|
627
|
+
//# sourceMappingURL=extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../src/scraper/extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.test.d.ts","sourceRoot":"","sources":["../../src/scraper/extractor.test.ts"],"names":[],"mappings":""}
|