offcourse 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +50 -0
- package/.husky/commit-msg +2 -0
- package/.husky/pre-commit +1 -0
- package/.husky/pre-push +3 -0
- package/.prettierrc +8 -0
- package/.release-it.json +23 -0
- package/ARCHITECTURE.md +233 -0
- package/CHANGELOG.md +78 -0
- package/README.md +255 -20
- package/commitlint.config.js +4 -0
- package/dist/ai/openRouter.d.ts +47 -0
- package/dist/ai/openRouter.d.ts.map +1 -0
- package/dist/ai/openRouter.js +116 -0
- package/dist/ai/openRouter.js.map +1 -0
- package/dist/ai/transcriptPolisher.d.ts +24 -0
- package/dist/ai/transcriptPolisher.d.ts.map +1 -0
- package/dist/ai/transcriptPolisher.js +89 -0
- package/dist/ai/transcriptPolisher.js.map +1 -0
- package/dist/cli/commands/config.d.ts +13 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +66 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/enrich.d.ts +14 -0
- package/dist/cli/commands/enrich.d.ts.map +1 -0
- package/dist/cli/commands/enrich.js +271 -0
- package/dist/cli/commands/enrich.js.map +1 -0
- package/dist/cli/commands/inspect.d.ts +11 -0
- package/dist/cli/commands/inspect.d.ts.map +1 -0
- package/dist/cli/commands/inspect.js +365 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/login.d.ts +12 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +55 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/status.d.ts +15 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +118 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +16 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +922 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/syncGhl.d.ts +20 -0
- package/dist/cli/commands/syncGhl.d.ts.map +1 -0
- package/dist/cli/commands/syncGhl.js +483 -0
- package/dist/cli/commands/syncGhl.js.map +1 -0
- package/dist/cli/commands/syncHighLevel.d.ts +24 -0
- package/dist/cli/commands/syncHighLevel.d.ts.map +1 -0
- package/dist/cli/commands/syncHighLevel.js +483 -0
- package/dist/cli/commands/syncHighLevel.js.map +1 -0
- package/dist/cli/commands/syncHighLevel.test.d.ts +2 -0
- package/dist/cli/commands/syncHighLevel.test.d.ts.map +1 -0
- package/dist/cli/commands/syncHighLevel.test.js +102 -0
- package/dist/cli/commands/syncHighLevel.test.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +106 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/configManager.d.ts +31 -0
- package/dist/config/configManager.d.ts.map +1 -0
- package/dist/config/configManager.js +64 -0
- package/dist/config/configManager.js.map +1 -0
- package/dist/config/paths.d.ts +21 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +33 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/paths.test.d.ts +2 -0
- package/dist/config/paths.test.d.ts.map +1 -0
- package/dist/config/paths.test.js +70 -0
- package/dist/config/paths.test.js.map +1 -0
- package/dist/config/schema.d.ts +60 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +50 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/schema.test.d.ts +2 -0
- package/dist/config/schema.test.d.ts.map +1 -0
- package/dist/config/schema.test.js +151 -0
- package/dist/config/schema.test.js.map +1 -0
- package/dist/downloader/hlsDownloader.d.ts +58 -0
- package/dist/downloader/hlsDownloader.d.ts.map +1 -0
- package/dist/downloader/hlsDownloader.js +254 -0
- package/dist/downloader/hlsDownloader.js.map +1 -0
- package/dist/downloader/hlsDownloader.test.d.ts +2 -0
- package/dist/downloader/hlsDownloader.test.d.ts.map +1 -0
- package/dist/downloader/hlsDownloader.test.js +116 -0
- package/dist/downloader/hlsDownloader.test.js.map +1 -0
- package/dist/downloader/hlsValidator.d.ts +35 -0
- package/dist/downloader/hlsValidator.d.ts.map +1 -0
- package/dist/downloader/hlsValidator.js +148 -0
- package/dist/downloader/hlsValidator.js.map +1 -0
- package/dist/downloader/index.d.ts +26 -0
- package/dist/downloader/index.d.ts.map +1 -0
- package/dist/downloader/index.js +52 -0
- package/dist/downloader/index.js.map +1 -0
- package/dist/downloader/loomDownloader.d.ts +56 -0
- package/dist/downloader/loomDownloader.d.ts.map +1 -0
- package/dist/downloader/loomDownloader.js +559 -0
- package/dist/downloader/loomDownloader.js.map +1 -0
- package/dist/downloader/loomDownloader.test.d.ts +2 -0
- package/dist/downloader/loomDownloader.test.d.ts.map +1 -0
- package/dist/downloader/loomDownloader.test.js +36 -0
- package/dist/downloader/loomDownloader.test.js.map +1 -0
- package/dist/downloader/queue.d.ts +56 -0
- package/dist/downloader/queue.d.ts.map +1 -0
- package/dist/downloader/queue.js +88 -0
- package/dist/downloader/queue.js.map +1 -0
- package/dist/downloader/queue.test.d.ts +2 -0
- package/dist/downloader/queue.test.d.ts.map +1 -0
- package/dist/downloader/queue.test.js +158 -0
- package/dist/downloader/queue.test.js.map +1 -0
- package/dist/downloader/videoDownloader.d.ts +32 -0
- package/dist/downloader/videoDownloader.d.ts.map +1 -0
- package/dist/downloader/videoDownloader.js +173 -0
- package/dist/downloader/videoDownloader.js.map +1 -0
- package/dist/downloader/vimeoDownloader.d.ts +52 -0
- package/dist/downloader/vimeoDownloader.d.ts.map +1 -0
- package/dist/downloader/vimeoDownloader.js +565 -0
- package/dist/downloader/vimeoDownloader.js.map +1 -0
- package/dist/downloader/vimeoDownloader.test.d.ts +2 -0
- package/dist/downloader/vimeoDownloader.test.d.ts.map +1 -0
- package/dist/downloader/vimeoDownloader.test.js +51 -0
- package/dist/downloader/vimeoDownloader.test.js.map +1 -0
- package/dist/scraper/auth.d.ts +29 -0
- package/dist/scraper/auth.d.ts.map +1 -0
- package/dist/scraper/auth.js +115 -0
- package/dist/scraper/auth.js.map +1 -0
- package/dist/scraper/extractor.d.ts +49 -0
- package/dist/scraper/extractor.d.ts.map +1 -0
- package/dist/scraper/extractor.js +627 -0
- package/dist/scraper/extractor.js.map +1 -0
- package/dist/scraper/extractor.test.d.ts +2 -0
- package/dist/scraper/extractor.test.d.ts.map +1 -0
- package/dist/scraper/extractor.test.js +65 -0
- package/dist/scraper/extractor.test.js.map +1 -0
- package/dist/scraper/ghl/auth.d.ts +25 -0
- package/dist/scraper/ghl/auth.d.ts.map +1 -0
- package/dist/scraper/ghl/auth.js +187 -0
- package/dist/scraper/ghl/auth.js.map +1 -0
- package/dist/scraper/ghl/extractor.d.ts +96 -0
- package/dist/scraper/ghl/extractor.d.ts.map +1 -0
- package/dist/scraper/ghl/extractor.js +345 -0
- package/dist/scraper/ghl/extractor.js.map +1 -0
- package/dist/scraper/ghl/index.d.ts +4 -0
- package/dist/scraper/ghl/index.d.ts.map +1 -0
- package/dist/scraper/ghl/index.js +4 -0
- package/dist/scraper/ghl/index.js.map +1 -0
- package/dist/scraper/ghl/navigator.d.ts +93 -0
- package/dist/scraper/ghl/navigator.d.ts.map +1 -0
- package/dist/scraper/ghl/navigator.js +447 -0
- package/dist/scraper/ghl/navigator.js.map +1 -0
- package/dist/scraper/highlevel/auth.d.ts +25 -0
- package/dist/scraper/highlevel/auth.d.ts.map +1 -0
- package/dist/scraper/highlevel/auth.js +189 -0
- package/dist/scraper/highlevel/auth.js.map +1 -0
- package/dist/scraper/highlevel/extractor.d.ts +97 -0
- package/dist/scraper/highlevel/extractor.d.ts.map +1 -0
- package/dist/scraper/highlevel/extractor.js +386 -0
- package/dist/scraper/highlevel/extractor.js.map +1 -0
- package/dist/scraper/highlevel/extractor.test.d.ts +2 -0
- package/dist/scraper/highlevel/extractor.test.d.ts.map +1 -0
- package/dist/scraper/highlevel/extractor.test.js +101 -0
- package/dist/scraper/highlevel/extractor.test.js.map +1 -0
- package/dist/scraper/highlevel/index.d.ts +3 -0
- package/dist/scraper/highlevel/index.d.ts.map +1 -0
- package/dist/scraper/highlevel/index.js +3 -0
- package/dist/scraper/highlevel/index.js.map +1 -0
- package/dist/scraper/highlevel/navigator.d.ts +93 -0
- package/dist/scraper/highlevel/navigator.d.ts.map +1 -0
- package/dist/scraper/highlevel/navigator.js +492 -0
- package/dist/scraper/highlevel/navigator.js.map +1 -0
- package/dist/scraper/highlevel/navigator.test.d.ts +2 -0
- package/dist/scraper/highlevel/navigator.test.d.ts.map +1 -0
- package/dist/scraper/highlevel/navigator.test.js +78 -0
- package/dist/scraper/highlevel/navigator.test.js.map +1 -0
- package/dist/scraper/navigator.d.ts +65 -0
- package/dist/scraper/navigator.d.ts.map +1 -0
- package/dist/scraper/navigator.js +300 -0
- package/dist/scraper/navigator.js.map +1 -0
- package/dist/scraper/navigator.test.d.ts +2 -0
- package/dist/scraper/navigator.test.d.ts.map +1 -0
- package/dist/scraper/navigator.test.js +63 -0
- package/dist/scraper/navigator.test.js.map +1 -0
- package/dist/scraper/skoolApi.d.ts +17 -0
- package/dist/scraper/skoolApi.d.ts.map +1 -0
- package/dist/scraper/skoolApi.js +72 -0
- package/dist/scraper/skoolApi.js.map +1 -0
- package/dist/scraper/videoInterceptor.d.ts +19 -0
- package/dist/scraper/videoInterceptor.d.ts.map +1 -0
- package/dist/scraper/videoInterceptor.js +315 -0
- package/dist/scraper/videoInterceptor.js.map +1 -0
- package/dist/shared/auth.d.ts +58 -0
- package/dist/shared/auth.d.ts.map +1 -0
- package/dist/shared/auth.js +211 -0
- package/dist/shared/auth.js.map +1 -0
- package/dist/shared/fs.d.ts +31 -0
- package/dist/shared/fs.d.ts.map +1 -0
- package/dist/shared/fs.js +73 -0
- package/dist/shared/fs.js.map +1 -0
- package/dist/shared/http.d.ts +15 -0
- package/dist/shared/http.d.ts.map +1 -0
- package/dist/shared/http.js +31 -0
- package/dist/shared/http.js.map +1 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/state/database.d.ts +245 -0
- package/dist/state/database.d.ts.map +1 -0
- package/dist/state/database.js +676 -0
- package/dist/state/database.js.map +1 -0
- package/dist/state/database.test.d.ts +2 -0
- package/dist/state/database.test.d.ts.map +1 -0
- package/dist/state/database.test.js +34 -0
- package/dist/state/database.test.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +2 -0
- package/dist/state/index.js.map +1 -0
- package/dist/storage/fileSystem.d.ts +56 -0
- package/dist/storage/fileSystem.d.ts.map +1 -0
- package/dist/storage/fileSystem.js +121 -0
- package/dist/storage/fileSystem.js.map +1 -0
- package/dist/transcription/whisperService.d.ts +27 -0
- package/dist/transcription/whisperService.d.ts.map +1 -0
- package/dist/transcription/whisperService.js +102 -0
- package/dist/transcription/whisperService.js.map +1 -0
- package/eslint.config.js +55 -0
- package/package.json +68 -11
- package/src/__fixtures__/highlevel-post-response.json +68 -0
- package/src/__fixtures__/hls-master-playlist.m3u8 +24 -0
- package/src/cli/commands/__snapshots__/syncHighLevel.test.ts.snap +38 -0
- package/src/cli/commands/config.ts +74 -0
- package/src/cli/commands/inspect.ts +441 -0
- package/src/cli/commands/login.ts +68 -0
- package/src/cli/commands/status.ts +147 -0
- package/src/cli/commands/sync.ts +1235 -0
- package/src/cli/commands/syncHighLevel.test.ts +144 -0
- package/src/cli/commands/syncHighLevel.ts +639 -0
- package/src/cli/index.ts +121 -0
- package/src/config/configManager.ts +75 -0
- package/src/config/paths.test.ts +83 -0
- package/src/config/paths.ts +36 -0
- package/src/config/schema.test.ts +173 -0
- package/src/config/schema.ts +65 -0
- package/src/downloader/hlsDownloader.test.ts +148 -0
- package/src/downloader/hlsDownloader.ts +327 -0
- package/src/downloader/hlsValidator.ts +196 -0
- package/src/downloader/index.ts +122 -0
- package/src/downloader/loomDownloader.test.ts +43 -0
- package/src/downloader/loomDownloader.ts +742 -0
- package/src/downloader/queue.test.ts +199 -0
- package/src/downloader/queue.ts +118 -0
- package/src/downloader/vimeoDownloader.test.ts +62 -0
- package/src/downloader/vimeoDownloader.ts +722 -0
- package/src/scraper/extractor.test.ts +124 -0
- package/src/scraper/extractor.ts +757 -0
- package/src/scraper/highlevel/__snapshots__/extractor.test.ts.snap +41 -0
- package/src/scraper/highlevel/extractor.test.ts +134 -0
- package/src/scraper/highlevel/extractor.ts +537 -0
- package/src/scraper/highlevel/index.ts +2 -0
- package/src/scraper/highlevel/navigator.test.ts +110 -0
- package/src/scraper/highlevel/navigator.ts +668 -0
- package/src/scraper/highlevel/schemas.ts +183 -0
- package/src/scraper/navigator.test.ts +122 -0
- package/src/scraper/navigator.ts +355 -0
- package/src/scraper/schemas.ts +177 -0
- package/src/scraper/videoInterceptor.ts +435 -0
- package/src/shared/auth.test.ts +58 -0
- package/src/shared/auth.ts +251 -0
- package/src/shared/firebase.ts +151 -0
- package/src/shared/fs.ts +80 -0
- package/src/shared/http.ts +34 -0
- package/src/shared/index.ts +6 -0
- package/src/shared/slug.ts +26 -0
- package/src/shared/url.test.ts +122 -0
- package/src/shared/url.ts +57 -0
- package/src/state/database.test.ts +49 -0
- package/src/state/database.ts +919 -0
- package/src/state/index.ts +14 -0
- package/src/storage/fileSystem.test.ts +64 -0
- package/src/storage/fileSystem.ts +175 -0
- package/tsconfig.json +28 -0
- package/vitest.config.ts +29 -0
- package/cli.js +0 -45
package/README.md
CHANGED
|
@@ -1,33 +1,142 @@
|
|
|
1
1
|
# Offcourse
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/offcourse)
|
|
4
|
+
[](https://www.npmjs.com/package/offcourse)
|
|
5
|
+
[](https://github.com/sebastian-software/offcourse/blob/main/LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](https://codecov.io/gh/sebastian-software/offcourse)
|
|
8
|
+
[](https://github.com/sebastian-software/offcourse/actions/workflows/ci.yml)
|
|
7
9
|
|
|
8
10
|
Download online courses for offline access – of course! 📚
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
Saves video content and lesson text as Markdown files, organized by module structure.
|
|
11
13
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
Offcourse is a CLI tool that downloads online courses for offline access. It preserves the course structure, downloads videos, and converts lesson content to clean Markdown files.
|
|
15
|
-
|
|
16
|
-
## Planned Features
|
|
14
|
+
## Features
|
|
17
15
|
|
|
18
16
|
- 🔐 **Browser-based authentication** – Log in once, sessions are cached
|
|
19
17
|
- 📚 **Course structure preservation** – Maintains module/lesson hierarchy
|
|
20
|
-
- 🎬 **Video downloads** – Supports
|
|
18
|
+
- 🎬 **Video downloads** – Supports HLS streams, Loom and Vimeo
|
|
21
19
|
- 📝 **Content extraction** – Converts lesson text to clean Markdown
|
|
22
20
|
- ⏸️ **Resumable syncs** – Skips already downloaded content
|
|
23
21
|
- ⚡ **Concurrent downloads** – Configurable parallelism
|
|
22
|
+
- 🔍 **Auto-detection** – Automatically detects platform from URL
|
|
24
23
|
|
|
25
24
|
## Supported Platforms
|
|
26
25
|
|
|
27
|
-
| Platform | Status |
|
|
28
|
-
|
|
29
|
-
| [Skool.com](https://skool.com) | ✅
|
|
30
|
-
| [
|
|
26
|
+
| Platform | Status | Notes |
|
|
27
|
+
|----------|--------|-------|
|
|
28
|
+
| [Skool.com](https://skool.com) | ✅ Supported | Community courses |
|
|
29
|
+
| [HighLevel (GoHighLevel)](https://gohighlevel.com) | ✅ Supported | Membership portals, ClientClub |
|
|
30
|
+
| [LearningSuite.io](https://learningsuite.io) | 🚧 Planned | |
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g offcourse
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or run directly with npx:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx offcourse <command>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Requires Node.js 22+.
|
|
45
|
+
|
|
46
|
+
For HLS video downloads (HighLevel native videos), [ffmpeg](https://ffmpeg.org/) must be installed:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# macOS
|
|
50
|
+
brew install ffmpeg
|
|
51
|
+
|
|
52
|
+
# Ubuntu/Debian
|
|
53
|
+
sudo apt install ffmpeg
|
|
54
|
+
|
|
55
|
+
# Windows (via Chocolatey)
|
|
56
|
+
choco install ffmpeg
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
### Login
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Opens browser for interactive login
|
|
65
|
+
offcourse login
|
|
66
|
+
|
|
67
|
+
# Force re-login
|
|
68
|
+
offcourse login --force
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Sync a Course
|
|
72
|
+
|
|
73
|
+
The `sync` command auto-detects the platform from the URL:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Auto-detect platform and download
|
|
77
|
+
offcourse sync <url>
|
|
78
|
+
|
|
79
|
+
# Skip video downloads
|
|
80
|
+
offcourse sync <url> --skip-videos
|
|
81
|
+
|
|
82
|
+
# Skip text content
|
|
83
|
+
offcourse sync <url> --skip-content
|
|
84
|
+
|
|
85
|
+
# Preview without downloading
|
|
86
|
+
offcourse sync <url> --dry-run
|
|
87
|
+
|
|
88
|
+
# Limit to first N lessons (for testing)
|
|
89
|
+
offcourse sync <url> --limit 5
|
|
90
|
+
|
|
91
|
+
# Override course name (useful when auto-detection fails)
|
|
92
|
+
offcourse sync <url> --course-name "My Course Name"
|
|
93
|
+
|
|
94
|
+
# Prefer specific video quality
|
|
95
|
+
offcourse sync <url> --quality 720p
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Platform-Specific Commands
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Skool courses
|
|
102
|
+
offcourse sync-skool https://www.skool.com/your-community/classroom
|
|
103
|
+
|
|
104
|
+
# HighLevel/GoHighLevel membership portals
|
|
105
|
+
offcourse sync-highlevel https://member.example.com/courses/products/<id>
|
|
106
|
+
offcourse sync-highlevel <url> --course-name "Course Name"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Configuration
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Show current config
|
|
113
|
+
offcourse config show
|
|
114
|
+
|
|
115
|
+
# Set output directory
|
|
116
|
+
offcourse config set outputDir ~/Courses
|
|
117
|
+
|
|
118
|
+
# Set video quality (highest, lowest, 1080p, 720p, 480p)
|
|
119
|
+
offcourse config set videoQuality 720p
|
|
120
|
+
|
|
121
|
+
# Set download concurrency (1-5)
|
|
122
|
+
offcourse config set concurrency 3
|
|
123
|
+
|
|
124
|
+
# Run headless (no browser window)
|
|
125
|
+
offcourse config set headless true
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Inspect (Debugging)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Analyze page structure
|
|
132
|
+
offcourse inspect <url>
|
|
133
|
+
|
|
134
|
+
# Save analysis to files
|
|
135
|
+
offcourse inspect <url> --output ./analysis
|
|
136
|
+
|
|
137
|
+
# Include full HTML dump
|
|
138
|
+
offcourse inspect <url> --full
|
|
139
|
+
```
|
|
31
140
|
|
|
32
141
|
## Output Structure
|
|
33
142
|
|
|
@@ -45,13 +154,139 @@ Offcourse is a CLI tool that downloads online courses for offline access. It pre
|
|
|
45
154
|
└── ...
|
|
46
155
|
```
|
|
47
156
|
|
|
48
|
-
##
|
|
157
|
+
## Platform Notes
|
|
49
158
|
|
|
50
|
-
|
|
159
|
+
### HighLevel (GoHighLevel)
|
|
51
160
|
|
|
52
|
-
|
|
161
|
+
HighLevel is an all-in-one marketing platform with a "Memberships" feature for hosting courses. Offcourse supports:
|
|
53
162
|
|
|
54
|
-
|
|
163
|
+
- **Authentication**: Firebase-based login via browser
|
|
164
|
+
- **Course structure**: Extracts products, categories, and posts via API
|
|
165
|
+
- **Video downloads**: Native HLS videos with quality selection (requires ffmpeg)
|
|
166
|
+
- **Embedded videos**: Vimeo, Loom, and other embedded players
|
|
167
|
+
|
|
168
|
+
Common HighLevel portal URLs:
|
|
169
|
+
- `https://member.yourdomain.com/courses/...`
|
|
170
|
+
- `https://portal.yourdomain.com/courses/...`
|
|
171
|
+
- `https://courses.yourdomain.com/...`
|
|
172
|
+
|
|
173
|
+
## Development
|
|
174
|
+
|
|
175
|
+
### Setup
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Clone the repository
|
|
179
|
+
git clone https://github.com/sebastian-software/offcourse.git
|
|
180
|
+
cd offcourse
|
|
181
|
+
|
|
182
|
+
# Install dependencies
|
|
183
|
+
npm install
|
|
184
|
+
|
|
185
|
+
# Build
|
|
186
|
+
npm run build
|
|
187
|
+
|
|
188
|
+
# Link globally (optional)
|
|
189
|
+
npm link
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Commands
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Watch mode
|
|
196
|
+
npm run dev
|
|
197
|
+
|
|
198
|
+
# Run directly (without build)
|
|
199
|
+
npx tsx src/cli/index.ts <command>
|
|
200
|
+
|
|
201
|
+
# Lint
|
|
202
|
+
npm run lint
|
|
203
|
+
|
|
204
|
+
# Format
|
|
205
|
+
npm run format
|
|
206
|
+
|
|
207
|
+
# Type check
|
|
208
|
+
npm run typecheck
|
|
209
|
+
|
|
210
|
+
# Test
|
|
211
|
+
npm test
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Git Hooks
|
|
215
|
+
|
|
216
|
+
This project uses [Husky](https://typicode.github.io/husky/) for Git hooks:
|
|
217
|
+
|
|
218
|
+
- **pre-commit**: Runs Prettier on staged files via lint-staged
|
|
219
|
+
- **pre-push**: Runs ESLint and TypeScript type checking
|
|
220
|
+
- **commit-msg**: Validates commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
|
|
221
|
+
|
|
222
|
+
### Commit Convention
|
|
223
|
+
|
|
224
|
+
We follow [Conventional Commits](https://www.conventionalcommits.org/). Commit messages must follow this format:
|
|
55
225
|
|
|
56
|
-
|
|
226
|
+
```
|
|
227
|
+
<type>[optional scope]: <description>
|
|
228
|
+
|
|
229
|
+
[optional body]
|
|
230
|
+
|
|
231
|
+
[optional footer(s)]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Types:**
|
|
235
|
+
|
|
236
|
+
- `feat`: New feature
|
|
237
|
+
- `fix`: Bug fix
|
|
238
|
+
- `docs`: Documentation changes
|
|
239
|
+
- `style`: Code style changes (formatting, semicolons, etc.)
|
|
240
|
+
- `refactor`: Code refactoring
|
|
241
|
+
- `perf`: Performance improvements
|
|
242
|
+
- `test`: Adding or updating tests
|
|
243
|
+
- `chore`: Maintenance tasks
|
|
244
|
+
- `ci`: CI/CD changes
|
|
245
|
+
|
|
246
|
+
**Examples:**
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
git commit -m "feat: add support for Vimeo downloads"
|
|
250
|
+
git commit -m "fix: handle missing video URLs gracefully"
|
|
251
|
+
git commit -m "docs: update installation instructions"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Releasing
|
|
255
|
+
|
|
256
|
+
Releases are managed with [release-it](https://github.com/release-it/release-it). The release process:
|
|
257
|
+
|
|
258
|
+
1. Runs linting, type checking, and tests
|
|
259
|
+
2. Bumps version based on conventional commits
|
|
260
|
+
3. Generates/updates `CHANGELOG.md`
|
|
261
|
+
4. Creates a Git tag and GitHub release
|
|
262
|
+
5. Publishes to npm
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# Interactive release (will prompt for version bump)
|
|
266
|
+
npm run release
|
|
267
|
+
|
|
268
|
+
# Dry run (preview what would happen)
|
|
269
|
+
npm run release -- --dry-run
|
|
270
|
+
|
|
271
|
+
# Specific version bump
|
|
272
|
+
npm run release -- --minor
|
|
273
|
+
npm run release -- --major
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Acknowledgments
|
|
277
|
+
|
|
278
|
+
A huge thank you to [Sindre Sorhus](https://github.com/sindresorhus) 🙏 for creating and maintaining so many excellent packages that power this project:
|
|
279
|
+
|
|
280
|
+
- [`@sindresorhus/slugify`](https://github.com/sindresorhus/slugify) – Slugify a string
|
|
281
|
+
- [`conf`](https://github.com/sindresorhus/conf) – Simple config handling
|
|
282
|
+
- [`delay`](https://github.com/sindresorhus/delay) – Delay a promise
|
|
283
|
+
- [`execa`](https://github.com/sindresorhus/execa) – Process execution for humans
|
|
284
|
+
- [`ky`](https://github.com/sindresorhus/ky) – Tiny & elegant HTTP client
|
|
285
|
+
- [`p-queue`](https://github.com/sindresorhus/p-queue) – Promise queue with concurrency control
|
|
286
|
+
- [`p-retry`](https://github.com/sindresorhus/p-retry) – Retry a promise-returning function
|
|
287
|
+
|
|
288
|
+
His commitment to high-quality, well-documented, and beautifully designed open source software is truly inspiring. If you find his work useful, consider [sponsoring him](https://github.com/sponsors/sindresorhus).
|
|
289
|
+
|
|
290
|
+
## License
|
|
57
291
|
|
|
292
|
+
MIT
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface OpenRouterOptions {
|
|
2
|
+
model?: string;
|
|
3
|
+
maxTokens?: number;
|
|
4
|
+
temperature?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ChatMessage {
|
|
7
|
+
role: "system" | "user" | "assistant";
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
export interface UsageInfo {
|
|
11
|
+
promptTokens: number;
|
|
12
|
+
completionTokens: number;
|
|
13
|
+
totalTokens: number;
|
|
14
|
+
cost: number | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface ChatResult {
|
|
17
|
+
content: string;
|
|
18
|
+
usage: UsageInfo;
|
|
19
|
+
model: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get cumulative usage stats for the current session.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getCumulativeUsage(): UsageInfo;
|
|
25
|
+
/**
|
|
26
|
+
* Reset cumulative usage stats.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resetCumulativeUsage(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Check if OpenRouter is configured.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isConfigured(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Send a chat completion request to OpenRouter.
|
|
35
|
+
* Returns full result with usage info.
|
|
36
|
+
*/
|
|
37
|
+
export declare function chatWithUsage(messages: ChatMessage[], options?: OpenRouterOptions): Promise<ChatResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Send a chat completion request to OpenRouter.
|
|
40
|
+
* Simple version that just returns the content string.
|
|
41
|
+
*/
|
|
42
|
+
export declare function chat(messages: ChatMessage[], options?: OpenRouterOptions): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Simple prompt helper.
|
|
45
|
+
*/
|
|
46
|
+
export declare function prompt(userPrompt: string, systemPrompt?: string, options?: OpenRouterOptions): Promise<string>;
|
|
47
|
+
//# sourceMappingURL=openRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openRouter.d.ts","sourceRoot":"","sources":["../../src/ai/openRouter.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAUD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,CAE9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAsBD;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAcD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CA+CrB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CACxB,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,MAAM,CAC1B,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAUjB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { config } from "dotenv";
|
|
2
|
+
// Load .env file
|
|
3
|
+
config();
|
|
4
|
+
const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
5
|
+
// Track cumulative usage across calls
|
|
6
|
+
let cumulativeUsage = {
|
|
7
|
+
promptTokens: 0,
|
|
8
|
+
completionTokens: 0,
|
|
9
|
+
totalTokens: 0,
|
|
10
|
+
cost: undefined,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Get cumulative usage stats for the current session.
|
|
14
|
+
*/
|
|
15
|
+
export function getCumulativeUsage() {
|
|
16
|
+
return { ...cumulativeUsage };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reset cumulative usage stats.
|
|
20
|
+
*/
|
|
21
|
+
export function resetCumulativeUsage() {
|
|
22
|
+
cumulativeUsage = {
|
|
23
|
+
promptTokens: 0,
|
|
24
|
+
completionTokens: 0,
|
|
25
|
+
totalTokens: 0,
|
|
26
|
+
cost: undefined,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the OpenRouter API key from environment.
|
|
31
|
+
*/
|
|
32
|
+
function getApiKey() {
|
|
33
|
+
const key = process.env.OPENROUTER_API_KEY;
|
|
34
|
+
if (!key) {
|
|
35
|
+
throw new Error("OPENROUTER_API_KEY not set. Add it to .env file or set as environment variable.");
|
|
36
|
+
}
|
|
37
|
+
return key;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the default model from environment or use fallback.
|
|
41
|
+
*/
|
|
42
|
+
function getDefaultModel() {
|
|
43
|
+
return process.env.OPENROUTER_MODEL ?? "anthropic/claude-3.5-sonnet";
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if OpenRouter is configured.
|
|
47
|
+
*/
|
|
48
|
+
export function isConfigured() {
|
|
49
|
+
return !!process.env.OPENROUTER_API_KEY;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Send a chat completion request to OpenRouter.
|
|
53
|
+
* Returns full result with usage info.
|
|
54
|
+
*/
|
|
55
|
+
export async function chatWithUsage(messages, options = {}) {
|
|
56
|
+
const apiKey = getApiKey();
|
|
57
|
+
const model = options.model ?? getDefaultModel();
|
|
58
|
+
const response = await fetch(OPENROUTER_API_URL, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${apiKey}`,
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
"HTTP-Referer": "https://github.com/course-grab",
|
|
64
|
+
"X-Title": "course-grab",
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
model,
|
|
68
|
+
messages,
|
|
69
|
+
max_tokens: options.maxTokens ?? 4096,
|
|
70
|
+
temperature: options.temperature ?? 0.3,
|
|
71
|
+
}),
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const error = await response.text();
|
|
75
|
+
throw new Error(`OpenRouter API error: ${response.status} - ${error}`);
|
|
76
|
+
}
|
|
77
|
+
const data = (await response.json());
|
|
78
|
+
const usage = {
|
|
79
|
+
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
80
|
+
completionTokens: data.usage?.completion_tokens ?? 0,
|
|
81
|
+
totalTokens: data.usage?.total_tokens ?? 0,
|
|
82
|
+
cost: data.total_cost,
|
|
83
|
+
};
|
|
84
|
+
// Accumulate usage
|
|
85
|
+
cumulativeUsage.promptTokens += usage.promptTokens;
|
|
86
|
+
cumulativeUsage.completionTokens += usage.completionTokens;
|
|
87
|
+
cumulativeUsage.totalTokens += usage.totalTokens;
|
|
88
|
+
if (usage.cost !== undefined) {
|
|
89
|
+
cumulativeUsage.cost = (cumulativeUsage.cost ?? 0) + usage.cost;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
content: data.choices[0]?.message?.content ?? "",
|
|
93
|
+
usage,
|
|
94
|
+
model: data.model ?? model,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Send a chat completion request to OpenRouter.
|
|
99
|
+
* Simple version that just returns the content string.
|
|
100
|
+
*/
|
|
101
|
+
export async function chat(messages, options = {}) {
|
|
102
|
+
const result = await chatWithUsage(messages, options);
|
|
103
|
+
return result.content;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Simple prompt helper.
|
|
107
|
+
*/
|
|
108
|
+
export async function prompt(userPrompt, systemPrompt, options) {
|
|
109
|
+
const messages = [];
|
|
110
|
+
if (systemPrompt) {
|
|
111
|
+
messages.push({ role: "system", content: systemPrompt });
|
|
112
|
+
}
|
|
113
|
+
messages.push({ role: "user", content: userPrompt });
|
|
114
|
+
return chat(messages, options);
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=openRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openRouter.js","sourceRoot":"","sources":["../../src/ai/openRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface PolishedTranscript {
|
|
2
|
+
summary: string;
|
|
3
|
+
transcript: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Convert folder name to readable title.
|
|
7
|
+
* "01-1-herzlich-willkommen" → "Herzlich Willkommen"
|
|
8
|
+
* "02-onboarding-social-leads-academy" → "Onboarding Social Leads Academy"
|
|
9
|
+
*/
|
|
10
|
+
export declare function folderNameToTitle(folderName: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Polish a transcript using an LLM.
|
|
13
|
+
* Returns separate summary and transcript.
|
|
14
|
+
*/
|
|
15
|
+
export declare function polishTranscript(rawTranscript: string): Promise<PolishedTranscript>;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a module summary from multiple lesson summaries.
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateModuleSummary(moduleName: string, lessonSummaries: Array<{
|
|
20
|
+
name: string;
|
|
21
|
+
title: string;
|
|
22
|
+
summary: string;
|
|
23
|
+
}>): Promise<string>;
|
|
24
|
+
//# sourceMappingURL=transcriptPolisher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcriptPolisher.d.ts","sourceRoot":"","sources":["../../src/ai/transcriptPolisher.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAW5D;AAkCD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA0BzF;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GACvE,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { prompt, isConfigured } from "./openRouter.js";
|
|
2
|
+
/**
|
|
3
|
+
* Convert folder name to readable title.
|
|
4
|
+
* "01-1-herzlich-willkommen" → "Herzlich Willkommen"
|
|
5
|
+
* "02-onboarding-social-leads-academy" → "Onboarding Social Leads Academy"
|
|
6
|
+
*/
|
|
7
|
+
export function folderNameToTitle(folderName) {
|
|
8
|
+
return folderName
|
|
9
|
+
// Remove leading numbers and separators (e.g., "01-1-", "02-")
|
|
10
|
+
.replace(/^[\d]+-[\d]*-?/, "")
|
|
11
|
+
// Replace remaining dashes with spaces
|
|
12
|
+
.replace(/-/g, " ")
|
|
13
|
+
// Capitalize each word
|
|
14
|
+
.split(" ")
|
|
15
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
16
|
+
.join(" ")
|
|
17
|
+
.trim();
|
|
18
|
+
}
|
|
19
|
+
const SYSTEM_PROMPT = `Du bist ein Experte für die Aufbereitung von Video-Transkripten.
|
|
20
|
+
|
|
21
|
+
Deine Aufgabe:
|
|
22
|
+
1. Erstelle eine kurze TLDR-Zusammenfassung (2-3 Sätze)
|
|
23
|
+
2. Strukturiere den Text in sinnvolle Absätze
|
|
24
|
+
3. Füge Markdown-Formatierung hinzu:
|
|
25
|
+
- **Fettschrift** für wichtige Begriffe und Kernaussagen
|
|
26
|
+
- *Kursiv* für Betonungen
|
|
27
|
+
- Überschriften (## oder ###) für klare Themenwechsel
|
|
28
|
+
- Aufzählungen wo es Sinn macht
|
|
29
|
+
|
|
30
|
+
Regeln:
|
|
31
|
+
- Behalte den Originaltext bei, ändere keine Wörter
|
|
32
|
+
- Korrigiere nur offensichtliche Transkriptionsfehler
|
|
33
|
+
- Füge Absätze an natürlichen Sprechpausen ein
|
|
34
|
+
- Halte alles auf Deutsch`;
|
|
35
|
+
const USER_PROMPT_TEMPLATE = `Hier ist ein Video-Transkript das aufbereitet werden soll:
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
{transcript}
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
Antworte in folgendem Format:
|
|
42
|
+
|
|
43
|
+
## TLDR
|
|
44
|
+
[2-3 Sätze Zusammenfassung]
|
|
45
|
+
|
|
46
|
+
## Transkript
|
|
47
|
+
|
|
48
|
+
[Aufbereiteter Text mit Markdown-Formatierung]`;
|
|
49
|
+
/**
|
|
50
|
+
* Polish a transcript using an LLM.
|
|
51
|
+
* Returns separate summary and transcript.
|
|
52
|
+
*/
|
|
53
|
+
export async function polishTranscript(rawTranscript) {
|
|
54
|
+
if (!isConfigured()) {
|
|
55
|
+
return {
|
|
56
|
+
summary: "",
|
|
57
|
+
transcript: rawTranscript,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const userPrompt = USER_PROMPT_TEMPLATE.replace("{transcript}", rawTranscript);
|
|
61
|
+
const result = await prompt(userPrompt, SYSTEM_PROMPT, {
|
|
62
|
+
maxTokens: 8192,
|
|
63
|
+
temperature: 0.2,
|
|
64
|
+
});
|
|
65
|
+
// Parse the response
|
|
66
|
+
const tldrMatch = result.match(/## TLDR\s*\n+([\s\S]*?)(?=\n## Transkript|$)/i);
|
|
67
|
+
const transcriptMatch = result.match(/## Transkript\s*\n+([\s\S]*?)$/i);
|
|
68
|
+
const summary = tldrMatch?.[1]?.trim() ?? "";
|
|
69
|
+
const transcript = transcriptMatch?.[1]?.trim() ?? result;
|
|
70
|
+
return {
|
|
71
|
+
summary,
|
|
72
|
+
transcript,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate a module summary from multiple lesson summaries.
|
|
77
|
+
*/
|
|
78
|
+
export async function generateModuleSummary(moduleName, lessonSummaries) {
|
|
79
|
+
if (!isConfigured() || lessonSummaries.length === 0) {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
const moduleTitle = folderNameToTitle(moduleName);
|
|
83
|
+
const summariesText = lessonSummaries
|
|
84
|
+
.map((l, i) => `### ${i + 1}. ${l.title}\n${l.summary}`)
|
|
85
|
+
.join("\n\n");
|
|
86
|
+
const result = await prompt(`Hier sind die Zusammenfassungen aller Lektionen des Moduls "${moduleTitle}":\n\n${summariesText}\n\nErstelle eine übergreifende Zusammenfassung des gesamten Moduls (5-8 Sätze). Fasse die wichtigsten Lernziele und Kernkonzepte zusammen.`, "Du bist ein Experte für Kurszusammenfassungen. Antworte auf Deutsch in klarem, präzisem Stil.", { maxTokens: 512, temperature: 0.3 });
|
|
87
|
+
return `# Zusammenfassung: ${moduleTitle}\n\n${result.trim()}\n\n---\n\n## Lektionen\n\n${summariesText}`;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=transcriptPolisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcriptPolisher.js","sourceRoot":"","sources":["../../src/ai/transcriptPolisher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAOvD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,OAAO,UAAU;QACf,+DAA+D;SAC9D,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC9B,uCAAuC;SACtC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;QACnB,uBAAuB;SACtB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAC3D,IAAI,CAAC,GAAG,CAAC;SACT,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;0BAeI,CAAC;AAE3B,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;+CAakB,CAAC;AAEhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,aAAqB;IAC1D,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,aAAa;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,oBAAoB,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,aAAa,EAAE;QACrD,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IAEH,qBAAqB;IACrB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAChF,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAExE,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC7C,MAAM,UAAU,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC;IAE1D,OAAO;QACL,OAAO;QACP,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB,EAClB,eAAwE;IAExE,IAAI,CAAC,YAAY,EAAE,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,WAAW,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAElD,MAAM,aAAa,GAAG,eAAe;SAClC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;SACvD,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,+DAA+D,WAAW,SAAS,aAAa,6IAA6I,EAC7O,+FAA+F,EAC/F,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,CACrC,CAAC;IAEF,OAAO,sBAAsB,WAAW,OAAO,MAAM,CAAC,IAAI,EAAE,8BAA8B,aAAa,EAAE,CAAC;AAC5G,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shows all current configuration values.
|
|
3
|
+
*/
|
|
4
|
+
export declare function configShowCommand(): void;
|
|
5
|
+
/**
|
|
6
|
+
* Sets a configuration value.
|
|
7
|
+
*/
|
|
8
|
+
export declare function configSetCommand(key: string, value: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Gets a specific configuration value.
|
|
11
|
+
*/
|
|
12
|
+
export declare function configGetCommand(key: string): void;
|
|
13
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAUxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAiCjE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAWlD"}
|