offcourse 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/README.md +107 -8
  2. package/dist/cli/commands/config.js.map +1 -1
  3. package/dist/cli/commands/inspect.js +1 -1
  4. package/dist/cli/commands/inspect.js.map +1 -1
  5. package/dist/cli/commands/sync.d.ts +1 -2
  6. package/dist/cli/commands/sync.d.ts.map +1 -1
  7. package/dist/cli/commands/sync.js +17 -15
  8. package/dist/cli/commands/sync.js.map +1 -1
  9. package/dist/cli/commands/syncHighLevel.d.ts +1 -2
  10. package/dist/cli/commands/syncHighLevel.d.ts.map +1 -1
  11. package/dist/cli/commands/syncHighLevel.js +8 -9
  12. package/dist/cli/commands/syncHighLevel.js.map +1 -1
  13. package/dist/cli/commands/syncLearningSuite.d.ts +35 -0
  14. package/dist/cli/commands/syncLearningSuite.d.ts.map +1 -0
  15. package/dist/cli/commands/syncLearningSuite.js +765 -0
  16. package/dist/cli/commands/syncLearningSuite.js.map +1 -0
  17. package/dist/cli/index.js +39 -1
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/config/configManager.d.ts.map +1 -1
  20. package/dist/config/configManager.js +4 -0
  21. package/dist/config/configManager.js.map +1 -1
  22. package/dist/downloader/hlsDownloader.d.ts +10 -4
  23. package/dist/downloader/hlsDownloader.d.ts.map +1 -1
  24. package/dist/downloader/hlsDownloader.js +60 -29
  25. package/dist/downloader/hlsDownloader.js.map +1 -1
  26. package/dist/downloader/hlsValidator.d.ts.map +1 -1
  27. package/dist/downloader/hlsValidator.js +6 -2
  28. package/dist/downloader/hlsValidator.js.map +1 -1
  29. package/dist/downloader/index.d.ts +7 -0
  30. package/dist/downloader/index.d.ts.map +1 -1
  31. package/dist/downloader/index.js +9 -6
  32. package/dist/downloader/index.js.map +1 -1
  33. package/dist/downloader/loomDownloader.d.ts +1 -1
  34. package/dist/downloader/loomDownloader.d.ts.map +1 -1
  35. package/dist/downloader/loomDownloader.js +32 -27
  36. package/dist/downloader/loomDownloader.js.map +1 -1
  37. package/dist/downloader/queue.d.ts +4 -4
  38. package/dist/downloader/queue.d.ts.map +1 -1
  39. package/dist/downloader/queue.js.map +1 -1
  40. package/dist/downloader/vimeoDownloader.d.ts.map +1 -1
  41. package/dist/downloader/vimeoDownloader.js +7 -3
  42. package/dist/downloader/vimeoDownloader.js.map +1 -1
  43. package/dist/scraper/extractor.d.ts +4 -0
  44. package/dist/scraper/extractor.d.ts.map +1 -1
  45. package/dist/scraper/extractor.js +79 -79
  46. package/dist/scraper/extractor.js.map +1 -1
  47. package/dist/scraper/highlevel/extractor.d.ts +11 -19
  48. package/dist/scraper/highlevel/extractor.d.ts.map +1 -1
  49. package/dist/scraper/highlevel/extractor.js +72 -85
  50. package/dist/scraper/highlevel/extractor.js.map +1 -1
  51. package/dist/scraper/highlevel/navigator.d.ts +3 -10
  52. package/dist/scraper/highlevel/navigator.d.ts.map +1 -1
  53. package/dist/scraper/highlevel/navigator.js +140 -127
  54. package/dist/scraper/highlevel/navigator.js.map +1 -1
  55. package/dist/scraper/highlevel/schemas.d.ts +188 -0
  56. package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
  57. package/dist/scraper/highlevel/schemas.js +139 -0
  58. package/dist/scraper/highlevel/schemas.js.map +1 -0
  59. package/dist/scraper/learningsuite/extractor.d.ts +50 -0
  60. package/dist/scraper/learningsuite/extractor.d.ts.map +1 -0
  61. package/dist/scraper/learningsuite/extractor.js +429 -0
  62. package/dist/scraper/learningsuite/extractor.js.map +1 -0
  63. package/dist/scraper/learningsuite/index.d.ts +4 -0
  64. package/dist/scraper/learningsuite/index.d.ts.map +1 -0
  65. package/dist/scraper/{ghl → learningsuite}/index.js +1 -1
  66. package/dist/scraper/learningsuite/index.js.map +1 -0
  67. package/dist/scraper/learningsuite/navigator.d.ts +122 -0
  68. package/dist/scraper/learningsuite/navigator.d.ts.map +1 -0
  69. package/dist/scraper/learningsuite/navigator.js +736 -0
  70. package/dist/scraper/learningsuite/navigator.js.map +1 -0
  71. package/dist/scraper/learningsuite/schemas.d.ts +270 -0
  72. package/dist/scraper/learningsuite/schemas.d.ts.map +1 -0
  73. package/dist/scraper/learningsuite/schemas.js +147 -0
  74. package/dist/scraper/learningsuite/schemas.js.map +1 -0
  75. package/dist/scraper/navigator.d.ts +14 -11
  76. package/dist/scraper/navigator.d.ts.map +1 -1
  77. package/dist/scraper/navigator.js +61 -104
  78. package/dist/scraper/navigator.js.map +1 -1
  79. package/dist/scraper/schemas.d.ts +57 -0
  80. package/dist/scraper/schemas.d.ts.map +1 -0
  81. package/dist/scraper/schemas.js +135 -0
  82. package/dist/scraper/schemas.js.map +1 -0
  83. package/dist/scraper/videoInterceptor.d.ts +4 -0
  84. package/dist/scraper/videoInterceptor.d.ts.map +1 -1
  85. package/dist/scraper/videoInterceptor.js +66 -51
  86. package/dist/scraper/videoInterceptor.js.map +1 -1
  87. package/dist/shared/auth.d.ts +9 -9
  88. package/dist/shared/auth.d.ts.map +1 -1
  89. package/dist/shared/auth.js +24 -38
  90. package/dist/shared/auth.js.map +1 -1
  91. package/dist/shared/firebase.d.ts +60 -0
  92. package/dist/shared/firebase.d.ts.map +1 -0
  93. package/dist/shared/firebase.js +102 -0
  94. package/dist/shared/firebase.js.map +1 -0
  95. package/dist/shared/fs.d.ts.map +1 -1
  96. package/dist/shared/fs.js +4 -0
  97. package/dist/shared/fs.js.map +1 -1
  98. package/dist/shared/index.d.ts +3 -0
  99. package/dist/shared/index.d.ts.map +1 -1
  100. package/dist/shared/index.js +3 -0
  101. package/dist/shared/index.js.map +1 -1
  102. package/dist/shared/slug.d.ts +11 -0
  103. package/dist/shared/slug.d.ts.map +1 -0
  104. package/{src/shared/slug.ts → dist/shared/slug.js} +10 -11
  105. package/dist/shared/slug.js.map +1 -0
  106. package/dist/shared/url.d.ts +43 -0
  107. package/dist/shared/url.d.ts.map +1 -0
  108. package/{src/shared/url.ts → dist/shared/url.js} +12 -15
  109. package/dist/shared/url.js.map +1 -0
  110. package/dist/state/database.d.ts +1 -0
  111. package/dist/state/database.d.ts.map +1 -1
  112. package/dist/state/database.js +3 -0
  113. package/dist/state/database.js.map +1 -1
  114. package/dist/storage/fileSystem.d.ts +17 -17
  115. package/dist/storage/fileSystem.d.ts.map +1 -1
  116. package/dist/storage/fileSystem.js +39 -31
  117. package/dist/storage/fileSystem.js.map +1 -1
  118. package/package.json +5 -2
  119. package/.github/workflows/ci.yml +0 -50
  120. package/.husky/commit-msg +0 -2
  121. package/.husky/pre-commit +0 -1
  122. package/.husky/pre-push +0 -3
  123. package/.prettierrc +0 -8
  124. package/.release-it.json +0 -23
  125. package/ARCHITECTURE.md +0 -233
  126. package/CHANGELOG.md +0 -78
  127. package/commitlint.config.js +0 -4
  128. package/dist/ai/openRouter.d.ts +0 -47
  129. package/dist/ai/openRouter.d.ts.map +0 -1
  130. package/dist/ai/openRouter.js +0 -116
  131. package/dist/ai/openRouter.js.map +0 -1
  132. package/dist/ai/transcriptPolisher.d.ts +0 -24
  133. package/dist/ai/transcriptPolisher.d.ts.map +0 -1
  134. package/dist/ai/transcriptPolisher.js +0 -89
  135. package/dist/ai/transcriptPolisher.js.map +0 -1
  136. package/dist/cli/commands/enrich.d.ts +0 -14
  137. package/dist/cli/commands/enrich.d.ts.map +0 -1
  138. package/dist/cli/commands/enrich.js +0 -271
  139. package/dist/cli/commands/enrich.js.map +0 -1
  140. package/dist/cli/commands/syncGhl.d.ts +0 -20
  141. package/dist/cli/commands/syncGhl.d.ts.map +0 -1
  142. package/dist/cli/commands/syncGhl.js +0 -483
  143. package/dist/cli/commands/syncGhl.js.map +0 -1
  144. package/dist/cli/commands/syncHighLevel.test.d.ts +0 -2
  145. package/dist/cli/commands/syncHighLevel.test.d.ts.map +0 -1
  146. package/dist/cli/commands/syncHighLevel.test.js +0 -102
  147. package/dist/cli/commands/syncHighLevel.test.js.map +0 -1
  148. package/dist/config/paths.test.d.ts +0 -2
  149. package/dist/config/paths.test.d.ts.map +0 -1
  150. package/dist/config/paths.test.js +0 -70
  151. package/dist/config/paths.test.js.map +0 -1
  152. package/dist/config/schema.test.d.ts +0 -2
  153. package/dist/config/schema.test.d.ts.map +0 -1
  154. package/dist/config/schema.test.js +0 -151
  155. package/dist/config/schema.test.js.map +0 -1
  156. package/dist/downloader/hlsDownloader.test.d.ts +0 -2
  157. package/dist/downloader/hlsDownloader.test.d.ts.map +0 -1
  158. package/dist/downloader/hlsDownloader.test.js +0 -116
  159. package/dist/downloader/hlsDownloader.test.js.map +0 -1
  160. package/dist/downloader/loomDownloader.test.d.ts +0 -2
  161. package/dist/downloader/loomDownloader.test.d.ts.map +0 -1
  162. package/dist/downloader/loomDownloader.test.js +0 -36
  163. package/dist/downloader/loomDownloader.test.js.map +0 -1
  164. package/dist/downloader/queue.test.d.ts +0 -2
  165. package/dist/downloader/queue.test.d.ts.map +0 -1
  166. package/dist/downloader/queue.test.js +0 -158
  167. package/dist/downloader/queue.test.js.map +0 -1
  168. package/dist/downloader/videoDownloader.d.ts +0 -32
  169. package/dist/downloader/videoDownloader.d.ts.map +0 -1
  170. package/dist/downloader/videoDownloader.js +0 -173
  171. package/dist/downloader/videoDownloader.js.map +0 -1
  172. package/dist/downloader/vimeoDownloader.test.d.ts +0 -2
  173. package/dist/downloader/vimeoDownloader.test.d.ts.map +0 -1
  174. package/dist/downloader/vimeoDownloader.test.js +0 -51
  175. package/dist/downloader/vimeoDownloader.test.js.map +0 -1
  176. package/dist/scraper/auth.d.ts +0 -29
  177. package/dist/scraper/auth.d.ts.map +0 -1
  178. package/dist/scraper/auth.js +0 -115
  179. package/dist/scraper/auth.js.map +0 -1
  180. package/dist/scraper/extractor.test.d.ts +0 -2
  181. package/dist/scraper/extractor.test.d.ts.map +0 -1
  182. package/dist/scraper/extractor.test.js +0 -65
  183. package/dist/scraper/extractor.test.js.map +0 -1
  184. package/dist/scraper/ghl/auth.d.ts +0 -25
  185. package/dist/scraper/ghl/auth.d.ts.map +0 -1
  186. package/dist/scraper/ghl/auth.js +0 -187
  187. package/dist/scraper/ghl/auth.js.map +0 -1
  188. package/dist/scraper/ghl/extractor.d.ts +0 -96
  189. package/dist/scraper/ghl/extractor.d.ts.map +0 -1
  190. package/dist/scraper/ghl/extractor.js +0 -345
  191. package/dist/scraper/ghl/extractor.js.map +0 -1
  192. package/dist/scraper/ghl/index.d.ts +0 -4
  193. package/dist/scraper/ghl/index.d.ts.map +0 -1
  194. package/dist/scraper/ghl/index.js.map +0 -1
  195. package/dist/scraper/ghl/navigator.d.ts +0 -93
  196. package/dist/scraper/ghl/navigator.d.ts.map +0 -1
  197. package/dist/scraper/ghl/navigator.js +0 -447
  198. package/dist/scraper/ghl/navigator.js.map +0 -1
  199. package/dist/scraper/highlevel/auth.d.ts +0 -25
  200. package/dist/scraper/highlevel/auth.d.ts.map +0 -1
  201. package/dist/scraper/highlevel/auth.js +0 -189
  202. package/dist/scraper/highlevel/auth.js.map +0 -1
  203. package/dist/scraper/highlevel/extractor.test.d.ts +0 -2
  204. package/dist/scraper/highlevel/extractor.test.d.ts.map +0 -1
  205. package/dist/scraper/highlevel/extractor.test.js +0 -101
  206. package/dist/scraper/highlevel/extractor.test.js.map +0 -1
  207. package/dist/scraper/highlevel/navigator.test.d.ts +0 -2
  208. package/dist/scraper/highlevel/navigator.test.d.ts.map +0 -1
  209. package/dist/scraper/highlevel/navigator.test.js +0 -78
  210. package/dist/scraper/highlevel/navigator.test.js.map +0 -1
  211. package/dist/scraper/navigator.test.d.ts +0 -2
  212. package/dist/scraper/navigator.test.d.ts.map +0 -1
  213. package/dist/scraper/navigator.test.js +0 -63
  214. package/dist/scraper/navigator.test.js.map +0 -1
  215. package/dist/scraper/skoolApi.d.ts +0 -17
  216. package/dist/scraper/skoolApi.d.ts.map +0 -1
  217. package/dist/scraper/skoolApi.js +0 -72
  218. package/dist/scraper/skoolApi.js.map +0 -1
  219. package/dist/state/database.test.d.ts +0 -2
  220. package/dist/state/database.test.d.ts.map +0 -1
  221. package/dist/state/database.test.js +0 -34
  222. package/dist/state/database.test.js.map +0 -1
  223. package/dist/transcription/whisperService.d.ts +0 -27
  224. package/dist/transcription/whisperService.d.ts.map +0 -1
  225. package/dist/transcription/whisperService.js +0 -102
  226. package/dist/transcription/whisperService.js.map +0 -1
  227. package/eslint.config.js +0 -55
  228. package/src/__fixtures__/highlevel-post-response.json +0 -68
  229. package/src/__fixtures__/hls-master-playlist.m3u8 +0 -24
  230. package/src/cli/commands/__snapshots__/syncHighLevel.test.ts.snap +0 -38
  231. package/src/cli/commands/config.ts +0 -74
  232. package/src/cli/commands/inspect.ts +0 -441
  233. package/src/cli/commands/login.ts +0 -68
  234. package/src/cli/commands/status.ts +0 -147
  235. package/src/cli/commands/sync.ts +0 -1235
  236. package/src/cli/commands/syncHighLevel.test.ts +0 -144
  237. package/src/cli/commands/syncHighLevel.ts +0 -639
  238. package/src/cli/index.ts +0 -121
  239. package/src/config/configManager.ts +0 -75
  240. package/src/config/paths.test.ts +0 -83
  241. package/src/config/paths.ts +0 -36
  242. package/src/config/schema.test.ts +0 -173
  243. package/src/config/schema.ts +0 -65
  244. package/src/downloader/hlsDownloader.test.ts +0 -148
  245. package/src/downloader/hlsDownloader.ts +0 -327
  246. package/src/downloader/hlsValidator.ts +0 -196
  247. package/src/downloader/index.ts +0 -122
  248. package/src/downloader/loomDownloader.test.ts +0 -43
  249. package/src/downloader/loomDownloader.ts +0 -742
  250. package/src/downloader/queue.test.ts +0 -199
  251. package/src/downloader/queue.ts +0 -118
  252. package/src/downloader/vimeoDownloader.test.ts +0 -62
  253. package/src/downloader/vimeoDownloader.ts +0 -722
  254. package/src/scraper/extractor.test.ts +0 -124
  255. package/src/scraper/extractor.ts +0 -757
  256. package/src/scraper/highlevel/__snapshots__/extractor.test.ts.snap +0 -41
  257. package/src/scraper/highlevel/extractor.test.ts +0 -134
  258. package/src/scraper/highlevel/extractor.ts +0 -537
  259. package/src/scraper/highlevel/index.ts +0 -2
  260. package/src/scraper/highlevel/navigator.test.ts +0 -110
  261. package/src/scraper/highlevel/navigator.ts +0 -668
  262. package/src/scraper/highlevel/schemas.ts +0 -183
  263. package/src/scraper/navigator.test.ts +0 -122
  264. package/src/scraper/navigator.ts +0 -355
  265. package/src/scraper/schemas.ts +0 -177
  266. package/src/scraper/videoInterceptor.ts +0 -435
  267. package/src/shared/auth.test.ts +0 -58
  268. package/src/shared/auth.ts +0 -251
  269. package/src/shared/firebase.ts +0 -151
  270. package/src/shared/fs.ts +0 -80
  271. package/src/shared/http.ts +0 -34
  272. package/src/shared/index.ts +0 -6
  273. package/src/shared/url.test.ts +0 -122
  274. package/src/state/database.test.ts +0 -49
  275. package/src/state/database.ts +0 -919
  276. package/src/state/index.ts +0 -14
  277. package/src/storage/fileSystem.test.ts +0 -64
  278. package/src/storage/fileSystem.ts +0 -175
  279. package/tsconfig.json +0 -28
  280. package/vitest.config.ts +0 -29
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Zod schemas for HighLevel API responses.
3
+ * These provide runtime validation and type inference.
4
+ */
5
+ import { z } from "zod";
6
+ // Re-export Firebase auth types (Firebase is used by HighLevel for auth)
7
+ export { FirebaseAuthTokenSchema, } from "../../shared/firebase.js";
8
+ // ============================================================================
9
+ // Portal Settings API
10
+ // ============================================================================
11
+ export const PortalSettingsResponseSchema = z.object({
12
+ locationId: z.string(),
13
+ portalName: z.string().optional(),
14
+ name: z.string().optional(),
15
+ });
16
+ // ============================================================================
17
+ // Video License API
18
+ // ============================================================================
19
+ export const VideoLicenseResponseSchema = z.object({
20
+ url: z.string(),
21
+ token: z.string(),
22
+ });
23
+ // ============================================================================
24
+ // Post Details API
25
+ // ============================================================================
26
+ const VideoAssetSchema = z.object({
27
+ id: z.string().optional(),
28
+ assetId: z.string().optional(),
29
+ assetsLicenseId: z.string().optional(),
30
+ url: z.string().optional(),
31
+ });
32
+ const PosterImageSchema = z.object({
33
+ assetId: z.string().optional(),
34
+ url: z.string().optional(),
35
+ });
36
+ const ContentBlockSchema = z.object({
37
+ type: z.string(),
38
+ id: z.string().optional(),
39
+ assetId: z.string().optional(),
40
+ assetsLicenseId: z.string().optional(),
41
+ url: z.string().optional(),
42
+ });
43
+ const MaterialSchema = z.object({
44
+ id: z.string().optional(),
45
+ name: z.string().optional(),
46
+ url: z.string().optional(),
47
+ type: z.string().optional(),
48
+ });
49
+ export const PostDetailsSchema = z.object({
50
+ title: z.string().optional(),
51
+ description: z.string().nullable().optional(),
52
+ video: VideoAssetSchema.nullable().optional(),
53
+ posterImage: PosterImageSchema.nullable().optional(),
54
+ contentBlock: z.array(ContentBlockSchema).optional(),
55
+ materials: z.array(MaterialSchema).optional(),
56
+ post_materials: z.array(MaterialSchema).optional(),
57
+ });
58
+ // Response can have data directly or wrapped in "post"
59
+ export const PostDetailsResponseSchema = z.object({
60
+ post: PostDetailsSchema.optional(),
61
+ // Also allow all post fields directly on root
62
+ title: z.string().optional(),
63
+ description: z.string().nullable().optional(),
64
+ video: VideoAssetSchema.nullable().optional(),
65
+ posterImage: PosterImageSchema.nullable().optional(),
66
+ contentBlock: z.array(ContentBlockSchema).optional(),
67
+ materials: z.array(MaterialSchema).optional(),
68
+ post_materials: z.array(MaterialSchema).optional(),
69
+ });
70
+ // ============================================================================
71
+ // Categories API
72
+ // ============================================================================
73
+ export const CategorySchema = z.object({
74
+ id: z.string(),
75
+ title: z.string(),
76
+ description: z.string().nullable().optional(),
77
+ position: z.number().optional(),
78
+ postCount: z.number().optional(),
79
+ visibility: z.string().optional(),
80
+ });
81
+ export const CategoriesResponseSchema = z.object({
82
+ categories: z.array(CategorySchema),
83
+ });
84
+ // ============================================================================
85
+ // Posts (Lessons) API
86
+ // ============================================================================
87
+ export const PostSchema = z.object({
88
+ id: z.string(),
89
+ title: z.string(),
90
+ indexPosition: z.number().optional(),
91
+ visibility: z.string().optional(),
92
+ });
93
+ export const PostsResponseSchema = z.object({
94
+ category: z
95
+ .object({
96
+ posts: z.array(PostSchema),
97
+ })
98
+ .optional(),
99
+ });
100
+ // ============================================================================
101
+ // Product (Course) API
102
+ // ============================================================================
103
+ export const ProductSchema = z.object({
104
+ id: z.string().optional(),
105
+ title: z.string(),
106
+ description: z.string().optional(),
107
+ posterImage: z.string().nullable().optional(),
108
+ instructor: z.string().nullable().optional(),
109
+ postCount: z.number().optional(),
110
+ });
111
+ export const ProductResponseSchema = z.object({
112
+ product: ProductSchema.optional(),
113
+ // Also allow fields directly on root
114
+ id: z.string().optional(),
115
+ title: z.string().optional(),
116
+ description: z.string().optional(),
117
+ posterImage: z.string().nullable().optional(),
118
+ instructor: z.string().nullable().optional(),
119
+ postCount: z.number().optional(),
120
+ });
121
+ // ============================================================================
122
+ // Helper: Safe parse with logging
123
+ // ============================================================================
124
+ /**
125
+ * Safely parses data with a Zod schema.
126
+ * Returns the parsed data or null if validation fails.
127
+ * Logs validation errors for debugging.
128
+ */
129
+ export function safeParse(schema, data, context) {
130
+ const result = schema.safeParse(data);
131
+ if (result.success) {
132
+ return result.data;
133
+ }
134
+ if (context) {
135
+ console.warn(`[${context}] Validation failed:`, z.treeifyError(result.error));
136
+ }
137
+ return null;
138
+ }
139
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../src/scraper/highlevel/schemas.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,yEAAyE;AACzE,OAAO,EACL,uBAAuB,GAGxB,MAAM,0BAA0B,CAAC;AAElC,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAIH,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAIH,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,iBAAiB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACpD,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE;IACpD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE;IAC7C,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAEH,uDAAuD;AACvD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IAClC,8CAA8C;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,iBAAiB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACpD,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE;IACpD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE;IAC7C,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAIH,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;CACpC,CAAC,CAAC;AAKH,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,CAAC;SACR,MAAM,CAAC;QACN,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;KAC3B,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC;AAKH,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,OAAO,EAAE,aAAa,CAAC,QAAQ,EAAE;IACjC,qCAAqC;IACrC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAKH,+EAA+E;AAC/E,kCAAkC;AAClC,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAI,MAAoB,EAAE,IAAa,EAAE,OAAgB;IAChF,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,sBAAsB,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { Page } from "playwright";
2
+ export interface LearningSuiteVideoInfo {
3
+ type: "hls" | "vimeo" | "loom" | "youtube" | "wistia" | "native" | "unknown";
4
+ url: string;
5
+ hlsUrl?: string;
6
+ thumbnailUrl?: string;
7
+ duration?: number;
8
+ }
9
+ export interface LearningSuitePostContent {
10
+ id: string;
11
+ title: string;
12
+ description: string | null;
13
+ htmlContent: string | null;
14
+ video: LearningSuiteVideoInfo | null;
15
+ attachments: {
16
+ id: string;
17
+ name: string;
18
+ url: string;
19
+ type: string;
20
+ size?: number;
21
+ }[];
22
+ }
23
+ /**
24
+ * Detects the video type from a URL.
25
+ */
26
+ export declare function detectVideoType(url: string): LearningSuiteVideoInfo["type"];
27
+ /**
28
+ * Extracts video information from a lesson page.
29
+ */
30
+ export declare function extractVideoFromPage(page: Page): Promise<LearningSuiteVideoInfo | null>;
31
+ /**
32
+ * Extracts HTML content from the lesson page using semantic HTML structure.
33
+ * Uses accessibility-friendly selectors: main element, semantic headings, paragraphs, lists.
34
+ * Falls back to data-* attributes which are also stable.
35
+ */
36
+ export declare function extractHtmlContent(page: Page): Promise<string | null>;
37
+ /**
38
+ * Extracts attachments/materials from the lesson page.
39
+ */
40
+ export declare function extractAttachmentsFromPage(page: Page): Promise<LearningSuitePostContent["attachments"]>;
41
+ /**
42
+ * Extracts complete lesson content using DOM-based extraction with network interception.
43
+ * Note: LearningSuite uses persisted GraphQL queries, so we can't make arbitrary API calls.
44
+ */
45
+ export declare function extractLearningSuitePostContent(page: Page, lessonUrl: string, _tenantId: string, _courseId: string, lessonId: string): Promise<LearningSuitePostContent | null>;
46
+ /**
47
+ * Intercepts network requests to capture video URLs during page load.
48
+ */
49
+ export declare function interceptVideoRequests(page: Page, lessonUrl: string): Promise<LearningSuiteVideoInfo | null>;
50
+ //# sourceMappingURL=extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/scraper/learningsuite/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACrC,WAAW,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACL;AAOD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAuB3E;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAkI7F;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkE3E;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,wBAAwB,CAAC,aAAa,CAAC,CAAC,CAuElD;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAqH1C;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAwDxC"}
@@ -0,0 +1,429 @@
1
+ // ============================================================================
2
+ // Browser/API Automation
3
+ // ============================================================================
4
+ /* v8 ignore start */
5
+ /**
6
+ * Detects the video type from a URL.
7
+ */
8
+ export function detectVideoType(url) {
9
+ const lowerUrl = url.toLowerCase();
10
+ if (lowerUrl.includes("vimeo.com") || lowerUrl.includes("player.vimeo")) {
11
+ return "vimeo";
12
+ }
13
+ if (lowerUrl.includes("loom.com")) {
14
+ return "loom";
15
+ }
16
+ if (lowerUrl.includes("youtube.com") || lowerUrl.includes("youtu.be")) {
17
+ return "youtube";
18
+ }
19
+ if (lowerUrl.includes("wistia.com") || lowerUrl.includes("wistia.net")) {
20
+ return "wistia";
21
+ }
22
+ if (lowerUrl.includes(".m3u8")) {
23
+ return "hls";
24
+ }
25
+ if (lowerUrl.includes(".mp4") || lowerUrl.includes(".webm")) {
26
+ return "native";
27
+ }
28
+ return "unknown";
29
+ }
30
+ /**
31
+ * Extracts video information from a lesson page.
32
+ */
33
+ export async function extractVideoFromPage(page) {
34
+ // Check for HLS video
35
+ const hlsUrl = await page.evaluate(() => {
36
+ // Look for video elements with HLS source
37
+ const videos = Array.from(document.querySelectorAll("video"));
38
+ for (const video of videos) {
39
+ const src = video.currentSrc ?? video.src;
40
+ if (src?.includes(".m3u8")) {
41
+ return src;
42
+ }
43
+ }
44
+ // Check for HLS source elements
45
+ const sources = Array.from(document.querySelectorAll('source[type*="m3u8"], source[src*=".m3u8"]'));
46
+ for (const source of sources) {
47
+ const src = source.src;
48
+ if (src)
49
+ return src;
50
+ }
51
+ // Look for HLS URLs in script tags
52
+ const scripts = Array.from(document.querySelectorAll("script"));
53
+ for (const script of scripts) {
54
+ const content = script.textContent ?? "";
55
+ const hlsMatch = /"(https?:\/\/[^"]+\.m3u8[^"]*)"/i.exec(content);
56
+ if (hlsMatch?.[1])
57
+ return hlsMatch[1];
58
+ }
59
+ return null;
60
+ });
61
+ if (hlsUrl) {
62
+ return {
63
+ type: "hls",
64
+ url: hlsUrl,
65
+ hlsUrl,
66
+ };
67
+ }
68
+ // Check for Vimeo embed
69
+ const vimeoUrl = await page.evaluate(() => {
70
+ const iframe = document.querySelector('iframe[src*="vimeo.com"], iframe[src*="player.vimeo"]');
71
+ if (iframe) {
72
+ return iframe.src;
73
+ }
74
+ return null;
75
+ });
76
+ if (vimeoUrl) {
77
+ return {
78
+ type: "vimeo",
79
+ url: vimeoUrl,
80
+ };
81
+ }
82
+ // Check for Loom embed
83
+ const loomUrl = await page.evaluate(() => {
84
+ const iframe = document.querySelector('iframe[src*="loom.com"]');
85
+ if (iframe) {
86
+ return iframe.src;
87
+ }
88
+ return null;
89
+ });
90
+ if (loomUrl) {
91
+ return {
92
+ type: "loom",
93
+ url: loomUrl,
94
+ };
95
+ }
96
+ // Check for YouTube embed
97
+ const youtubeUrl = await page.evaluate(() => {
98
+ const iframe = document.querySelector('iframe[src*="youtube.com"], iframe[src*="youtube-nocookie.com"], iframe[src*="youtu.be"]');
99
+ if (iframe) {
100
+ return iframe.src;
101
+ }
102
+ return null;
103
+ });
104
+ if (youtubeUrl) {
105
+ return {
106
+ type: "youtube",
107
+ url: youtubeUrl,
108
+ };
109
+ }
110
+ // Check for Wistia
111
+ const wistiaInfo = await page.evaluate(() => {
112
+ const wistiaEmbed = document.querySelector('[class*="wistia"]');
113
+ if (wistiaEmbed) {
114
+ const match = /wistia_embed wistia_async_(\w+)/.exec(wistiaEmbed.className);
115
+ if (match?.[1]) {
116
+ return { id: match[1] };
117
+ }
118
+ }
119
+ return null;
120
+ });
121
+ if (wistiaInfo?.id) {
122
+ return {
123
+ type: "wistia",
124
+ url: `https://fast.wistia.net/embed/medias/${wistiaInfo.id}`,
125
+ };
126
+ }
127
+ // Check for native video
128
+ const nativeVideoUrl = await page.evaluate(() => {
129
+ const video = document.querySelector("video");
130
+ if (video) {
131
+ const source = video.querySelector("source");
132
+ const src = source?.src ?? video.src ?? video.currentSrc;
133
+ if (src && !src.includes(".m3u8")) {
134
+ return src;
135
+ }
136
+ }
137
+ return null;
138
+ });
139
+ if (nativeVideoUrl) {
140
+ return {
141
+ type: "native",
142
+ url: nativeVideoUrl,
143
+ };
144
+ }
145
+ return null;
146
+ }
147
+ /**
148
+ * Extracts HTML content from the lesson page using semantic HTML structure.
149
+ * Uses accessibility-friendly selectors: main element, semantic headings, paragraphs, lists.
150
+ * Falls back to data-* attributes which are also stable.
151
+ */
152
+ export async function extractHtmlContent(page) {
153
+ return page.evaluate(() => {
154
+ // Find the main content area (semantic HTML)
155
+ const main = document.querySelector("main");
156
+ if (!main)
157
+ return null;
158
+ // Find content elements using semantic selectors first, then data attributes as fallback
159
+ // Priority: semantic HTML (p, ul, ol in main) > data-slate-node > data-cy attributes
160
+ const contentElements = main.querySelectorAll(
161
+ // Semantic HTML within main
162
+ "p[data-slate-node], ul[data-slate-node], ol[data-slate-node], " +
163
+ // Stable data attributes as fallback
164
+ '[data-cy="paragraph-element"], [data-cy="list-item"]');
165
+ if (contentElements.length > 0) {
166
+ const htmlParts = [];
167
+ const processedTexts = new Set();
168
+ for (const el of Array.from(contentElements)) {
169
+ const tag = el.tagName.toLowerCase();
170
+ const text = el.textContent?.trim() ?? "";
171
+ // Skip empty, duplicate, or very short text
172
+ if (!text || processedTexts.has(text) || text.length < 3)
173
+ continue;
174
+ processedTexts.add(text);
175
+ if (tag === "p") {
176
+ htmlParts.push(`<p>${text}</p>`);
177
+ }
178
+ else if (tag === "ul" || tag === "ol") {
179
+ const items = el.querySelectorAll("li");
180
+ const listItems = Array.from(items)
181
+ .map((li) => li.textContent?.trim() ?? "")
182
+ .filter((t) => t.length > 0)
183
+ .map((t) => `<li>${t}</li>`)
184
+ .join("");
185
+ if (listItems) {
186
+ htmlParts.push(`<${tag}>${listItems}</${tag}>`);
187
+ }
188
+ }
189
+ }
190
+ if (htmlParts.length > 0) {
191
+ return htmlParts.join("\n");
192
+ }
193
+ }
194
+ // Fallback: extract from main, excluding navigation and interactive elements
195
+ const clone = main.cloneNode(true);
196
+ // Remove non-content elements using semantic/role selectors
197
+ const unwanted = clone.querySelectorAll("script, style, nav, video, iframe, svg, button, input, " +
198
+ '[role="navigation"], [role="button"], [role="menuitem"], [role="menu"]');
199
+ unwanted.forEach((el) => {
200
+ el.remove();
201
+ });
202
+ const text = clone.textContent?.trim();
203
+ if (text && text.length > 50) {
204
+ return `<p>${text}</p>`;
205
+ }
206
+ return null;
207
+ });
208
+ }
209
+ /**
210
+ * Extracts attachments/materials from the lesson page.
211
+ */
212
+ export async function extractAttachmentsFromPage(page) {
213
+ return page.evaluate(() => {
214
+ const attachments = [];
215
+ // Look for download links - include storage URLs (Google Cloud Storage)
216
+ const downloadLinks = document.querySelectorAll('a[download], a[href*=".pdf"], a[href*=".doc"], a[href*=".xls"], a[href*=".ppt"], a[href*=".zip"], a[href*="storage.googleapis.com"], a[href*="storage.cloud.google"]');
217
+ const seen = new Set();
218
+ for (const link of Array.from(downloadLinks)) {
219
+ const anchor = link;
220
+ const url = anchor.href;
221
+ if (!url || seen.has(url))
222
+ continue;
223
+ // Skip non-file URLs
224
+ if (url.startsWith("javascript:") || url.startsWith("#"))
225
+ continue;
226
+ seen.add(url);
227
+ // Get filename from download attribute, text content, or URL
228
+ let name = anchor.download || "";
229
+ if (!name) {
230
+ // Try to get name from visible text (often the file name is shown)
231
+ const textContent = anchor.textContent?.trim() ?? "";
232
+ if (textContent?.includes(".")) {
233
+ name = textContent;
234
+ }
235
+ }
236
+ if (!name) {
237
+ // Extract from URL, handling encoded characters
238
+ const urlParts = url.split("/");
239
+ const lastPart = urlParts[urlParts.length - 1]?.split("?")[0] ?? "";
240
+ try {
241
+ name = decodeURIComponent(lastPart);
242
+ }
243
+ catch {
244
+ name = lastPart;
245
+ }
246
+ }
247
+ if (!name) {
248
+ name = "attachment";
249
+ }
250
+ // Determine type from extension
251
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
252
+ let type = "file";
253
+ if (["pdf"].includes(ext))
254
+ type = "pdf";
255
+ else if (["doc", "docx"].includes(ext))
256
+ type = "document";
257
+ else if (["xls", "xlsx"].includes(ext))
258
+ type = "spreadsheet";
259
+ else if (["ppt", "pptx"].includes(ext))
260
+ type = "presentation";
261
+ else if (["zip", "rar", "7z"].includes(ext))
262
+ type = "archive";
263
+ else if (["jpg", "jpeg", "png", "gif", "webp"].includes(ext))
264
+ type = "image";
265
+ attachments.push({
266
+ id: `attachment-${attachments.length}`,
267
+ name,
268
+ url,
269
+ type,
270
+ });
271
+ }
272
+ return attachments;
273
+ });
274
+ }
275
+ /**
276
+ * Extracts complete lesson content using DOM-based extraction with network interception.
277
+ * Note: LearningSuite uses persisted GraphQL queries, so we can't make arbitrary API calls.
278
+ */
279
+ export async function extractLearningSuitePostContent(page, lessonUrl, _tenantId, _courseId, lessonId) {
280
+ // Set up request interception to capture HLS video URLs
281
+ const hlsUrls = [];
282
+ const requestHandler = (request) => {
283
+ const url = request.url();
284
+ // Capture HLS playlists from Bunny API or direct m3u8
285
+ if (url.includes("/playlist/master") || url.includes(".m3u8")) {
286
+ hlsUrls.push(url);
287
+ }
288
+ };
289
+ page.on("request", requestHandler);
290
+ // Navigate to lesson page
291
+ await page.goto(lessonUrl, { timeout: 30000 });
292
+ await page.waitForLoadState("domcontentloaded");
293
+ // Wait for video player to appear (if any)
294
+ const hasVideoPlayer = await page
295
+ .locator("video, [class*='video'], [class*='Video'], [class*='player'], [class*='Player']")
296
+ .first()
297
+ .waitFor({ state: "attached", timeout: 5000 })
298
+ .then(() => true)
299
+ .catch(() => false);
300
+ // If video player exists but no HLS URL captured yet, try to trigger video load
301
+ if (hasVideoPlayer && hlsUrls.length === 0) {
302
+ // Try clicking play button or video element to trigger load
303
+ const playButton = page.locator('[aria-label*="play" i], [aria-label*="Play" i], [class*="play" i], button[class*="Play"], video');
304
+ try {
305
+ await playButton.first().click({ timeout: 2000 });
306
+ // Wait for HLS URL to be captured after clicking play
307
+ await page.waitForTimeout(2000);
308
+ }
309
+ catch {
310
+ // Play button not found or not clickable, continue anyway
311
+ }
312
+ }
313
+ // Give a bit more time for lazy-loaded videos
314
+ if (hlsUrls.length === 0 && hasVideoPlayer) {
315
+ await page.waitForTimeout(2000);
316
+ }
317
+ // Remove handler
318
+ page.off("request", requestHandler);
319
+ // Try to get video from intercepted requests first
320
+ let video = null;
321
+ const masterPlaylist = hlsUrls.find((url) => url.includes("/playlist/master"));
322
+ if (masterPlaylist) {
323
+ video = {
324
+ type: "hls",
325
+ url: masterPlaylist,
326
+ hlsUrl: masterPlaylist,
327
+ };
328
+ }
329
+ else if (hlsUrls.length > 0 && hlsUrls[0]) {
330
+ video = {
331
+ type: "hls",
332
+ url: hlsUrls[0],
333
+ hlsUrl: hlsUrls[0],
334
+ };
335
+ }
336
+ // Fallback to DOM extraction if no HLS found
337
+ video ??= await extractVideoFromPage(page);
338
+ const htmlContent = await extractHtmlContent(page);
339
+ const attachments = await extractAttachmentsFromPage(page);
340
+ // Get title from page using semantic HTML structure
341
+ // The lesson title is typically an h3 within the main element
342
+ const title = await page.evaluate(() => {
343
+ const main = document.querySelector("main");
344
+ // Find h3 heading within main (lesson title is usually h3)
345
+ if (main) {
346
+ const h3 = main.querySelector("h3");
347
+ if (h3?.textContent?.trim()) {
348
+ return h3.textContent.trim();
349
+ }
350
+ }
351
+ // Try breadcrumb navigation (last item is the current page)
352
+ const breadcrumb = document.querySelector('nav[aria-label*="breadcrumb"], [role="navigation"] li:last-child');
353
+ if (breadcrumb?.textContent?.trim()) {
354
+ return breadcrumb.textContent.trim();
355
+ }
356
+ // Try any h3 on the page
357
+ const h3 = document.querySelector("h3");
358
+ if (h3?.textContent?.trim()) {
359
+ return h3.textContent.trim();
360
+ }
361
+ // Try h1 as fallback
362
+ const h1 = document.querySelector("h1");
363
+ if (h1?.textContent?.trim()) {
364
+ return h1.textContent.trim();
365
+ }
366
+ // Use page title as last resort
367
+ return document.title.split(" - ")[0] ?? "Untitled";
368
+ });
369
+ return {
370
+ id: lessonId,
371
+ title,
372
+ description: null,
373
+ htmlContent,
374
+ video,
375
+ attachments,
376
+ };
377
+ }
378
+ /**
379
+ * Intercepts network requests to capture video URLs during page load.
380
+ */
381
+ export async function interceptVideoRequests(page, lessonUrl) {
382
+ const hlsUrls = [];
383
+ const videoUrls = [];
384
+ // Set up request interception
385
+ const requestHandler = (request) => {
386
+ const url = request.url();
387
+ // Capture HLS playlists
388
+ if (url.includes(".m3u8") || url.includes("master.m3u8")) {
389
+ hlsUrls.push(url);
390
+ }
391
+ // Capture video files
392
+ if (url.includes(".mp4") || url.includes(".webm")) {
393
+ videoUrls.push(url);
394
+ }
395
+ };
396
+ page.on("request", requestHandler);
397
+ // Navigate to the lesson
398
+ await page.goto(lessonUrl, { timeout: 30000 });
399
+ await page.waitForLoadState("domcontentloaded");
400
+ await page.waitForTimeout(3000);
401
+ // Remove handler
402
+ page.off("request", requestHandler);
403
+ // Return the best URL found
404
+ const masterPlaylist = hlsUrls.find((url) => url.includes("master.m3u8"));
405
+ if (masterPlaylist) {
406
+ return {
407
+ type: "hls",
408
+ url: masterPlaylist,
409
+ hlsUrl: masterPlaylist,
410
+ };
411
+ }
412
+ if (hlsUrls.length > 0 && hlsUrls[0]) {
413
+ return {
414
+ type: "hls",
415
+ url: hlsUrls[0],
416
+ hlsUrl: hlsUrls[0],
417
+ };
418
+ }
419
+ if (videoUrls.length > 0 && videoUrls[0]) {
420
+ return {
421
+ type: "native",
422
+ url: videoUrls[0],
423
+ };
424
+ }
425
+ // Fallback to DOM extraction
426
+ return extractVideoFromPage(page);
427
+ }
428
+ /* v8 ignore stop */
429
+ //# sourceMappingURL=extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../../src/scraper/learningsuite/extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from "./navigator.js";
2
+ export * from "./extractor.js";
3
+ export * from "./schemas.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scraper/learningsuite/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}