Logo
Search YouTube, arrange a playlist, and save it to your Yoto Make Your Own (MYO) cards — Yoto’s blank cards you load your own audio onto.
Self-hosted Nuxt server app. Yoto OAuth token exchange and YouTube audio download (via yt-dlp) need a long-running server process, so a static export (Netlify/Vercel static, GitHub Pages, etc.) cannot power those flows.
louis-demo.mp4
Self-host · Download (desktop) · Native development · Contributing · Releases · Desktop
Personal use only. You are responsible for complying with YouTube’s Terms of Service and applicable law when downloading audio.
- Search YouTube and preview audio (server-side via yt-dlp)
- Browse and select your Yoto MYO cards
- Drag-and-drop playlist editing (desktop browser); phone Search / Library flow with Add-to-card
- Save playlists to Yoto with download / transcode progress
- Optional desktop app (macOS / Windows) — same app, no Docker required
Installers ship as Assets on each GitHub Release (same vX.Y.Z as Docker):
| Platform | Asset |
|---|---|
| macOS Apple Silicon | Louis-<version>-arm64.dmg |
| macOS Intel | Louis-<version>-x64.dmg |
| Windows | Louis-Setup-<version>.exe |
After install, open Settings → Desktop API keys and add your Yoto client ID + YouTube Data API key. Register OAuth redirect http://127.0.0.1:4010/api/yoto/auth/callback on yoto.dev. Details: docs/DESKTOP.md.
Installers are currently unsigned (Gatekeeper / SmartScreen may warn). Signing notes: docs/DESKTOP_SIGNING.md.
Self-host the same release via GHCR:
docker pull ghcr.io/stuartromanek/louis:latest
# or pin: ghcr.io/stuartromanek/louis:vX.Y.ZCompose / env setup below. Cut releases: docs/RELEASE.md.
Docker includes Node, yt-dlp, and ffmpeg — you only need Docker and API credentials.
git clone https://github.com/stuartromanek/louis.git
cd louis
cp .env.example .env
# Fill in LOUIS_YOTO_CLIENT_ID and LOUIS_YOUTUBE_API_KEY (see below)
docker compose up -d --buildOpen http://localhost:4000. Health: GET /api/health.
Create a public client at yoto.dev:
| Setting | Value |
|---|---|
| Redirect URI (self-host) | https://your-domain/api/yoto/auth/callback |
| Local redirect | http://localhost:4000/api/yoto/auth/callback |
| Desktop app redirect | http://127.0.0.1:4010/api/yoto/auth/callback — see DESKTOP.md |
| Scopes | user:content:view user:content:manage |
You only need LOUIS_YOTO_CLIENT_ID. Leave LOUIS_YOTO_CLIENT_SECRET empty for PKCE.
Enable YouTube Data API v3 in Google Cloud Console and create an API key.
Copy [.env.example](.env.example). Use LOUIS_* names so the same file works for local dev, docker compose, and docker run --env-file .env without rebuilding the image. Legacy NUXT_* / NUXT_PUBLIC_* names still work as a deprecated fallback (LOUIS_* wins when both are set).
| Variable | Notes |
|---|---|
LOUIS_YOTO_CLIENT_ID |
Public client ID |
LOUIS_YOUTUBE_API_KEY |
Server-side only |
| Variable | Notes |
|---|---|
LOUIS_YOTO_CLIENT_SECRET |
Leave empty for PKCE |
LOUIS_YOTO_REDIRECT_URI |
Required in production; must match the portal. Local/dev can auto-detect from host |
| Variable | Notes |
|---|---|
LOUIS_AUDIO_WORK_DIR |
Default /data/audio in Docker |
LOUIS_AUDIO_JOB_MAX_AGE_MS |
Stale jobs/ cleanup (default 1h) |
LOUIS_AUDIO_CACHE_MAX_AGE_MS |
Cache file TTL (default 14d) |
LOUIS_AUDIO_CACHE_MAX_BYTES |
Combined preview + save cache cap (default 5 GiB) |
LOUIS_YTDLP_PATH |
Docker ships yt-dlp nightly on PATH (refreshed when the image is rebuilt with a new YTDLP_CACHE_BUST) |
LOUIS_YTDLP_COOKIES_FILE |
Optional Netscape cookies.txt. Downloads try anonymously first; cookies are used only if YouTube blocks with bot check, hard 403, or age-gate. Prefer a throwaway Google account; never commit the file |
| Variable | Notes |
|---|---|
LOUIS_ENABLE_DEBUG_ROUTES |
true enables debug API routes |
docker run -p 4000:4000 --env-file .env louis:local- Single instance — save-job progress is in memory
- HTTPS in production — OAuth cookies set
securewhenNODE_ENV=production - Persistent disk — recommended for the audio cache under
LOUIS_AUDIO_WORK_DIR(cache/preview/,cache/save/). Stalejobs/dirs and old cache files are swept on startup and after downloads.
For local Node (without Docker), install these first:
- Node.js 22+ (also used as yt-dlp’s JS runtime for YouTube signing)
- yt-dlp — keep it current; YouTube breaks outdated extractors
- ffmpeg — required for save
- Optional:
LOUIS_YTDLP_COOKIES_FILEas above
npm install
cp .env.example .env
npm run devDev server: port 4000.
Production without Docker:
npm run build
npm run startMIT — see LICENSE.
Fonts (LT Saeada, self-hosted), OpenMoji icons, and SND UI sounds are used; see THIRD_PARTY_NOTICES.md.
Security reports: SECURITY.md.