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 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,SAAS,EACT,oBAAoB,EACpB,QAAQ,EACR,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { CourseDatabase, LessonStatus, VideoType, extractCommunitySlug, getDbDir, getDbPath, } from "./database.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,SAAS,EACT,oBAAoB,EACpB,QAAQ,EACR,SAAS,GAOV,MAAM,eAAe,CAAC"}
@@ -0,0 +1,56 @@
1
+ import type { CourseSyncState } from "../config/schema.js";
2
+ /**
3
+ * Gets the base filename for a lesson (without extension).
4
+ * Format: "01-lesson-name"
5
+ */
6
+ export declare function getLessonBasename(lessonIndex: number, lessonName: string): string;
7
+ /**
8
+ * Gets the video file path for a lesson.
9
+ * Videos are stored directly in the module directory with lesson name.
10
+ */
11
+ export declare function getVideoPath(moduleDir: string, lessonIndex: number, lessonName: string): string;
12
+ /**
13
+ * Gets the markdown file path for a lesson.
14
+ * Markdown files are stored directly in the module directory with lesson name.
15
+ */
16
+ export declare function getMarkdownPath(moduleDir: string, lessonIndex: number, lessonName: string): string;
17
+ /**
18
+ * Gets the path for a downloadable file.
19
+ * Files are stored in the module directory with lesson prefix.
20
+ */
21
+ export declare function getDownloadFilePath(moduleDir: string, lessonIndex: number, lessonName: string, filename: string): string;
22
+ /**
23
+ * Creates the output directory structure for a course.
24
+ */
25
+ export declare function createCourseDirectory(outputBase: string, courseName: string): Promise<string>;
26
+ /**
27
+ * Creates a module directory within a course.
28
+ */
29
+ export declare function createModuleDirectory(courseDir: string, moduleIndex: number, moduleName: string): Promise<string>;
30
+ /**
31
+ * Saves markdown content to a file.
32
+ */
33
+ export declare function saveMarkdown(directory: string, filename: string, content: string): Promise<string>;
34
+ /**
35
+ * Loads the sync state for a course.
36
+ */
37
+ export declare function loadSyncState(courseSlug: string): Promise<CourseSyncState | null>;
38
+ /**
39
+ * Saves the sync state for a course.
40
+ */
41
+ export declare function saveSyncState(courseSlug: string, state: CourseSyncState): Promise<void>;
42
+ /**
43
+ * Checks if a lesson has been fully synced.
44
+ */
45
+ export declare function isLessonSynced(moduleDir: string, lessonIndex: number, lessonName: string): Promise<{
46
+ video: boolean;
47
+ content: boolean;
48
+ }>;
49
+ /**
50
+ * Downloads a file from a URL to the specified path.
51
+ */
52
+ export declare function downloadFile(url: string, outputPath: string): Promise<{
53
+ success: boolean;
54
+ error?: string;
55
+ }>;
56
+ //# sourceMappingURL=fileSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileSystem.d.ts","sourceRoot":"","sources":["../../src/storage/fileSystem.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAU3D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CAKR;AAOD;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAavF;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7F;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAM/C;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB/C"}
@@ -0,0 +1,129 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Readable } from "node:stream";
4
+ import { pipeline } from "node:stream/promises";
5
+ import { expandPath, getSyncStatePath } from "../config/paths.js";
6
+ import { courseSyncStateSchema } from "../config/schema.js";
7
+ import { createFolderName } from "../scraper/navigator.js";
8
+ import { ensureDir, outputFile, pathExists, readJson, outputJson } from "../shared/fs.js";
9
+ import { http } from "../shared/http.js";
10
+ // ============================================
11
+ // Pure functions - testable without mocking
12
+ // ============================================
13
+ /**
14
+ * Gets the base filename for a lesson (without extension).
15
+ * Format: "01-lesson-name"
16
+ */
17
+ export function getLessonBasename(lessonIndex, lessonName) {
18
+ return createFolderName(lessonIndex, lessonName);
19
+ }
20
+ /**
21
+ * Gets the video file path for a lesson.
22
+ * Videos are stored directly in the module directory with lesson name.
23
+ */
24
+ export function getVideoPath(moduleDir, lessonIndex, lessonName) {
25
+ return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.mp4`);
26
+ }
27
+ /**
28
+ * Gets the markdown file path for a lesson.
29
+ * Markdown files are stored directly in the module directory with lesson name.
30
+ */
31
+ export function getMarkdownPath(moduleDir, lessonIndex, lessonName) {
32
+ return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.md`);
33
+ }
34
+ /**
35
+ * Gets the path for a downloadable file.
36
+ * Files are stored in the module directory with lesson prefix.
37
+ */
38
+ export function getDownloadFilePath(moduleDir, lessonIndex, lessonName, filename) {
39
+ const lessonPrefix = getLessonBasename(lessonIndex, lessonName);
40
+ // Sanitize filename
41
+ const safeFilename = filename.replace(/[<>:"/\\|?*]/g, "_");
42
+ return join(moduleDir, `${lessonPrefix}-${safeFilename}`);
43
+ }
44
+ // ============================================
45
+ // I/O functions - require filesystem access
46
+ // ============================================
47
+ /* v8 ignore start */
48
+ /**
49
+ * Creates the output directory structure for a course.
50
+ */
51
+ export async function createCourseDirectory(outputBase, courseName) {
52
+ const expanded = expandPath(outputBase);
53
+ const courseDir = join(expanded, createFolderName(0, courseName).replace(/^\d+-/, ""));
54
+ await ensureDir(courseDir);
55
+ return courseDir;
56
+ }
57
+ /**
58
+ * Creates a module directory within a course.
59
+ */
60
+ export async function createModuleDirectory(courseDir, moduleIndex, moduleName) {
61
+ const moduleDir = join(courseDir, createFolderName(moduleIndex, moduleName));
62
+ await ensureDir(moduleDir);
63
+ return moduleDir;
64
+ }
65
+ /**
66
+ * Saves markdown content to a file.
67
+ */
68
+ export async function saveMarkdown(directory, filename, content) {
69
+ const filePath = join(directory, filename);
70
+ await outputFile(filePath, content);
71
+ return filePath;
72
+ }
73
+ /**
74
+ * Loads the sync state for a course.
75
+ */
76
+ export async function loadSyncState(courseSlug) {
77
+ const statePath = getSyncStatePath(courseSlug);
78
+ const data = await readJson(statePath);
79
+ if (!data) {
80
+ return null;
81
+ }
82
+ try {
83
+ return courseSyncStateSchema.parse(data);
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Saves the sync state for a course.
91
+ */
92
+ export async function saveSyncState(courseSlug, state) {
93
+ const statePath = getSyncStatePath(courseSlug);
94
+ await outputJson(statePath, state);
95
+ }
96
+ /**
97
+ * Checks if a lesson has been fully synced.
98
+ */
99
+ export async function isLessonSynced(moduleDir, lessonIndex, lessonName) {
100
+ const [video, content] = await Promise.all([
101
+ pathExists(getVideoPath(moduleDir, lessonIndex, lessonName)),
102
+ pathExists(getMarkdownPath(moduleDir, lessonIndex, lessonName)),
103
+ ]);
104
+ return { video, content };
105
+ }
106
+ /**
107
+ * Downloads a file from a URL to the specified path.
108
+ */
109
+ export async function downloadFile(url, outputPath) {
110
+ if (await pathExists(outputPath)) {
111
+ return { success: true }; // Already downloaded
112
+ }
113
+ await ensureDir(join(outputPath, ".."));
114
+ try {
115
+ const response = await http.get(url);
116
+ const body = response.body;
117
+ if (!body) {
118
+ return { success: false, error: "No response body" };
119
+ }
120
+ const fileStream = createWriteStream(outputPath);
121
+ await pipeline(Readable.fromWeb(body), fileStream);
122
+ return { success: true };
123
+ }
124
+ catch (error) {
125
+ return { success: false, error: String(error) };
126
+ }
127
+ }
128
+ /* v8 ignore stop */
129
+ //# sourceMappingURL=fileSystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileSystem.js","sourceRoot":"","sources":["../../src/storage/fileSystem.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "offcourse",
3
- "version": "0.0.2",
4
- "description": "Download online courses for offline access – of course! πŸ“š",
3
+ "version": "1.0.1",
4
+ "description": "Download online courses for offline access – of course!",
5
5
  "type": "module",
6
6
  "bin": {
7
- "offcourse": "./cli.js"
7
+ "offcourse": "./dist/cli/index.js"
8
8
  },
9
+ "files": [
10
+ "dist"
11
+ ],
9
12
  "repository": {
10
13
  "type": "git",
11
14
  "url": "git+https://github.com/sebastian-software/offcourse.git"
@@ -13,21 +16,78 @@
13
16
  "bugs": {
14
17
  "url": "https://github.com/sebastian-software/offcourse/issues"
15
18
  },
16
- "homepage": "https://github.com/sebastian-software/offcourse",
19
+ "homepage": "https://offcourse.app",
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.build.json",
22
+ "dev": "tsc --watch",
23
+ "start": "node dist/cli/index.js",
24
+ "lint": "eslint src",
25
+ "lint:fix": "eslint src --fix",
26
+ "format": "prettier --write \"src/**/*.ts\"",
27
+ "format:check": "prettier --check \"src/**/*.ts\"",
28
+ "test": "vitest",
29
+ "test:coverage": "vitest run --coverage",
30
+ "typecheck": "tsc --noEmit",
31
+ "release": "release-it",
32
+ "prepare": "husky"
33
+ },
34
+ "lint-staged": {
35
+ "src/**/*.ts": [
36
+ "prettier --write"
37
+ ]
38
+ },
17
39
  "keywords": [
18
40
  "course",
19
41
  "downloader",
20
42
  "offline",
21
43
  "cli",
22
44
  "e-learning",
23
- "video",
24
- "skool",
25
- "loom"
45
+ "video"
26
46
  ],
27
- "author": "Sebastian Software GmbH",
47
+ "author": "",
28
48
  "license": "MIT",
29
49
  "engines": {
30
- "node": ">=18.0.0"
31
- }
50
+ "node": ">=22.0.0"
51
+ },
52
+ "dependencies": {
53
+ "@sindresorhus/slugify": "^3.0.0",
54
+ "better-sqlite3": "^12.5.0",
55
+ "chalk": "^5.6.2",
56
+ "cli-progress": "^3.12.0",
57
+ "commander": "^14.0.2",
58
+ "conf": "^15.0.2",
59
+ "delay": "^7.0.0",
60
+ "execa": "^9.6.1",
61
+ "hls-parser": "^0.16.0",
62
+ "ky": "^1.14.1",
63
+ "ora": "^9.0.0",
64
+ "p-queue": "^9.0.1",
65
+ "p-retry": "^7.1.1",
66
+ "playwright": "^1.57.0",
67
+ "turndown": "^7.2.2",
68
+ "untildify": "^6.0.0",
69
+ "zod": "^4.2.1"
70
+ },
71
+ "devDependencies": {
72
+ "@commitlint/cli": "^20.2.0",
73
+ "@commitlint/config-conventional": "^20.2.0",
74
+ "@release-it/conventional-changelog": "^10.0.4",
75
+ "@types/better-sqlite3": "^7.6.13",
76
+ "@types/cli-progress": "^3.11.6",
77
+ "@types/node": "^22.10.2",
78
+ "@types/turndown": "^5.0.6",
79
+ "@vitest/coverage-v8": "^4.0.16",
80
+ "conventional-changelog-conventionalcommits": "^9.1.0",
81
+ "eslint": "^9.39.2",
82
+ "eslint-config-prettier": "^10.1.8",
83
+ "husky": "^9.1.7",
84
+ "lint-staged": "^16.2.7",
85
+ "prettier": "^3.7.4",
86
+ "release-it": "^19.1.0",
87
+ "tsx": "^4.21.0",
88
+ "typescript": "^5.9.3",
89
+ "typescript-eslint": "^8.50.1",
90
+ "vitest": "^4.0.16"
91
+ },
92
+ "packageManager": "pnpm@10.26.1+sha512.664074abc367d2c9324fdc18037097ce0a8f126034160f709928e9e9f95d98714347044e5c3164d65bd5da6c59c6be362b107546292a8eecb7999196e5ce58fa"
32
93
  }
33
-
package/cli.js DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const VERSION = "0.0.2";
4
-
5
- const banner = `
6
- ╔═══════════════════════════════════════════════════════════════╗
7
- β•‘ β•‘
8
- β•‘ β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”¬ β”¬β”¬β”€β”β”Œβ”€β”β”Œβ”€β” β•‘
9
- β•‘ β”‚ β”‚β”œβ”€ β”œβ”€ β”‚ β”‚ β”‚β”‚ β”‚β”œβ”¬β”˜β””β”€β”β”œβ”€ β•‘
10
- β•‘ β””β”€β”˜β”” β”” β””β”€β”˜β””β”€β”˜β””β”€β”˜β”΄β””β”€β””β”€β”˜β””β”€β”˜ β•‘
11
- β•‘ β•‘
12
- β•‘ Download online courses for offline access – of course! πŸ“š β•‘
13
- β•‘ β•‘
14
- β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
15
- `;
16
-
17
- const comingSoon = `
18
- 🚧 Coming Soon!
19
-
20
- This package is currently in private development.
21
- The full release will include:
22
-
23
- β€’ πŸ” Browser-based authentication with session caching
24
- β€’ πŸ“š Course structure preservation (module/lesson hierarchy)
25
- β€’ 🎬 Video downloads (Loom, Vimeo, YouTube, Wistia)
26
- β€’ πŸ“ Content extraction to clean Markdown
27
- β€’ ⏸️ Resumable syncs
28
- β€’ ⚑ Concurrent downloads
29
-
30
- Supported platforms:
31
- β€’ Skool.com (ready)
32
- β€’ LearningSuite.io (planned)
33
-
34
- Follow the project:
35
- β†’ https://github.com/sebastian-software/offcourse
36
-
37
- `;
38
-
39
- console.log(banner);
40
- console.log(comingSoon);
41
-
42
- if (process.argv.includes("--version") || process.argv.includes("-v")) {
43
- console.log(`v${VERSION}`);
44
- }
45
-