offcourse 0.0.1 → 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 +256 -16
- 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,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Captures Vimeo video URL by extracting it from the running player.
|
|
3
|
+
* The key insight: the video is ALREADY playing in the iframe - we just need to get the URL.
|
|
4
|
+
*/
|
|
5
|
+
export async function captureVimeoConfig(page, _videoId, timeoutMs = 20000) {
|
|
6
|
+
try {
|
|
7
|
+
// Step 1: Make sure we have a Vimeo iframe or video wrapper
|
|
8
|
+
// Skool wraps videos in a VideoPlayerWrapper - click it to ensure video loads
|
|
9
|
+
const videoWrapper = await page.$('[class*="VideoPlayerWrapper"], [class*="video-wrapper"], [class*="VideoPlayer"]');
|
|
10
|
+
if (videoWrapper) {
|
|
11
|
+
await videoWrapper.click().catch(() => { });
|
|
12
|
+
await page.waitForTimeout(1000);
|
|
13
|
+
}
|
|
14
|
+
// Step 2: Wait for Vimeo iframe to appear
|
|
15
|
+
let vimeoFrame = null;
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
while (!vimeoFrame && Date.now() - startTime < timeoutMs) {
|
|
18
|
+
// Try to find the iframe
|
|
19
|
+
const iframe = await page.$('iframe[src*="vimeo.com"], iframe[src*="player.vimeo"]');
|
|
20
|
+
if (iframe) {
|
|
21
|
+
vimeoFrame = await iframe.contentFrame();
|
|
22
|
+
if (vimeoFrame)
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
await page.waitForTimeout(500);
|
|
26
|
+
}
|
|
27
|
+
if (!vimeoFrame) {
|
|
28
|
+
return { hlsUrl: null, progressiveUrl: null, error: "Vimeo iframe not found after waiting" };
|
|
29
|
+
}
|
|
30
|
+
// Step 3: Mute the video before playing (we don't want audio!)
|
|
31
|
+
await vimeoFrame.evaluate(() => {
|
|
32
|
+
const video = document.querySelector('video');
|
|
33
|
+
if (video) {
|
|
34
|
+
video.muted = true;
|
|
35
|
+
video.volume = 0;
|
|
36
|
+
}
|
|
37
|
+
}).catch(() => { });
|
|
38
|
+
// Step 4: Click play button in the iframe to start video
|
|
39
|
+
try {
|
|
40
|
+
// Multiple selectors for Vimeo's play button
|
|
41
|
+
await vimeoFrame.click('.vp-controls button, .play-icon, [aria-label="Play"], .vp-big-play-button, button', {
|
|
42
|
+
timeout: 2000
|
|
43
|
+
}).catch(() => { });
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Video might auto-play
|
|
47
|
+
}
|
|
48
|
+
// Ensure video stays muted
|
|
49
|
+
await vimeoFrame.evaluate(() => {
|
|
50
|
+
const video = document.querySelector('video');
|
|
51
|
+
if (video) {
|
|
52
|
+
video.muted = true;
|
|
53
|
+
video.volume = 0;
|
|
54
|
+
}
|
|
55
|
+
}).catch(() => { });
|
|
56
|
+
// Step 4: Wait for video to actually start playing and get the URL
|
|
57
|
+
let hlsUrl = null;
|
|
58
|
+
let progressiveUrl = null;
|
|
59
|
+
const extractionStart = Date.now();
|
|
60
|
+
while (!hlsUrl && !progressiveUrl && Date.now() - extractionStart < timeoutMs - 5000) {
|
|
61
|
+
const urls = await vimeoFrame.evaluate(() => {
|
|
62
|
+
const result = {
|
|
63
|
+
hlsUrl: null,
|
|
64
|
+
progressiveUrl: null,
|
|
65
|
+
debug: []
|
|
66
|
+
};
|
|
67
|
+
// Method 1: Get URL directly from video element
|
|
68
|
+
const video = document.querySelector('video');
|
|
69
|
+
if (video) {
|
|
70
|
+
result.debug.push(`Video element found, src length: ${video.src?.length ?? 0}`);
|
|
71
|
+
// Check currentSrc (what's actually playing)
|
|
72
|
+
if (video.currentSrc) {
|
|
73
|
+
result.debug.push(`currentSrc: ${video.currentSrc.substring(0, 80)}`);
|
|
74
|
+
if (video.currentSrc.includes('.m3u8')) {
|
|
75
|
+
result.hlsUrl = video.currentSrc;
|
|
76
|
+
}
|
|
77
|
+
else if (video.currentSrc.includes('.mp4')) {
|
|
78
|
+
result.progressiveUrl = video.currentSrc;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Also check src attribute
|
|
82
|
+
if (!result.hlsUrl && !result.progressiveUrl && video.src) {
|
|
83
|
+
if (video.src.includes('.m3u8')) {
|
|
84
|
+
result.hlsUrl = video.src;
|
|
85
|
+
}
|
|
86
|
+
else if (video.src.includes('.mp4')) {
|
|
87
|
+
result.progressiveUrl = video.src;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Method 2: Check source elements
|
|
92
|
+
if (!result.hlsUrl && !result.progressiveUrl) {
|
|
93
|
+
const sources = document.querySelectorAll('video source');
|
|
94
|
+
result.debug.push(`Found ${sources.length} source elements`);
|
|
95
|
+
for (const source of Array.from(sources)) {
|
|
96
|
+
const src = source.src;
|
|
97
|
+
if (src?.includes('.m3u8')) {
|
|
98
|
+
result.hlsUrl = src;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
else if (src?.includes('.mp4') && !result.progressiveUrl) {
|
|
102
|
+
result.progressiveUrl = src;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Method 3: Extract from Vimeo's internal player state
|
|
107
|
+
if (!result.hlsUrl && !result.progressiveUrl) {
|
|
108
|
+
try {
|
|
109
|
+
const win = window;
|
|
110
|
+
// Try various Vimeo internal variables
|
|
111
|
+
const configPaths = [
|
|
112
|
+
win.playerConfig?.request?.files,
|
|
113
|
+
win.vimeo?.config?.request?.files,
|
|
114
|
+
win.__vimeo_player__?.config?.request?.files,
|
|
115
|
+
];
|
|
116
|
+
for (const files of configPaths) {
|
|
117
|
+
if (!files)
|
|
118
|
+
continue;
|
|
119
|
+
// HLS
|
|
120
|
+
if (files.hls?.cdns) {
|
|
121
|
+
const cdns = files.hls.cdns;
|
|
122
|
+
for (const cdn of Object.keys(cdns)) {
|
|
123
|
+
if (cdns[cdn]?.url) {
|
|
124
|
+
result.hlsUrl = cdns[cdn].url;
|
|
125
|
+
result.debug.push(`Found HLS in playerConfig.${cdn}`);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Progressive MP4
|
|
131
|
+
if (!result.progressiveUrl && files.progressive?.length > 0) {
|
|
132
|
+
const sorted = [...files.progressive].sort((a, b) => (b.height ?? 0) - (a.height ?? 0));
|
|
133
|
+
result.progressiveUrl = sorted[0]?.url ?? null;
|
|
134
|
+
if (result.progressiveUrl) {
|
|
135
|
+
result.debug.push('Found progressive in playerConfig');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (result.hlsUrl || result.progressiveUrl)
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
result.debug.push(`Config extraction error: ${e}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Method 4: Network request URLs might be in DOM attributes
|
|
147
|
+
if (!result.hlsUrl && !result.progressiveUrl) {
|
|
148
|
+
const allElements = document.querySelectorAll('*');
|
|
149
|
+
for (const el of Array.from(allElements)) {
|
|
150
|
+
for (const attr of Array.from(el.attributes)) {
|
|
151
|
+
if (attr.value.includes('vimeocdn.com') && attr.value.includes('.m3u8')) {
|
|
152
|
+
result.hlsUrl = (/https:\/\/[^\s"']+\.m3u8[^\s"']*/.exec(attr.value))?.[0] ?? null;
|
|
153
|
+
if (result.hlsUrl) {
|
|
154
|
+
result.debug.push('Found HLS in element attribute');
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (result.hlsUrl)
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
});
|
|
165
|
+
hlsUrl = urls.hlsUrl;
|
|
166
|
+
progressiveUrl = urls.progressiveUrl;
|
|
167
|
+
if (!hlsUrl && !progressiveUrl) {
|
|
168
|
+
// Wait and try again
|
|
169
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (hlsUrl || progressiveUrl) {
|
|
173
|
+
return { hlsUrl, progressiveUrl };
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
hlsUrl: null,
|
|
177
|
+
progressiveUrl: null,
|
|
178
|
+
error: "Could not extract video URL from Vimeo player"
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
hlsUrl: null,
|
|
184
|
+
progressiveUrl: null,
|
|
185
|
+
error: `Vimeo extraction failed: ${error}`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Captures Loom HLS URL by navigating directly to the embed page.
|
|
191
|
+
* This works better than CDP because we can intercept all requests on that page.
|
|
192
|
+
*/
|
|
193
|
+
export async function captureLoomHls(page, videoId, timeoutMs = 15000) {
|
|
194
|
+
let capturedUrl = null;
|
|
195
|
+
const originalUrl = page.url();
|
|
196
|
+
try {
|
|
197
|
+
// Use CDP to intercept network responses
|
|
198
|
+
const client = await page.context().newCDPSession(page);
|
|
199
|
+
await client.send('Network.enable');
|
|
200
|
+
// Match HLS playlists from Loom's CDN
|
|
201
|
+
// Prefer master playlist (playlist.m3u8) over media playlists (mediaplaylist-*.m3u8)
|
|
202
|
+
const masterPattern = /luna\.loom\.com.*\/playlist\.m3u8/;
|
|
203
|
+
const anyHlsPattern = /luna\.loom\.com.*\.m3u8/;
|
|
204
|
+
// Set up listener before navigation
|
|
205
|
+
const responsePromise = new Promise((resolve) => {
|
|
206
|
+
const timeout = setTimeout(() => { resolve(); }, timeoutMs);
|
|
207
|
+
let hasMasterPlaylist = false;
|
|
208
|
+
client.on('Network.responseReceived', (event) => {
|
|
209
|
+
const url = event.response.url;
|
|
210
|
+
// Always prefer master playlist
|
|
211
|
+
if (masterPattern.test(url)) {
|
|
212
|
+
capturedUrl = url;
|
|
213
|
+
hasMasterPlaylist = true;
|
|
214
|
+
clearTimeout(timeout);
|
|
215
|
+
resolve();
|
|
216
|
+
}
|
|
217
|
+
else if (!hasMasterPlaylist && anyHlsPattern.test(url)) {
|
|
218
|
+
// Capture any HLS as fallback, but keep listening for master
|
|
219
|
+
capturedUrl = url;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
// Navigate directly to Loom embed with autoplay (muted)
|
|
224
|
+
const embedUrl = `https://www.loom.com/embed/${videoId}?autoplay=1`;
|
|
225
|
+
await page.goto(embedUrl, { waitUntil: 'domcontentloaded', timeout: 10000 });
|
|
226
|
+
// Mute the video immediately
|
|
227
|
+
await page.evaluate(() => {
|
|
228
|
+
const video = document.querySelector('video');
|
|
229
|
+
if (video) {
|
|
230
|
+
video.muted = true;
|
|
231
|
+
video.volume = 0;
|
|
232
|
+
}
|
|
233
|
+
}).catch(() => { });
|
|
234
|
+
// Try to click play button if video doesn't autoplay
|
|
235
|
+
try {
|
|
236
|
+
await page.waitForTimeout(2000);
|
|
237
|
+
const playButton = await page.$('[data-testid="play-button"], .PlayButton, [aria-label="Play"], button[class*="play"]');
|
|
238
|
+
if (playButton) {
|
|
239
|
+
// Mute again before clicking play
|
|
240
|
+
await page.evaluate(() => {
|
|
241
|
+
const video = document.querySelector('video');
|
|
242
|
+
if (video) {
|
|
243
|
+
video.muted = true;
|
|
244
|
+
video.volume = 0;
|
|
245
|
+
}
|
|
246
|
+
}).catch(() => { });
|
|
247
|
+
await playButton.click();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// No play button or click failed
|
|
252
|
+
}
|
|
253
|
+
// Ensure video stays muted after play
|
|
254
|
+
await page.evaluate(() => {
|
|
255
|
+
const video = document.querySelector('video');
|
|
256
|
+
if (video) {
|
|
257
|
+
video.muted = true;
|
|
258
|
+
video.volume = 0;
|
|
259
|
+
}
|
|
260
|
+
}).catch(() => { });
|
|
261
|
+
// Wait for HLS to be captured
|
|
262
|
+
await responsePromise;
|
|
263
|
+
// Also try to extract from page JS if not found via network
|
|
264
|
+
if (!capturedUrl) {
|
|
265
|
+
const jsUrl = await page.evaluate(() => {
|
|
266
|
+
const win = window;
|
|
267
|
+
// Check __LOOM_SSR_STATE__
|
|
268
|
+
if (win.__LOOM_SSR_STATE__?.video?.asset_urls?.hls_url) {
|
|
269
|
+
return win.__LOOM_SSR_STATE__.video.asset_urls.hls_url;
|
|
270
|
+
}
|
|
271
|
+
// Check for Next.js data
|
|
272
|
+
const nextData = document.getElementById('__NEXT_DATA__');
|
|
273
|
+
if (nextData?.textContent) {
|
|
274
|
+
try {
|
|
275
|
+
const data = JSON.parse(nextData.textContent);
|
|
276
|
+
const hlsUrl = data?.props?.pageProps?.video?.asset_urls?.hls_url;
|
|
277
|
+
if (hlsUrl)
|
|
278
|
+
return hlsUrl;
|
|
279
|
+
// Try regex match in full data
|
|
280
|
+
const videoData = /hls_url['":\s]+['"]([^'"]+)['"]/.exec(JSON.stringify(data));
|
|
281
|
+
if (videoData?.[1])
|
|
282
|
+
return videoData[1];
|
|
283
|
+
}
|
|
284
|
+
catch { /* ignore parse errors */ }
|
|
285
|
+
}
|
|
286
|
+
// Scan scripts for HLS URL
|
|
287
|
+
const scripts = Array.from(document.querySelectorAll('script'));
|
|
288
|
+
for (const script of scripts) {
|
|
289
|
+
const match = /https:\/\/luna\.loom\.com[^"'\s]+\.m3u8[^"'\s]*/.exec(script.textContent);
|
|
290
|
+
if (match)
|
|
291
|
+
return match[0];
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
});
|
|
295
|
+
if (jsUrl) {
|
|
296
|
+
capturedUrl = jsUrl;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
await client.detach();
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// Error during capture
|
|
303
|
+
}
|
|
304
|
+
// Navigate back to original page
|
|
305
|
+
try {
|
|
306
|
+
await page.goto(originalUrl, { waitUntil: 'domcontentloaded', timeout: 10000 });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
// Failed to navigate back
|
|
310
|
+
}
|
|
311
|
+
return capturedUrl
|
|
312
|
+
? { hlsUrl: capturedUrl }
|
|
313
|
+
: { hlsUrl: null, error: "HLS URL not captured" };
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=videoInterceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoInterceptor.js","sourceRoot":"","sources":["../../src/scraper/videoInterceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Browser, BrowserContext, Page } from "playwright";
|
|
2
|
+
export interface AuthSession {
|
|
3
|
+
context: BrowserContext;
|
|
4
|
+
page: Page;
|
|
5
|
+
}
|
|
6
|
+
export interface AuthConfig {
|
|
7
|
+
/** Domain to store session under */
|
|
8
|
+
domain: string;
|
|
9
|
+
/** URL to navigate to for login */
|
|
10
|
+
loginUrl: string;
|
|
11
|
+
/** Function to check if current URL is a login page */
|
|
12
|
+
isLoginPage: (url: string) => boolean;
|
|
13
|
+
/** Optional: Function to verify session is valid after navigation */
|
|
14
|
+
verifySession?: (page: Page) => Promise<boolean>;
|
|
15
|
+
/** Login timeout in ms (default: 5 minutes) */
|
|
16
|
+
loginTimeout?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a login page checker from patterns.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createLoginChecker(patterns?: RegExp[]): (url: string) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Checks if a valid session exists for the given domain.
|
|
24
|
+
*/
|
|
25
|
+
export declare function hasValidSession(domain: string): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Performs interactive login by opening a browser window.
|
|
28
|
+
* The user logs in manually, and we capture the session.
|
|
29
|
+
*/
|
|
30
|
+
export declare function performInteractiveLogin(config: AuthConfig): Promise<AuthSession>;
|
|
31
|
+
/**
|
|
32
|
+
* Gets an authenticated session, either from cache or via interactive login.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getAuthenticatedSession(config: AuthConfig, options?: {
|
|
35
|
+
forceLogin?: boolean;
|
|
36
|
+
headless?: boolean;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
browser: Browser;
|
|
39
|
+
session: AuthSession;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Clears the session for a domain.
|
|
43
|
+
*/
|
|
44
|
+
export declare function clearSession(domain: string): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Checks if the page has a valid Firebase auth token.
|
|
47
|
+
* Used by HighLevel/GoHighLevel portals.
|
|
48
|
+
*/
|
|
49
|
+
export declare function hasValidFirebaseToken(page: Page): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Skool-specific login page checker.
|
|
52
|
+
*/
|
|
53
|
+
export declare const isSkoolLoginPage: (url: string) => boolean;
|
|
54
|
+
/**
|
|
55
|
+
* HighLevel-specific login page checker.
|
|
56
|
+
*/
|
|
57
|
+
export declare const isHighLevelLoginPage: (url: string) => boolean;
|
|
58
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/shared/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAKhE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,qEAAqE;IACrE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAcD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,GAAE,MAAM,EAA2B,GAC1C,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAE1B;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE;AAyBD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAoDtF;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC,CAwErD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGnE;AAMD;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBxE;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,QApNpB,MAAM,KAAK,OAoNoE,CAAC;AAEzF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAzNxB,MAAM,KAAK,OAgOlB,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { chromium } from "playwright";
|
|
2
|
+
import { getSessionPath, SESSIONS_DIR } from "../config/paths.js";
|
|
3
|
+
import { ensureDir, outputJson, pathExists, readJson, removeFile } from "./fs.js";
|
|
4
|
+
/**
|
|
5
|
+
* Default login page detection patterns.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_LOGIN_PATTERNS = [
|
|
8
|
+
/\/login/,
|
|
9
|
+
/\/signin/,
|
|
10
|
+
/\/auth/,
|
|
11
|
+
/accounts\.google\.com/,
|
|
12
|
+
/firebaseapp\.com/,
|
|
13
|
+
/sso\./,
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Creates a login page checker from patterns.
|
|
17
|
+
*/
|
|
18
|
+
export function createLoginChecker(patterns = DEFAULT_LOGIN_PATTERNS) {
|
|
19
|
+
return (url) => patterns.some((p) => p.test(url));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a valid session exists for the given domain.
|
|
23
|
+
*/
|
|
24
|
+
export async function hasValidSession(domain) {
|
|
25
|
+
const sessionPath = getSessionPath(domain);
|
|
26
|
+
return pathExists(sessionPath);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Loads an existing session from disk.
|
|
30
|
+
*/
|
|
31
|
+
async function loadSession(browser, domain) {
|
|
32
|
+
const sessionPath = getSessionPath(domain);
|
|
33
|
+
const storageState = await readJson(sessionPath);
|
|
34
|
+
if (!storageState) {
|
|
35
|
+
throw new Error("Session file not found or invalid");
|
|
36
|
+
}
|
|
37
|
+
// Cast to any since Playwright's storageState type is complex
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
return browser.newContext({ storageState: storageState });
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Saves the current session to disk.
|
|
43
|
+
*/
|
|
44
|
+
async function saveSession(context, domain) {
|
|
45
|
+
const sessionPath = getSessionPath(domain);
|
|
46
|
+
const storageState = await context.storageState();
|
|
47
|
+
await outputJson(sessionPath, storageState);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Performs interactive login by opening a browser window.
|
|
51
|
+
* The user logs in manually, and we capture the session.
|
|
52
|
+
*/
|
|
53
|
+
export async function performInteractiveLogin(config) {
|
|
54
|
+
await ensureDir(SESSIONS_DIR);
|
|
55
|
+
const browser = await chromium.launch({
|
|
56
|
+
headless: false, // Must be visible for user interaction
|
|
57
|
+
});
|
|
58
|
+
const context = await browser.newContext({
|
|
59
|
+
viewport: { width: 1280, height: 800 },
|
|
60
|
+
});
|
|
61
|
+
const page = await context.newPage();
|
|
62
|
+
await page.goto(config.loginUrl);
|
|
63
|
+
console.log("\n🔐 Browser opened. Please log in manually.");
|
|
64
|
+
console.log(" The window will close automatically after successful login.\n");
|
|
65
|
+
const timeout = config.loginTimeout ?? 300000; // 5 minutes default
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
let loggedIn = false;
|
|
68
|
+
while (!loggedIn && Date.now() - startTime < timeout) {
|
|
69
|
+
await page.waitForTimeout(1000);
|
|
70
|
+
const currentUrl = page.url();
|
|
71
|
+
// Check if we're no longer on a login page
|
|
72
|
+
if (!config.isLoginPage(currentUrl)) {
|
|
73
|
+
// Optionally verify with custom function
|
|
74
|
+
if (config.verifySession) {
|
|
75
|
+
loggedIn = await config.verifySession(page);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
loggedIn = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!loggedIn) {
|
|
83
|
+
await browser.close();
|
|
84
|
+
throw new Error(`Login timed out after ${timeout / 1000} seconds`);
|
|
85
|
+
}
|
|
86
|
+
// Give the page a moment to fully load after login
|
|
87
|
+
await page.waitForLoadState("networkidle").catch(() => { });
|
|
88
|
+
await page.waitForTimeout(1000);
|
|
89
|
+
// Save the session
|
|
90
|
+
await saveSession(context, config.domain);
|
|
91
|
+
console.log("✅ Login successful! Session saved.\n");
|
|
92
|
+
return { context, page };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets an authenticated session, either from cache or via interactive login.
|
|
96
|
+
*/
|
|
97
|
+
export async function getAuthenticatedSession(config, options = {}) {
|
|
98
|
+
const useHeadless = options.headless !== false;
|
|
99
|
+
const browser = await chromium.launch({
|
|
100
|
+
headless: useHeadless,
|
|
101
|
+
});
|
|
102
|
+
// Try to use existing session
|
|
103
|
+
if (!options.forceLogin && (await hasValidSession(config.domain))) {
|
|
104
|
+
try {
|
|
105
|
+
const context = await loadSession(browser, config.domain);
|
|
106
|
+
const page = await context.newPage();
|
|
107
|
+
// Navigate to verify session
|
|
108
|
+
await page.goto(config.loginUrl);
|
|
109
|
+
await page.waitForLoadState("domcontentloaded");
|
|
110
|
+
await page.waitForTimeout(2000);
|
|
111
|
+
const currentUrl = page.url();
|
|
112
|
+
// Check if we got redirected to login
|
|
113
|
+
if (config.isLoginPage(currentUrl)) {
|
|
114
|
+
console.log("⚠️ Session expired, need to re-login...");
|
|
115
|
+
await context.close();
|
|
116
|
+
await browser.close();
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Optionally verify session
|
|
120
|
+
if (config.verifySession) {
|
|
121
|
+
const isValid = await config.verifySession(page);
|
|
122
|
+
if (!isValid) {
|
|
123
|
+
console.log("⚠️ Session invalid, need to re-login...");
|
|
124
|
+
await context.close();
|
|
125
|
+
await browser.close();
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log("✅ Using cached session");
|
|
129
|
+
return { browser, session: { context, page } };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log("✅ Using cached session");
|
|
134
|
+
return { browser, session: { context, page } };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.log("⚠️ Failed to load session, need to re-login...", error);
|
|
140
|
+
await browser.close();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
await browser.close();
|
|
145
|
+
}
|
|
146
|
+
// Need fresh login - always visible for interactive login
|
|
147
|
+
const session = await performInteractiveLogin(config);
|
|
148
|
+
// Get the browser from the session context
|
|
149
|
+
const sessionBrowser = session.context.browser();
|
|
150
|
+
if (!sessionBrowser) {
|
|
151
|
+
throw new Error("Failed to get browser from session");
|
|
152
|
+
}
|
|
153
|
+
// After login, reopen with headless browser if needed
|
|
154
|
+
if (useHeadless) {
|
|
155
|
+
const newBrowser = await chromium.launch({ headless: true });
|
|
156
|
+
const context = await loadSession(newBrowser, config.domain);
|
|
157
|
+
const page = await context.newPage();
|
|
158
|
+
// Close the interactive session
|
|
159
|
+
await sessionBrowser.close();
|
|
160
|
+
return { browser: newBrowser, session: { context, page } };
|
|
161
|
+
}
|
|
162
|
+
return { browser: sessionBrowser, session };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Clears the session for a domain.
|
|
166
|
+
*/
|
|
167
|
+
export async function clearSession(domain) {
|
|
168
|
+
const sessionPath = getSessionPath(domain);
|
|
169
|
+
return removeFile(sessionPath);
|
|
170
|
+
}
|
|
171
|
+
// ============================================
|
|
172
|
+
// Platform-specific helpers
|
|
173
|
+
// ============================================
|
|
174
|
+
/**
|
|
175
|
+
* Checks if the page has a valid Firebase auth token.
|
|
176
|
+
* Used by HighLevel/GoHighLevel portals.
|
|
177
|
+
*/
|
|
178
|
+
export async function hasValidFirebaseToken(page) {
|
|
179
|
+
try {
|
|
180
|
+
return await page.evaluate(() => {
|
|
181
|
+
const tokenKey = Object.keys(localStorage).find((k) => k.includes("firebase:authUser"));
|
|
182
|
+
if (!tokenKey)
|
|
183
|
+
return false;
|
|
184
|
+
const tokenData = JSON.parse(localStorage.getItem(tokenKey) ?? "{}");
|
|
185
|
+
const expirationTime = tokenData?.stsTokenManager?.expirationTime;
|
|
186
|
+
if (expirationTime) {
|
|
187
|
+
return Date.now() < expirationTime;
|
|
188
|
+
}
|
|
189
|
+
return !!tokenData?.stsTokenManager?.accessToken;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Skool-specific login page checker.
|
|
198
|
+
*/
|
|
199
|
+
export const isSkoolLoginPage = createLoginChecker([/\/login/, /accounts\.google\.com/]);
|
|
200
|
+
/**
|
|
201
|
+
* HighLevel-specific login page checker.
|
|
202
|
+
*/
|
|
203
|
+
export const isHighLevelLoginPage = createLoginChecker([
|
|
204
|
+
/sso\.clientclub\.net/,
|
|
205
|
+
/\/login/,
|
|
206
|
+
/\/signin/,
|
|
207
|
+
/\/auth/,
|
|
208
|
+
/accounts\.google\.com/,
|
|
209
|
+
/firebaseapp\.com/,
|
|
210
|
+
]);
|
|
211
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/shared/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a file or directory exists.
|
|
3
|
+
*/
|
|
4
|
+
export declare function pathExists(path: string): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Ensure a directory exists, creating it recursively if needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensureDir(dir: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Write a file, creating parent directories if needed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function outputFile(path: string, data: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Write JSON to a file, creating parent directories if needed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function outputJson(path: string, data: unknown): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Read and parse a JSON file.
|
|
19
|
+
* Returns null if file doesn't exist or can't be parsed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readJson<T = unknown>(path: string): Promise<T | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Remove a file if it exists.
|
|
24
|
+
*/
|
|
25
|
+
export declare function removeFile(path: string): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Get file size in bytes, or null if file doesn't exist.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getFileSize(path: string): Promise<number | null>;
|
|
30
|
+
export { readFile, writeFile, mkdir, unlink, stat } from "node:fs/promises";
|
|
31
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/shared/fs.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAO3E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOtE;AAGD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC"}
|