Download online courses for offline access – of course! 📚
→ View Documentation & Homepage
# Install
npm install -g offcourse
# Download a course
offcourse sync <course-url>
# Or run without installing
npx offcourse sync <course-url>Requires Node.js 22+ and ffmpeg for HLS videos.
Video transcription is enabled by default and requires the native
Cuttledoc CLI on PATH. Use --no-transcribe to download without transcription.
| Platform | URL Pattern |
|---|---|
| Skool | skool.com/community/classroom |
| HighLevel | member.*.com/courses/... |
| Josh Comeau Courses | courses.joshwcomeau.com/<course> |
| LearningSuite | *.learningsuite.io/student/... |
| Piccalilli | piccalil.li/<course>/lessons |
# Sync a course (auto-detects platform)
offcourse sync <url>
# Sync with options
offcourse sync <url> --skip-videos # Skip downloading videos
offcourse sync <url> --dry-run # Preview
offcourse sync <url> --limit 5 # Test with 5 lessons
offcourse sync <url> # Download, then transcribe with Cuttledoc
offcourse sync <url> --no-transcribe # Download without transcription
# Skool login with community access verification
offcourse login https://www.skool.com/<community>/classroom
# Piccalilli OTP login (sync also prompts automatically)
offcourse login https://piccalil.li/<course>/lessons
# Josh Comeau Magic Link login (sync also prompts automatically)
offcourse login https://courses.joshwcomeau.com/<course>
# LearningSuite login (session is saved per tenant)
offcourse login https://<tenant>.learningsuite.io/student/course/<course>/<id>
# Unlock sequential content (LearningSuite)
offcourse complete <url>
# Configuration (optional)
offcourse config set outputDir ~/Courses # Default: current directory
offcourse config set videoQuality 720p # Default: highest
offcourse config set concurrency 3 # Parallel downloads (1-5, default: 2)
offcourse config set extractionConcurrency 6 # Browser tabs (1-8, default: 4)
offcourse config set cuttledocPath /usr/local/bin/cuttledoc
offcourse config set transcriptionLanguage de
offcourse config set transcriptionBackend auto
offcourse config set transcriptionEnhancement localOffcourse integrates Cuttledoc through its native CLI instead of embedding a platform-specific Node module. This keeps course extraction independent from speech engines and lets both tools be installed and released separately.
# Verify the native dependency
cuttledoc --version
# Transcribe new downloads and retry unfinished jobs from earlier runs
offcourse sync <course-url>
# Override the configured language, backend, or enhancement for one run
offcourse sync <course-url> \
--transcription-language en \
--transcription-backend auto \
--transcription-enhancement offTranscription runs automatically after downloads and is sequential to keep model memory bounded.
Use --no-transcribe to disable it for a sync; --dry-run never transcribes.
--skip-videos only skips downloads: existing local videos can still be transcribed. For every video,
Offcourse writes <video-stem>.transcript.json with Cuttledoc's complete machine-readable result
and <video-stem>.transcript.md for reading. SQLite stores the Cuttledoc version, selected settings,
durations, attempts, and errors.
Repeat the same offcourse sync <course-url> command to fill in missing transcripts for existing
downloads. Downloaded videos are reused. Complete transcript files are skipped, even when there
is no transcription record in SQLite. Missing Markdown is restored from valid JSON without
running Cuttledoc. Missing or invalid JSON triggers transcription while preserving any existing
nonempty Markdown, including manual edits. The files are checked even when SQLite marks the
transcription as completed.
Each sync attempts every unfinished video once and continues after individual transcription
failures. A subsequent sync retries missing transcripts regardless of previous attempt counts.
sync --force explicitly regenerates transcripts as well as refreshing course content.
Course scanning and content extraction use extractionConcurrency browser tabs (default: 4). All tabs share the same authenticated session. Video downloads use a separate concurrency queue (default: 2), so browser work and network/download load can be tuned independently.
Video downloads that use HLS require ffmpeg. Confirm it is available with ffmpeg -version; if the command is missing, install ffmpeg with your operating system's package manager and retry the sync.
Transcription is the only feature that requires Cuttledoc. It requires the native v3 CLI; the
older v1/v2 npm releases are not compatible. Follow the native CLI setup instructions in the
Cuttledoc repository, verify it with
cuttledoc --version, or configure an explicit executable:
offcourse config set cuttledocPath /absolute/path/to/cuttledocIf Cuttledoc is missing, downloaded videos remain on disk. Use sync --no-transcribe to
continue downloading, then repeat sync <course-url> after setting up Cuttledoc to fill in
missing transcripts.
Local recognition currently requires Apple Silicon with macOS 26+, FFmpeg, and the selected
backend’s speech assets or model. The default backend selection stays local; no hosted backend
is enabled automatically.
Install the browser binary used by the scraper, then retry:
npx playwright install chromiumWhen working from a repository checkout, use pnpm exec playwright install chromium instead.
Clear the saved session and force a fresh login for the affected platform URL:
offcourse logout <url>
offcourse login <url> --forceOffcourse stores per-lesson failure details for every supported platform and supports a targeted retry:
offcourse status <url> --errors
offcourse sync <url> --retry-failedSee CONTRIBUTING.md for commit conventions, Git hooks, and pull-request expectations.
git clone https://github.com/sebastian-software/offcourse.git
cd offcourse
corepack enable
pnpm install
pnpm build
pnpm link --global # optional: link globallypnpm dev # Watch mode
pnpm check # Format, lint, types, unit tests, and build
pnpm test # Unit tests in watch mode
pnpm test:integration # Network/ffmpeg integration testsReleases are automated with Release Please. Merging its release PR updates the package version and changelog, creates the GitHub release, and publishes the package to npm. The recommended setup is npm Trusted Publishing for the publish.yml workflow; an NPM_TOKEN repository secret remains supported as a fallback. A RELEASE_PLEASE_TOKEN secret is recommended so release PRs run the regular pull-request CI. To recover a release after configuring npm authentication, run the Release workflow manually with publish set to true.
- Create
src/scraper/newplatform/with:auth.ts– Session detectionnavigator.ts– Course structureextractor.ts– Content extractionschemas.ts– Zod schemas
- Add a platform sync handler in
src/cli/commands/ - Register URL detection in
src/cli/syncPlatform.tsand delegation insrc/cli/index.ts
Thanks to Sindre Sorhus for the excellent packages powering this project: slugify, conf, delay, execa, ky, p-retry.
MIT
Open Source at Sebastian Software
Copyright © 2026 Sebastian Software GmbH