A CLI app to download YouTube channels and playlists and keep them up to date locally.
It gives you simple daily commands, safe progress tracking, and optional background sync.
- Install runtime dependencies (
yt-dlpandffmpeg):
# macOS (Homebrew)
brew install yt-dlp ffmpeg# Ubuntu/Debian example
sudo apt update
sudo apt install -y yt-dlp ffmpeg- Install
yt-vod-manager(pick one):
Homebrew:
brew tap marcohefti/yt-vod-manager
brew install yt-vod-managerWinGet:
winget install --id MarcoHefti.YTVodManager; yt-vod-manager managenpm:
npm install -g @marcohefti/yt-vod-manager- Initialize workspace:
yt-vod-manager init- Add one or more sources:
yt-vod-manager add --name mkbhd --source "https://www.youtube.com/@mkbhd/videos"
yt-vod-manager add --name my-playlist --source "https://www.youtube.com/playlist?list=PLFs19LVskfNzQLZkGG_zf6yfYTp_3v_e6"- Sync all projects:
yt-vod-manager sync --all-projects- Check status:
yt-vod-manager status --allIf you see no projects configured, start with:
yt-vod-manager init
yt-vod-manager add --source "<url>"Think in three layers:
- Projects
- Each project is a named source URL in
config/projects.json.
- Runs
- Every sync writes/updates a run under
runs/<run_id>/with manifests and state.
- Sync cycle
syncdoes: refresh source from YouTube every run -> merge byvideo_id-> download pending/retryable jobs.
This means you can stop and continue later without re-downloading completed videos.
- List configured projects:
yt-vod-manager list- Open interactive manager (arrow keys + wizard):
yt-vod-manager manageManager controls:
up/down(orj/k) move selectionenter/eedit selected projectncreate new project (wizard)ddelete selected projectspacetoggleactiveon selected project- move down into the
Actionspanel and pressenteronSync Active Projectsto launch sync - move down into the
Actionspanel and pressenteronGlobal Settingsto edit global defaults left/rightorspacetoggle select/yes-no fields in the wizardqquit
The manager auto-adapts layout for narrow and wide terminals.
- Sync one project:
yt-vod-manager sync --project mkbhd- Sync all projects:
yt-vod-manager sync --all-projects- Refresh manifests only (no download):
yt-vod-manager sync --all-projects --no-run- Remove a project:
yt-vod-manager remove --name my-playlist --yes- Show or update global settings:
yt-vod-manager settings show
yt-vod-manager settings set --workers 6 --download-limit-mb-s 80 --proxy-mode per_worker
yt-vod-manager settings proxy add --value "http://user:pass@proxy-1:8080"- Update CLI directly from GitHub releases (useful when Winget review is pending):
yt-vod-manager self-updateThe CLI also prints a periodic update hint after successful commands when a newer release exists.
Set YTVM_DISABLE_UPDATE_CHECK=1 to disable this (useful for CI/non-interactive automation).
--workers 5download multiple videos in parallel (default is5).- On
add,--workers 0means "inherit global/default workers". --fragments 10stream chunks per video (default is10).--download-limit-mb-s 80cap transfer speed to 80 MB/s for this invocation (0disables cap).--order oldestprocess oldest-first by default.--quality best|1080p|720pchoose a simple quality preset.--subtitles auto|yes|nochoose whether subtitles are downloaded.--sub-langs english|allchoose subtitle language preference (defaultenglish).--browser-cookiesuse logged-in browser cookies for age-restricted videos.- Browser cookie auth can trigger OS security prompts and account notifications from YouTube/Google/browser.
--active-onlysync only projects marked active (with--project/--all-projects).--max-jobs 10process only a limited batch.--retry-permanentre-attempt permanent failures.--stop-on-retryablestop cleanly after transient/rate-limit failures.--cookies /path/to/cookies.txtuse authenticated access.--jsonprint machine-readable output.
Default config path: config/projects.json
Repository example: config/projects.example.json (safe template, no local secrets/paths).
Managed by:
initaddremove
Each saved source can keep defaults like workers/fragments/order/cookies/subtitle options.
Global settings in the same file (global) control:
- default workers
- global download limit in MB/s
- proxy mode and proxy list (one proxy per worker when
proxy_mode=per_worker)
Runtime precedence:
- CLI invocation flags
- Project overrides
- Global settings
- Built-in defaults
- Project config:
config/projects.json - Runs:
runs/<run_id>/ - Run state files:
manifest.raw.jsonmanifest.jobs.jsonrun.json
- Downloaded media (default):
runs/<run_id>/downloads/
Most users only need init/add/list/sync/status/remove.
Advanced commands are still available for low-level control:
discoverrefreshrun
For refresh and run, target selection is explicit and safer:
--run-id <id>--run-dir <path>--project <name>--latest
- Download state is checkpointed after each attempt.
- Run-level lock prevents concurrent writers on the same run directory.
- Interrupted
runningjobs are recovered as retryable. - Subtitle failures are non-fatal.
- Missing previously-downloaded local media is detected and re-queued.
- Manifest writes are atomic (temp-file + rename) to reduce partial-write corruption risk.
- Playlist size shown in live progress is an estimate (metadata/duration based), not an exact byte guarantee.
sync is safe for background execution. Use scripts/sync-active.sh (includes locking and --active-only defaults).
- macOS (
launchd): create~/Library/LaunchAgents/com.marcohefti.yt-vod-manager-sync.plistthat runs/absolute/path/to/yt-vod-manager/scripts/sync-active.sh, thenlaunchctl load ~/Library/LaunchAgents/com.marcohefti.yt-vod-manager-sync.plist. - Unix (
systemd --user): create amarcohefti-yt-vod-manager-sync.service+marcohefti-yt-vod-manager-sync.timerthat executes/absolute/path/to/yt-vod-manager/scripts/sync-active.sh, thensystemctl --user enable --now marcohefti-yt-vod-manager-sync.timer.
If you want to build locally instead of using npm/Homebrew:
- Install dependencies (
yt-dlp,ffmpeg, andgo):
# macOS (Homebrew)
brew install yt-dlp ffmpeg go# Ubuntu/Debian example
sudo apt update
sudo apt install -y yt-dlp ffmpeg golang-go- Build:
go build -o bin/yt-vod-manager ./cmd/yt-vod-manager- Run:
./bin/yt-vod-manager init./scripts/verify.shThis runs tests, architecture checks, linting, and build.
GitHub Releases are built automatically via .github/workflows/release.yml:
- push to
main-> versioned prerelease snapshot (v0.1.0-dev.<run>initially; aftervX.Y.Z, next snapshots usevX.Y.(Z+1)-dev.<run>) - push version tag
v*-> normal versioned release - each release includes a changelog generated from commits since the previous release tag
- stable tag releases also publish to npm, update Homebrew formula, and generate WinGet manifests (if repo secrets are set, WinGet updates are auto-submitted)
Create a release tag:
git tag v0.1.0
git push origin v0.1.0Artifacts are attached to the release page:
darwin(amd64,arm64)linux(amd64,arm64)windows(amd64)checksumsfile for integrity verificationwinget-manifests_<tag>.zip(stable tags only)
Homebrew:
brew tap marcohefti/yt-vod-manager
brew install yt-vod-managerWinGet:
winget install --id MarcoHefti.YTVodManagernpm:
npm install -g @marcohefti/yt-vod-managerFor automation in GitHub Actions:
- set
HOMEBREW_TAP_GITHUB_TOKEN(repo write access tomarcohefti/homebrew-yt-vod-manager) - set
WINGET_CREATE_GITHUB_TOKEN(GitHub PAT withpublic_reposcope forwingetcreatesubmit) - configure npm Trusted Publisher for
@marcohefti/yt-vod-manager:- owner:
marcohefti - repository:
yt-vod-manager - workflow file:
.github/workflows/release.yml
- owner:
- first WinGet submission is manual in
microsoft/winget-pkgs; after packageMarcoHefti.YTVodManagerexists, releases auto-submit updates
MIT (LICENSE).