offcourse 0.0.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +255 -20
  2. package/dist/cli/commands/config.d.ts +13 -0
  3. package/dist/cli/commands/config.d.ts.map +1 -0
  4. package/dist/cli/commands/config.js +66 -0
  5. package/dist/cli/commands/config.js.map +1 -0
  6. package/dist/cli/commands/inspect.d.ts +11 -0
  7. package/dist/cli/commands/inspect.d.ts.map +1 -0
  8. package/dist/cli/commands/inspect.js +365 -0
  9. package/dist/cli/commands/inspect.js.map +1 -0
  10. package/dist/cli/commands/login.d.ts +12 -0
  11. package/dist/cli/commands/login.d.ts.map +1 -0
  12. package/dist/cli/commands/login.js +55 -0
  13. package/dist/cli/commands/login.js.map +1 -0
  14. package/dist/cli/commands/status.d.ts +15 -0
  15. package/dist/cli/commands/status.d.ts.map +1 -0
  16. package/dist/cli/commands/status.js +118 -0
  17. package/dist/cli/commands/status.js.map +1 -0
  18. package/dist/cli/commands/sync.d.ts +15 -0
  19. package/dist/cli/commands/sync.d.ts.map +1 -0
  20. package/dist/cli/commands/sync.js +921 -0
  21. package/dist/cli/commands/sync.js.map +1 -0
  22. package/dist/cli/commands/syncHighLevel.d.ts +23 -0
  23. package/dist/cli/commands/syncHighLevel.d.ts.map +1 -0
  24. package/dist/cli/commands/syncHighLevel.js +479 -0
  25. package/dist/cli/commands/syncHighLevel.js.map +1 -0
  26. package/dist/cli/index.d.ts +3 -0
  27. package/dist/cli/index.d.ts.map +1 -0
  28. package/dist/cli/index.js +106 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/config/configManager.d.ts +31 -0
  31. package/dist/config/configManager.d.ts.map +1 -0
  32. package/dist/config/configManager.js +68 -0
  33. package/dist/config/configManager.js.map +1 -0
  34. package/dist/config/paths.d.ts +21 -0
  35. package/dist/config/paths.d.ts.map +1 -0
  36. package/dist/config/paths.js +33 -0
  37. package/dist/config/paths.js.map +1 -0
  38. package/dist/config/schema.d.ts +60 -0
  39. package/dist/config/schema.d.ts.map +1 -0
  40. package/dist/config/schema.js +50 -0
  41. package/dist/config/schema.js.map +1 -0
  42. package/dist/downloader/hlsDownloader.d.ts +58 -0
  43. package/dist/downloader/hlsDownloader.d.ts.map +1 -0
  44. package/dist/downloader/hlsDownloader.js +263 -0
  45. package/dist/downloader/hlsDownloader.js.map +1 -0
  46. package/dist/downloader/hlsValidator.d.ts +35 -0
  47. package/dist/downloader/hlsValidator.d.ts.map +1 -0
  48. package/dist/downloader/hlsValidator.js +152 -0
  49. package/dist/downloader/hlsValidator.js.map +1 -0
  50. package/dist/downloader/index.d.ts +29 -0
  51. package/dist/downloader/index.d.ts.map +1 -0
  52. package/dist/downloader/index.js +55 -0
  53. package/dist/downloader/index.js.map +1 -0
  54. package/dist/downloader/loomDownloader.d.ts +56 -0
  55. package/dist/downloader/loomDownloader.d.ts.map +1 -0
  56. package/dist/downloader/loomDownloader.js +562 -0
  57. package/dist/downloader/loomDownloader.js.map +1 -0
  58. package/dist/downloader/queue.d.ts +56 -0
  59. package/dist/downloader/queue.d.ts.map +1 -0
  60. package/dist/downloader/queue.js +88 -0
  61. package/dist/downloader/queue.js.map +1 -0
  62. package/dist/downloader/vimeoDownloader.d.ts +52 -0
  63. package/dist/downloader/vimeoDownloader.d.ts.map +1 -0
  64. package/dist/downloader/vimeoDownloader.js +569 -0
  65. package/dist/downloader/vimeoDownloader.js.map +1 -0
  66. package/dist/scraper/extractor.d.ts +53 -0
  67. package/dist/scraper/extractor.d.ts.map +1 -0
  68. package/dist/scraper/extractor.js +627 -0
  69. package/dist/scraper/extractor.js.map +1 -0
  70. package/dist/scraper/highlevel/extractor.d.ts +89 -0
  71. package/dist/scraper/highlevel/extractor.d.ts.map +1 -0
  72. package/dist/scraper/highlevel/extractor.js +373 -0
  73. package/dist/scraper/highlevel/extractor.js.map +1 -0
  74. package/dist/scraper/highlevel/index.d.ts +3 -0
  75. package/dist/scraper/highlevel/index.d.ts.map +1 -0
  76. package/dist/scraper/highlevel/index.js +3 -0
  77. package/dist/scraper/highlevel/index.js.map +1 -0
  78. package/dist/scraper/highlevel/navigator.d.ts +86 -0
  79. package/dist/scraper/highlevel/navigator.d.ts.map +1 -0
  80. package/dist/scraper/highlevel/navigator.js +505 -0
  81. package/dist/scraper/highlevel/navigator.js.map +1 -0
  82. package/dist/scraper/highlevel/schemas.d.ts +188 -0
  83. package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
  84. package/dist/scraper/highlevel/schemas.js +139 -0
  85. package/dist/scraper/highlevel/schemas.js.map +1 -0
  86. package/dist/scraper/navigator.d.ts +68 -0
  87. package/dist/scraper/navigator.d.ts.map +1 -0
  88. package/dist/scraper/navigator.js +257 -0
  89. package/dist/scraper/navigator.js.map +1 -0
  90. package/dist/scraper/schemas.d.ts +57 -0
  91. package/dist/scraper/schemas.d.ts.map +1 -0
  92. package/dist/scraper/schemas.js +135 -0
  93. package/dist/scraper/schemas.js.map +1 -0
  94. package/dist/scraper/videoInterceptor.d.ts +23 -0
  95. package/dist/scraper/videoInterceptor.d.ts.map +1 -0
  96. package/dist/scraper/videoInterceptor.js +330 -0
  97. package/dist/scraper/videoInterceptor.js.map +1 -0
  98. package/dist/shared/auth.d.ts +58 -0
  99. package/dist/shared/auth.d.ts.map +1 -0
  100. package/dist/shared/auth.js +197 -0
  101. package/dist/shared/auth.js.map +1 -0
  102. package/dist/shared/firebase.d.ts +60 -0
  103. package/dist/shared/firebase.d.ts.map +1 -0
  104. package/dist/shared/firebase.js +102 -0
  105. package/dist/shared/firebase.js.map +1 -0
  106. package/dist/shared/fs.d.ts +31 -0
  107. package/dist/shared/fs.d.ts.map +1 -0
  108. package/dist/shared/fs.js +77 -0
  109. package/dist/shared/fs.js.map +1 -0
  110. package/dist/shared/http.d.ts +15 -0
  111. package/dist/shared/http.d.ts.map +1 -0
  112. package/dist/shared/http.js +31 -0
  113. package/dist/shared/http.js.map +1 -0
  114. package/dist/shared/index.d.ts +7 -0
  115. package/dist/shared/index.d.ts.map +1 -0
  116. package/dist/shared/index.js +7 -0
  117. package/dist/shared/index.js.map +1 -0
  118. package/dist/shared/slug.d.ts +11 -0
  119. package/dist/shared/slug.d.ts.map +1 -0
  120. package/dist/shared/slug.js +25 -0
  121. package/dist/shared/slug.js.map +1 -0
  122. package/dist/shared/url.d.ts +43 -0
  123. package/dist/shared/url.d.ts.map +1 -0
  124. package/dist/shared/url.js +54 -0
  125. package/dist/shared/url.js.map +1 -0
  126. package/dist/state/database.d.ts +246 -0
  127. package/dist/state/database.d.ts.map +1 -0
  128. package/dist/state/database.js +679 -0
  129. package/dist/state/database.js.map +1 -0
  130. package/dist/state/index.d.ts +2 -0
  131. package/dist/state/index.d.ts.map +1 -0
  132. package/dist/state/index.js +2 -0
  133. package/dist/state/index.js.map +1 -0
  134. package/dist/storage/fileSystem.d.ts +56 -0
  135. package/dist/storage/fileSystem.d.ts.map +1 -0
  136. package/dist/storage/fileSystem.js +129 -0
  137. package/dist/storage/fileSystem.js.map +1 -0
  138. package/package.json +71 -11
  139. package/cli.js +0 -45
@@ -0,0 +1,263 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { execa } from "execa";
4
+ import * as HLS from "hls-parser";
5
+ /**
6
+ * Checks if ffmpeg is available on the system.
7
+ */
8
+ /* v8 ignore next 8 */
9
+ export async function checkFfmpeg() {
10
+ try {
11
+ await execa("ffmpeg", ["-version"]);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ /**
19
+ * Fetches an HLS master playlist and parses quality variants.
20
+ */
21
+ /* v8 ignore next 14 */
22
+ export async function fetchHLSQualities(masterUrl) {
23
+ try {
24
+ const response = await fetch(masterUrl);
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch playlist: ${response.status}`);
27
+ }
28
+ const content = await response.text();
29
+ return parseHLSPlaylist(content, masterUrl);
30
+ }
31
+ catch (error) {
32
+ console.error("Failed to fetch HLS qualities:", error);
33
+ return [];
34
+ }
35
+ }
36
+ /**
37
+ * Parses an HLS master playlist to extract quality variants.
38
+ * Uses hls-parser for robust parsing.
39
+ */
40
+ export function parseHLSPlaylist(content, baseUrl) {
41
+ try {
42
+ const playlist = HLS.parse(content);
43
+ // Check if it's a master playlist with variants
44
+ if (!("variants" in playlist) || !playlist.variants) {
45
+ return [];
46
+ }
47
+ const variants = playlist.variants.map((variant) => {
48
+ const bandwidth = variant.bandwidth ?? 0;
49
+ const resolution = variant.resolution;
50
+ const width = resolution?.width;
51
+ const height = resolution?.height;
52
+ // Build absolute URL
53
+ const variantUrl = variant.uri.startsWith("http")
54
+ ? variant.uri
55
+ : new URL(variant.uri, baseUrl).href;
56
+ const label = height ? `${height}p` : `${Math.round(bandwidth / 1000)}k`;
57
+ return {
58
+ label,
59
+ url: variantUrl,
60
+ bandwidth,
61
+ width,
62
+ height,
63
+ };
64
+ });
65
+ // Sort by bandwidth (highest first)
66
+ variants.sort((a, b) => b.bandwidth - a.bandwidth);
67
+ return variants;
68
+ }
69
+ catch {
70
+ // Fallback to empty array on parse error
71
+ return [];
72
+ }
73
+ }
74
+ /**
75
+ * Gets the best quality URL from a master playlist.
76
+ * @param masterUrl The master playlist URL
77
+ * @param preferredHeight Preferred video height (e.g., 720, 1080)
78
+ */
79
+ /* v8 ignore start */
80
+ export async function getBestQualityUrl(masterUrl, preferredHeight) {
81
+ const qualities = await fetchHLSQualities(masterUrl);
82
+ if (qualities.length === 0) {
83
+ // Assume it's a direct media playlist
84
+ return masterUrl;
85
+ }
86
+ if (preferredHeight) {
87
+ // Find closest match to preferred height
88
+ const match = qualities.find((q) => q.height === preferredHeight);
89
+ if (match)
90
+ return match.url;
91
+ // Find closest lower quality
92
+ const lower = qualities.filter((q) => q.height && q.height <= preferredHeight);
93
+ const closest = lower[0];
94
+ if (closest) {
95
+ return closest.url;
96
+ }
97
+ }
98
+ // Return highest quality
99
+ return qualities[0]?.url ?? masterUrl;
100
+ }
101
+ /**
102
+ * Downloads an HLS stream using ffmpeg.
103
+ * @param hlsUrl The HLS playlist URL (master or media)
104
+ * @param outputPath The output file path (should end in .mp4)
105
+ * @param onProgress Progress callback
106
+ */
107
+ export async function downloadHLSVideo(hlsUrl, outputPath, onProgress) {
108
+ // Check if ffmpeg is available
109
+ const hasFfmpeg = await checkFfmpeg();
110
+ if (!hasFfmpeg) {
111
+ return {
112
+ success: false,
113
+ error: "ffmpeg is not installed. Please install ffmpeg to download HLS videos.",
114
+ errorCode: "FFMPEG_NOT_FOUND",
115
+ };
116
+ }
117
+ // Ensure output directory exists
118
+ const outputDir = path.dirname(outputPath);
119
+ if (!fs.existsSync(outputDir)) {
120
+ fs.mkdirSync(outputDir, { recursive: true });
121
+ }
122
+ // Build ffmpeg command
123
+ const args = [
124
+ "-y", // Overwrite output
125
+ "-hide_banner",
126
+ "-loglevel",
127
+ "warning",
128
+ "-stats",
129
+ "-i",
130
+ hlsUrl,
131
+ "-c",
132
+ "copy", // Copy streams without re-encoding
133
+ "-bsf:a",
134
+ "aac_adtstoasc", // Fix AAC stream
135
+ outputPath,
136
+ ];
137
+ let duration = 0;
138
+ let currentTime = 0;
139
+ let lastProgressUpdate = 0;
140
+ const updateProgress = () => {
141
+ if (duration > 0 && onProgress) {
142
+ const percent = Math.min((currentTime / duration) * 100, 100);
143
+ const now = Date.now();
144
+ // Throttle progress updates to avoid spam
145
+ if (now - lastProgressUpdate > 200 || percent >= 100) {
146
+ lastProgressUpdate = now;
147
+ onProgress({
148
+ phase: "downloading",
149
+ percent: Math.round(percent),
150
+ currentBytes: currentTime,
151
+ totalBytes: duration,
152
+ });
153
+ }
154
+ }
155
+ };
156
+ try {
157
+ const subprocess = execa("ffmpeg", args);
158
+ // Parse stderr for progress info
159
+ subprocess.stderr?.on("data", (data) => {
160
+ const output = data.toString();
161
+ // Parse duration from input info
162
+ const durationMatch = /Duration:\s*(\d{2}):(\d{2}):(\d{2})\.(\d{2})/.exec(output);
163
+ if (durationMatch && duration === 0) {
164
+ const [, hours = "0", mins = "0", secs = "0", centis = "0"] = durationMatch;
165
+ duration =
166
+ parseInt(hours, 10) * 3600 +
167
+ parseInt(mins, 10) * 60 +
168
+ parseInt(secs, 10) +
169
+ parseInt(centis, 10) / 100;
170
+ }
171
+ // Parse current time from progress
172
+ const timeMatch = /time=(\d{2}):(\d{2}):(\d{2})\.(\d{2})/.exec(output);
173
+ if (timeMatch) {
174
+ const [, hours = "0", mins = "0", secs = "0", centis = "0"] = timeMatch;
175
+ currentTime =
176
+ parseInt(hours, 10) * 3600 +
177
+ parseInt(mins, 10) * 60 +
178
+ parseInt(secs, 10) +
179
+ parseInt(centis, 10) / 100;
180
+ updateProgress();
181
+ }
182
+ });
183
+ await subprocess;
184
+ // Final progress update
185
+ if (onProgress) {
186
+ onProgress({
187
+ phase: "complete",
188
+ percent: 100,
189
+ });
190
+ }
191
+ return {
192
+ success: true,
193
+ outputPath,
194
+ duration,
195
+ };
196
+ }
197
+ catch (error) {
198
+ const errorMessage = error instanceof Error ? error.message : String(error);
199
+ return {
200
+ success: false,
201
+ error: `ffmpeg error: ${errorMessage}`,
202
+ errorCode: "FFMPEG_ERROR",
203
+ };
204
+ }
205
+ }
206
+ /**
207
+ * Downloads a HighLevel HLS video with quality selection.
208
+ * @param masterUrl The master playlist URL (may include token)
209
+ * @param outputPath The output file path
210
+ * @param preferredQuality Preferred quality label (e.g., "720p", "1080p")
211
+ * @param onProgress Progress callback
212
+ */
213
+ export async function downloadHighLevelVideo(masterUrl, outputPath, preferredQuality, onProgress) {
214
+ // Report start
215
+ onProgress?.({
216
+ phase: "preparing",
217
+ percent: 0,
218
+ });
219
+ // Parse preferred height from quality string
220
+ let preferredHeight;
221
+ if (preferredQuality) {
222
+ const match = /(\d+)p?/i.exec(preferredQuality);
223
+ if (match?.[1]) {
224
+ preferredHeight = parseInt(match[1], 10);
225
+ }
226
+ }
227
+ // Get the best quality URL
228
+ let downloadUrl = masterUrl;
229
+ try {
230
+ downloadUrl = await getBestQualityUrl(masterUrl, preferredHeight);
231
+ }
232
+ catch (error) {
233
+ console.warn("Failed to fetch quality options, using master URL:", error);
234
+ }
235
+ // Download using ffmpeg
236
+ return downloadHLSVideo(downloadUrl, outputPath, onProgress);
237
+ }
238
+ /* v8 ignore stop */
239
+ /**
240
+ * Extracts video info from a HighLevel HLS URL.
241
+ */
242
+ export function parseHighLevelVideoUrl(url) {
243
+ try {
244
+ const urlObj = new URL(url);
245
+ // Pattern: /hls/v2/memberships/{locationId}/videos/{videoId}/...
246
+ const match = /\/memberships\/([^/]+)\/videos\/([^/,]+)/.exec(urlObj.pathname);
247
+ const locationId = match?.[1];
248
+ const videoId = match?.[2];
249
+ if (!locationId || !videoId) {
250
+ return null;
251
+ }
252
+ const token = urlObj.searchParams.get("token");
253
+ return {
254
+ locationId,
255
+ videoId,
256
+ ...(token ? { token } : {}),
257
+ };
258
+ }
259
+ catch {
260
+ return null;
261
+ }
262
+ }
263
+ //# sourceMappingURL=hlsDownloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlsDownloader.js","sourceRoot":"","sources":["../../src/downloader/hlsDownloader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { Page } from "playwright";
2
+ /**
3
+ * Result of HLS validation.
4
+ */
5
+ export interface HlsValidationResult {
6
+ isValid: boolean;
7
+ hlsUrl: string | null;
8
+ error?: string;
9
+ errorCode?: string;
10
+ details?: string;
11
+ }
12
+ /**
13
+ * Validates that a Loom video has an accessible HLS stream.
14
+ * This should be called during the scanning phase to catch issues early.
15
+ *
16
+ * @param loomUrl - The Loom video URL
17
+ * @param page - Optional Playwright page for network interception fallback
18
+ */
19
+ export declare function validateLoomHls(loomUrl: string, page?: Page): Promise<HlsValidationResult>;
20
+ /**
21
+ * Validates a Vimeo video has accessible streams.
22
+ * @param vimeoUrl - The Vimeo video URL
23
+ * @param page - Optional Playwright page for domain-restricted videos
24
+ * @param lessonUrl - Optional lesson URL for referer-based access
25
+ */
26
+ export declare function validateVimeoVideo(vimeoUrl: string, page?: Page, lessonUrl?: string): Promise<HlsValidationResult>;
27
+ /**
28
+ * Validates HLS availability for a video URL based on its type.
29
+ * @param videoUrl - The video URL to validate
30
+ * @param videoType - The type of video (loom, vimeo, etc.)
31
+ * @param page - Optional Playwright page for network interception fallback
32
+ * @param lessonUrl - Optional lesson URL for referer-based access
33
+ */
34
+ export declare function validateVideoHls(videoUrl: string, videoType: string, page?: Page, lessonUrl?: string): Promise<HlsValidationResult>;
35
+ //# sourceMappingURL=hlsValidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlsValidator.d.ts","sourceRoot":"","sources":["../../src/downloader/hlsValidator.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgDhG;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,IAAI,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,mBAAmB,CAAC,CA0D9B;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,IAAI,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,mBAAmB,CAAC,CAgC9B"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * HLS stream validation - requires network access to verify streams.
3
+ * Excluded from coverage via vitest.config.ts.
4
+ */
5
+ import { extractLoomId, getLoomVideoInfoDetailed } from "./loomDownloader.js";
6
+ import { extractVimeoId, getVimeoVideoInfo, getVimeoVideoInfoFromBrowser, } from "./vimeoDownloader.js";
7
+ import { captureLoomHls, captureVimeoConfig } from "../scraper/videoInterceptor.js";
8
+ /**
9
+ * Validates that a Loom video has an accessible HLS stream.
10
+ * This should be called during the scanning phase to catch issues early.
11
+ *
12
+ * @param loomUrl - The Loom video URL
13
+ * @param page - Optional Playwright page for network interception fallback
14
+ */
15
+ export async function validateLoomHls(loomUrl, page) {
16
+ const videoId = extractLoomId(loomUrl);
17
+ if (!videoId) {
18
+ return {
19
+ isValid: false,
20
+ hlsUrl: null,
21
+ error: "Invalid Loom URL - could not extract video ID",
22
+ errorCode: "INVALID_URL",
23
+ details: `URL: ${loomUrl}`,
24
+ };
25
+ }
26
+ // First try direct API
27
+ const result = await getLoomVideoInfoDetailed(videoId, 2, 500);
28
+ if (result.success && result.info) {
29
+ return {
30
+ isValid: true,
31
+ hlsUrl: result.info.hlsUrl,
32
+ };
33
+ }
34
+ // If direct API failed and we have a page, try network interception
35
+ if (page && result.errorCode === "HLS_NOT_FOUND") {
36
+ const captured = await captureLoomHls(page, videoId, 15000);
37
+ if (captured.hlsUrl) {
38
+ return {
39
+ isValid: true,
40
+ hlsUrl: captured.hlsUrl,
41
+ details: "Captured via network interception",
42
+ };
43
+ }
44
+ }
45
+ // Return the original error
46
+ const validation = {
47
+ isValid: false,
48
+ hlsUrl: null,
49
+ error: result.error ?? "Failed to fetch Loom video info",
50
+ };
51
+ if (result.errorCode) {
52
+ validation.errorCode = result.errorCode;
53
+ }
54
+ if (result.details) {
55
+ validation.details = result.details;
56
+ }
57
+ return validation;
58
+ }
59
+ /**
60
+ * Validates a Vimeo video has accessible streams.
61
+ * @param vimeoUrl - The Vimeo video URL
62
+ * @param page - Optional Playwright page for domain-restricted videos
63
+ * @param lessonUrl - Optional lesson URL for referer-based access
64
+ */
65
+ export async function validateVimeoVideo(vimeoUrl, page, lessonUrl) {
66
+ const videoId = extractVimeoId(vimeoUrl);
67
+ if (!videoId) {
68
+ return {
69
+ isValid: false,
70
+ hlsUrl: null,
71
+ error: "Invalid Vimeo URL - could not extract video ID",
72
+ errorCode: "INVALID_URL",
73
+ details: `URL: ${vimeoUrl}`,
74
+ };
75
+ }
76
+ // Extract unlisted hash if present
77
+ const hashMatch = /vimeo\.com\/\d+\/([a-f0-9]+)/.exec(vimeoUrl) ?? /[?&]h=([a-f0-9]+)/.exec(vimeoUrl);
78
+ const unlistedHash = hashMatch?.[1] ?? null;
79
+ // First try direct fetch (works for public videos)
80
+ let result = await getVimeoVideoInfo(videoId, unlistedHash, lessonUrl);
81
+ // If video is private/restricted and we have a browser context, try browser-based fetch
82
+ if (!result.success && result.errorCode === "PRIVATE_VIDEO" && page) {
83
+ result = await getVimeoVideoInfoFromBrowser(page, videoId, unlistedHash);
84
+ }
85
+ // If still failing and we have a page, try extracting from the running player
86
+ if (!result.success && result.errorCode === "PRIVATE_VIDEO" && page) {
87
+ const captured = await captureVimeoConfig(page, videoId, 20000);
88
+ if (captured.hlsUrl || captured.progressiveUrl) {
89
+ return {
90
+ isValid: true,
91
+ hlsUrl: captured.hlsUrl ?? captured.progressiveUrl,
92
+ details: "Extracted from running player",
93
+ };
94
+ }
95
+ }
96
+ if (!result.success || !result.info) {
97
+ const validation = {
98
+ isValid: false,
99
+ hlsUrl: null,
100
+ error: result.error ?? "Failed to fetch Vimeo video info",
101
+ };
102
+ if (result.errorCode) {
103
+ validation.errorCode = result.errorCode;
104
+ }
105
+ if (result.details) {
106
+ validation.details = result.details;
107
+ }
108
+ return validation;
109
+ }
110
+ // Return HLS URL if available, or progressive URL as fallback
111
+ return {
112
+ isValid: true,
113
+ hlsUrl: result.info.hlsUrl ?? result.info.progressiveUrl,
114
+ };
115
+ }
116
+ /**
117
+ * Validates HLS availability for a video URL based on its type.
118
+ * @param videoUrl - The video URL to validate
119
+ * @param videoType - The type of video (loom, vimeo, etc.)
120
+ * @param page - Optional Playwright page for network interception fallback
121
+ * @param lessonUrl - Optional lesson URL for referer-based access
122
+ */
123
+ export async function validateVideoHls(videoUrl, videoType, page, lessonUrl) {
124
+ switch (videoType) {
125
+ case "loom":
126
+ return validateLoomHls(videoUrl, page);
127
+ case "vimeo":
128
+ return validateVimeoVideo(videoUrl, page, lessonUrl);
129
+ case "youtube":
130
+ case "wistia":
131
+ // These require yt-dlp - skip validation, will fail at download
132
+ return {
133
+ isValid: true,
134
+ hlsUrl: null,
135
+ details: `${videoType} requires yt-dlp - will attempt download`,
136
+ };
137
+ case "native":
138
+ // Native videos have direct URLs, no HLS needed
139
+ return {
140
+ isValid: true,
141
+ hlsUrl: videoUrl,
142
+ };
143
+ default:
144
+ return {
145
+ isValid: false,
146
+ hlsUrl: null,
147
+ error: `Unknown video type: ${videoType}`,
148
+ errorCode: "UNKNOWN_TYPE",
149
+ };
150
+ }
151
+ }
152
+ //# sourceMappingURL=hlsValidator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlsValidator.js","sourceRoot":"","sources":["../../src/downloader/hlsValidator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Video download coordination - delegates to type-specific downloaders.
3
+ */
4
+ import { type DownloadProgress } from "./loomDownloader.js";
5
+ export interface VideoDownloadTask {
6
+ lessonId: number;
7
+ lessonName: string;
8
+ videoUrl: string;
9
+ videoType: "loom" | "vimeo" | "youtube" | "wistia" | "native" | "hls" | "highlevel" | "unknown" | null;
10
+ outputPath: string;
11
+ /** Optional preferred quality (e.g., "720p", "1080p") */
12
+ preferredQuality?: string | undefined;
13
+ }
14
+ export interface DownloadResult {
15
+ success: boolean;
16
+ error?: string | undefined;
17
+ errorCode?: string | undefined;
18
+ details?: string | undefined;
19
+ }
20
+ /**
21
+ * Downloads a video based on its type.
22
+ */
23
+ export declare function downloadVideo(task: VideoDownloadTask, onProgress?: (progress: DownloadProgress) => void): Promise<DownloadResult>;
24
+ export { downloadFile, downloadLoomVideo, extractLoomId, getLoomVideoInfoDetailed, type DownloadProgress, type LoomFetchResult, } from "./loomDownloader.js";
25
+ export { downloadVimeoVideo, extractVimeoId, getVimeoVideoInfo, getVimeoVideoInfoFromBrowser, type VimeoDownloadResult, type VimeoFetchResult, type VimeoVideoInfo, } from "./vimeoDownloader.js";
26
+ export { AsyncQueue, type QueueItem, type QueueOptions } from "./queue.js";
27
+ export { validateLoomHls, validateVideoHls, validateVimeoVideo, type HlsValidationResult, } from "./hlsValidator.js";
28
+ export { checkFfmpeg, downloadHighLevelVideo, downloadHLSVideo, fetchHLSQualities, getBestQualityUrl, parseHighLevelVideoUrl, parseHLSPlaylist, type HLSDownloadResult, type HLSQuality, } from "./hlsDownloader.js";
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/downloader/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAmC,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI7F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EACL,MAAM,GACN,OAAO,GACP,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,KAAK,GACL,WAAW,GACX,SAAS,GACT,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,EACvB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,GAChD,OAAO,CAAC,cAAc,CAAC,CA8CzB;AAED,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,4BAA4B,EAC5B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Video download coordination - delegates to type-specific downloaders.
3
+ */
4
+ import { downloadFile, downloadLoomVideo } from "./loomDownloader.js";
5
+ import { downloadVimeoVideo } from "./vimeoDownloader.js";
6
+ import { downloadHighLevelVideo, downloadHLSVideo } from "./hlsDownloader.js";
7
+ /**
8
+ * Downloads a video based on its type.
9
+ */
10
+ export async function downloadVideo(task, onProgress) {
11
+ const { videoUrl, videoType, outputPath, preferredQuality } = task;
12
+ switch (videoType) {
13
+ case "loom":
14
+ return downloadLoomVideo(videoUrl, outputPath, onProgress);
15
+ case "vimeo":
16
+ return downloadVimeoVideo(videoUrl, outputPath, onProgress);
17
+ case "native":
18
+ // Direct MP4/WebM URL - download directly
19
+ return downloadFile(videoUrl, outputPath, onProgress);
20
+ case "hls":
21
+ // Generic HLS stream
22
+ return downloadHLSVideo(videoUrl, outputPath, onProgress);
23
+ case "highlevel":
24
+ // HighLevel HLS video with quality selection
25
+ return downloadHighLevelVideo(videoUrl, outputPath, preferredQuality, onProgress);
26
+ case "youtube":
27
+ case "wistia":
28
+ // These require yt-dlp or special handling
29
+ return {
30
+ success: false,
31
+ error: `${videoType} videos are not yet supported. Consider installing yt-dlp. Video URL: ${videoUrl}`,
32
+ errorCode: "UNSUPPORTED_TYPE",
33
+ };
34
+ case "unknown":
35
+ default:
36
+ // Try direct download as fallback
37
+ if (/\.(mp4|webm|mov)(\?|$)/i.exec(videoUrl)) {
38
+ return downloadFile(videoUrl, outputPath, onProgress);
39
+ }
40
+ // Try HLS if it looks like a playlist
41
+ if (/\.m3u8(\?|$)/i.exec(videoUrl)) {
42
+ return downloadHLSVideo(videoUrl, outputPath, onProgress);
43
+ }
44
+ return {
45
+ success: false,
46
+ error: `Unknown video type. URL: ${videoUrl}`,
47
+ };
48
+ }
49
+ }
50
+ export { downloadFile, downloadLoomVideo, extractLoomId, getLoomVideoInfoDetailed, } from "./loomDownloader.js";
51
+ export { downloadVimeoVideo, extractVimeoId, getVimeoVideoInfo, getVimeoVideoInfoFromBrowser, } from "./vimeoDownloader.js";
52
+ export { AsyncQueue } from "./queue.js";
53
+ export { validateLoomHls, validateVideoHls, validateVimeoVideo, } from "./hlsValidator.js";
54
+ export { checkFfmpeg, downloadHighLevelVideo, downloadHLSVideo, fetchHLSQualities, getBestQualityUrl, parseHighLevelVideoUrl, parseHLSPlaylist, } from "./hlsDownloader.js";
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/downloader/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAyB,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA4B9E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAuB,EACvB,UAAiD;IAEjD,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC;IAEnE,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAE7D,KAAK,OAAO;YACV,OAAO,kBAAkB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAE9D,KAAK,QAAQ;YACX,0CAA0C;YAC1C,OAAO,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAExD,KAAK,KAAK;YACR,qBAAqB;YACrB,OAAO,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAE5D,KAAK,WAAW;YACd,6CAA6C;YAC7C,OAAO,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,CAAC,CAAC;QAEpF,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ;YACX,2CAA2C;YAC3C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,GAAG,SAAS,yEAAyE,QAAQ,EAAE;gBACtG,SAAS,EAAE,kBAAkB;aAC9B,CAAC;QAEJ,KAAK,SAAS,CAAC;QACf;YACE,kCAAkC;YAClC,IAAI,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7C,OAAO,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YACxD,CAAC;YACD,sCAAsC;YACtC,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnC,OAAO,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YAC5D,CAAC;YACD,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,4BAA4B,QAAQ,EAAE;aAC9C,CAAC;IACN,CAAC;AACH,CAAC;AAED,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,wBAAwB,GAGzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,4BAA4B,GAI7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAqC,MAAM,YAAY,CAAC;AAC3E,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GAEnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GAGjB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,56 @@
1
+ export interface LoomVideoInfo {
2
+ id: string;
3
+ title: string;
4
+ duration: number;
5
+ width: number;
6
+ height: number;
7
+ hlsUrl: string;
8
+ }
9
+ export interface LoomFetchResult {
10
+ success: boolean;
11
+ info?: LoomVideoInfo;
12
+ error?: string;
13
+ errorCode?: "EMBED_FETCH_FAILED" | "HLS_NOT_FOUND" | "RATE_LIMITED" | "NETWORK_ERROR" | "PARSE_ERROR";
14
+ statusCode?: number;
15
+ details?: string;
16
+ }
17
+ export interface DownloadProgress {
18
+ percent: number;
19
+ downloaded?: number | undefined;
20
+ total?: number | undefined;
21
+ phase?: "preparing" | "downloading" | "complete" | undefined;
22
+ currentBytes?: number | undefined;
23
+ totalBytes?: number | undefined;
24
+ }
25
+ /**
26
+ * Extracts the Loom video ID from various URL formats.
27
+ */
28
+ export declare function extractLoomId(url: string): string | null;
29
+ /**
30
+ * Fetches video information from Loom's embed page with detailed error reporting.
31
+ * Uses p-retry for automatic retries with exponential backoff.
32
+ */
33
+ export declare function getLoomVideoInfoDetailed(videoId: string, retryCount?: number, retryDelayMs?: number): Promise<LoomFetchResult>;
34
+ /**
35
+ * Fetches video information from Loom's embed page.
36
+ * @deprecated Use getLoomVideoInfoDetailed for better error reporting
37
+ */
38
+ export declare function getLoomVideoInfo(videoId: string): Promise<LoomVideoInfo | null>;
39
+ export interface LoomDownloadResult {
40
+ success: boolean;
41
+ error?: string;
42
+ errorCode?: LoomFetchResult["errorCode"] | "INVALID_URL" | "NO_VIDEO_STREAM" | "NO_SEGMENTS" | "DOWNLOAD_FAILED" | "MERGE_FAILED";
43
+ details?: string;
44
+ }
45
+ /**
46
+ * Downloads a Loom video using HLS.
47
+ */
48
+ export declare function downloadLoomVideo(urlOrId: string, outputPath: string, onProgress?: (progress: DownloadProgress) => void): Promise<LoomDownloadResult>;
49
+ /**
50
+ * Downloads a file directly.
51
+ */
52
+ export declare function downloadFile(url: string, outputPath: string, onProgress?: (progress: DownloadProgress) => void): Promise<{
53
+ success: boolean;
54
+ error?: string;
55
+ }>;
56
+ //# sourceMappingURL=loomDownloader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loomDownloader.d.ts","sourceRoot":"","sources":["../../src/downloader/loomDownloader.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EACN,oBAAoB,GACpB,eAAe,GACf,cAAc,GACd,eAAe,GACf,aAAa,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC;IAC7D,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGxD;AAkKD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,UAAU,SAAI,EACd,YAAY,SAAO,GAClB,OAAO,CAAC,eAAe,CAAC,CAoC1B;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAGrF;AAyMD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EACN,eAAe,CAAC,WAAW,CAAC,GAC5B,aAAa,GACb,iBAAiB,GACjB,aAAa,GACb,iBAAiB,GACjB,cAAc,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,GAChD,OAAO,CAAC,kBAAkB,CAAC,CAuL7B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,GAChD,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgE/C"}