yt-vod-manager

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.
Quick Start
- Install runtime dependencies (
yt-dlp and ffmpeg):
# 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-manager
WinGet:
winget install --id MarcoHefti.YTVodManager; yt-vod-manager manage
npm:
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 --all
If you see no projects configured, start with:
yt-vod-manager init
yt-vod-manager add --source ""
How It Works
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// with manifests and state.
- Sync cycle
sync does: refresh source from YouTube every run -> merge by video_id -> download pending/retryable jobs.
This means you can stop and continue later without re-downloading completed videos.
Common Commands
- List configured projects:
yt-vod-manager list
- Open interactive manager (arrow keys + wizard):
yt-vod-manager manage
Manager controls:
up/down (or j/k) move selection
enter / e edit selected project
n create new project (wizard)
d delete selected project
space toggle active on selected project
- move down into the
Actions panel and press enter on Sync Active Projects to launch sync
- move down into the
Actions panel and press enter on Global Settings to edit global defaults
left/right or space toggle select/yes-no fields in the wizard
q quit
The manager auto-adapts layout for narrow and wide terminals.
yt-vod-manager sync --project mkbhd
yt-vod-manager sync --all-projects
- Refresh manifests only (no download):
yt-vod-manager sync --all-projects --no-run
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-update
The 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).
Useful Options
--workers 5 download multiple videos in parallel (default is 5).
- On
add, --workers 0 means "inherit global/default workers".
--fragments 10 stream chunks per video (default is 10).
--download-limit-mb-s 80 cap transfer speed to 80 MB/s for this invocation (0 disables cap).
--order oldest process oldest-first by default.
--quality best|1080p|720p choose a simple quality preset.
--subtitles auto|yes|no choose whether subtitles are downloaded.
--sub-langs english|all choose subtitle language preference (default english).
--browser-cookies use logged-in browser cookies for age-restricted videos.
- Browser cookie auth can trigger OS security prompts and account notifications from YouTube/Google/browser.
--active-only sync only projects marked active (with --project/--all-projects).
--max-jobs 10 process only a limited batch.
--retry-permanent re-attempt permanent failures.
--stop-on-retryable stop cleanly after transient/rate-limit failures.
--cookies /path/to/cookies.txt use authenticated access.
--json print machine-readable output.
Configuration
Default config path: config/projects.json
Repository example: config/projects.example.json (safe template, no local secrets/paths).
Managed by:
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
Output Layout
- Project config:
config/projects.json
- Runs:
runs//
- Run state files:
manifest.raw.json
manifest.jobs.json
run.json
- Downloaded media (default):
runs//downloads/
Advanced Commands (Technical)
Most users only need init/add/list/sync/status/remove.
Advanced commands are still available for low-level control:
For refresh and run, target selection is explicit and safer:
--run-id
--run-dir
--project
--latest
Reliability Notes
- Download state is checkpointed after each attempt.
- Run-level lock prevents concurrent writers on the same run directory.
- Interrupted
running jobs 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.
Daemon Setup
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.plist that runs /absolute/path/to/yt-vod-manager/scripts/sync-active.sh, then launchctl load ~/Library/LaunchAgents/com.marcohefti.yt-vod-manager-sync.plist.
- Unix (
systemd --user): create a marcohefti-yt-vod-manager-sync.service + marcohefti-yt-vod-manager-sync.timer that executes /absolute/path/to/yt-vod-manager/scripts/sync-active.sh, then systemctl --user enable --now marcohefti-yt-vod-manager-sync.timer.
Local Build (From Source)
If you want to build locally instead of using npm/Homebrew:
- Install dependencies (
yt-dlp, ffmpeg, and go):
# 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
Dev Verification
./scripts/verify.sh
This runs tests, architecture checks, linting, and build.
Releases
GitHub Releases are built automatically via .github/workflows/release.yml:
- push to
main -> versioned prerelease snapshot (v0.1.0-dev. initially; after vX.Y.Z, next snapshots use vX.Y.(Z+1)-dev.)
- 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.0
Artifacts are attached to the release page:
darwin (amd64, arm64)
linux (amd64, arm64)
windows (amd64)
checksums file for integrity verification
winget-manifests_.zip (stable tags only)
Package Manager Install
Homebrew:
brew tap marcohefti/yt-vod-manager
brew install yt-vod-manager
WinGet:
winget install --id MarcoHefti.YTVodManager
npm:
npm install -g @marcohefti/yt-vod-manager
For automation in GitHub Actions:
- set
HOMEBREW_TAP_GITHUB_TOKEN (repo write access to marcohefti/homebrew-yt-vod-manager)
- set
WINGET_CREATE_GITHUB_TOKEN (GitHub PAT with public_repo scope for wingetcreate submit)
- configure npm Trusted Publisher for
@marcohefti/yt-vod-manager:
- owner:
marcohefti
- repository:
yt-vod-manager
- workflow file:
.github/workflows/release.yml
- first WinGet submission is manual in
microsoft/winget-pkgs; after package MarcoHefti.YTVodManager exists, releases auto-submit updates
License
MIT (LICENSE).