Download online courses for offline access
β of course! π
You paid for that course. Now own it for real. Offcourse saves videos, text, and attachments to your local drive β organized exactly like the original. Learn on a plane, in the mountains, or anywhere your internet can't follow.
$ npx offcourse sync <course-url>
100%
Yours to keep. Downloaded content stays on your drive forever.
1 cmd
Paste a URL, hit enter. No config files, no setup wizards.
0 lock-in
Standard files: MP4 videos, Markdown text, PDF attachments.
Not a proof of concept. A tool we actually use every day.
Offcourse handles the messy reality of online courses: different video hosts, authentication walls, locked content, flaky APIs. It just works.
Log in once through a real browser. Sessions are cached for future syncs.
Maintains module/lesson hierarchy. Navigate your course just like online.
Supports HLS streams, Vimeo, Loom, and native MP4/WebM files.
Lesson text converted to clean Markdown. Read anywhere, even offline.
Already downloaded content is skipped. Sync incrementally as courses update.
Just paste a URL. Platform is detected automatically. Simple as that.
Each platform has its quirks. We've figured them out so you don't have to.
Browser-based login means no API keys to manage. Just sign in like normal, and Offcourse remembers your session for future syncs.
Community-based learning platform with courses and classroom content.
Marketing platform with membership portals. Firebase auth, HLS native videos.
German LMS with sequential unlocking. Bunny CDN videos, PDF attachments.
Course creators use every video host imaginable. We handle them all.
From enterprise HLS streams to Loom screen recordings β Offcourse extracts the actual video files in the best available quality.
via ffmpeg
embedded players
screen recordings
direct downloads
CDN Support: Bunny CDN Β· Cloudflare Stream Β· Vimeo CDN
Copy a course URL. Run one command. Done.
No YAML configs, no Docker containers, no environment variables. If you can use a terminal, you can use Offcourse.
# Auto-detect platform and download everything offcourse sync <url> # Skip videos (text only) offcourse sync <url> --skip-videos # Preview without downloading offcourse sync <url> --dry-run # Limit to first 5 lessons (testing) offcourse sync <url> --limit 5
Some platforms lock lessons sequentially. The complete command marks lessons as done to unlock more content:
# Iterates until all content is unlocked offcourse complete <url> # Show browser window offcourse complete <url> --visible
# Set output directory offcourse config set outputDir ~/Courses # Set video quality (1080p, 720p, 480p) offcourse config set videoQuality 720p # Set download concurrency offcourse config set concurrency 3
No proprietary formats. No special apps needed.
Open videos in VLC, read text in any Markdown editor, print PDFs. Your course, your files, your choice of tools.
~/Downloads/offcourse/ βββ course-name/ βββ 01-module-name/ β βββ 01-lesson-name/ β β βββ content.md β β βββ video.mp4 β β βββ attachment.pdf β βββ 02-another-lesson/ β βββ ... βββ 02-next-module/ βββ ...
Get started in seconds
$ npm install -g offcourse
$ npx offcourse sync <url>
Node.js 22+
Required
ffmpeg
For HLS video downloads
Install ffmpeg: brew install ffmpeg (macOS) Β· apt install ffmpeg (Linux)
Scraping modern learning platforms isn't trivial. Here's how Offcourse solves real-world challenges.
Each platform has its own authentication, content locking, and video delivery quirks. We handle them all so you don't have to.
Some platforms serve encrypted HLS playlists that are decrypted client-side. Standard downloaders fail because the playlist data is gibberish.
Our approach:
We intercept the actual .ts video segments as the browser plays them, capture their individual auth tokens, download each segment, then concatenate them with ffmpeg.
Modern SPAs often use GraphQL with "persisted queries"βthe server only accepts pre-registered query hashes, rejecting arbitrary queries.
Our approach:
Instead of reverse-engineering GraphQL, we drive a real browser and extract data from the rendered DOM. This approach is more robust and survives API changes.
Some courses lock lessons until previous ones are "completed". You might own the course but can't access lesson 10 until you've watched lessons 1-9.
Our approach:
The complete command iteratively marks lessons as done, triggering the platform to unlock subsequent content. It loops until no new content appears.
Re-authenticating for every sync is tedious. Sessions need to persist, but also detect when they've expired.
Our approach:
We cache browser state (cookies, localStorage) per-platform. On each run, we validate the session and only prompt for login when truly needed. Interactive logins happen in a visible browser window you control.
Video CDNs like Bunny require signed URLs with short-lived tokens, API keys, and specific headers. Simple wget/curl doesn't work.
Our approach:
We extract all auth artifacts (cookies, tokens, API keys) from the browser session and pass them to ffmpeg and our download handlers via headers.
Stop depending on someone else's servers.
Platforms shut down. Subscriptions expire. Internet goes out. Downloaded courses don't have these problems.