Skip to content

Latest commit

 

History

100 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Louis! A Yoto Make Your Own (MYO) client backed by YouTube

Louis

Louis turns YouTube into audio playlists for your Yoto. Search or paste a link, arrange tracks, then Update — Louis downloads and transcodes. Linking a physical Make Your Own (MYO) card happens in the Yoto app, not here.

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.

Website: louis.romanek.us · install guides, FAQ, and desktop downloads for end users.

Demo:

louis-overview.mp4

Website · Download · Docker · Home Assistant · Hosting · Desktop docs · Contributing · Releases

Personal use only. You are responsible for complying with YouTube’s Terms of Service and applicable law when downloading audio.

Features

  • Search YouTube and preview audio — YouTube Data API when configured (faster typed search, optional content filtering), otherwise bundled yt-dlp. Paste a video, Shorts, playlist, or channel URL in Search to load it; check rows to add them together
  • Browse your Yoto playlists. New names a playlist and creates it on Yoto right away (empty, or with tracks already picked in Search)
  • Drag-and-drop playlist editing (desktop); phone Search / Library flow with Add to playlist; Add to Home; phone Menu can Update every pending playlist at once
  • Playlist covers: generated art on create; Artwork to generate, upload, or crop a 5×7 cover. Rename or delete from the playlist menu
  • Auto-split long YouTube sources (>55 min) into Part tracks; trim intros/outros before save
  • Save / Update to Yoto with download and transcode progress; optional normalize for new YouTube extracts
  • Per-track 16×16 art (Yoto icon library, yotoicons.com, upload, or draw) — reconnect Yoto once if icon upload asks for user:icons:manage
  • Same app as desktop (macOS / Windows), Docker, or Home Assistant — installers are unsigned (Gatekeeper / SmartScreen may warn)

Choose how to run

You want… Start here
Mac / Windows app, no Docker Download (desktop)
Docker on a NAS / homelab Docker · Hosting
Home Assistant on your LAN Home Assistant
Local Node development Native development

Download (desktop)

Installers ship as Assets on each GitHub Release (same vX.Y.Z as Docker):

Latest release

Platform Asset
macOS Apple Silicon Louis-<version>-arm64.dmg
macOS Intel Louis-<version>-x64.dmg
Windows Louis-Setup-<version>.exe

After install, the setup wizard asks for a Yoto client ID, then a recommended YouTube Data API key (Skip uses bundled yt-dlp). Prefer Use default client for Yoto, or bring your own from yoto.dev and paste http://127.0.0.1:4010/api/yoto/auth/callback into Allowed Callback URLs. Change keys later in Settings → Advanced. Details: docs/DESKTOP.md.

Installers are currently unsigned (Gatekeeper / SmartScreen may warn). Signing notes: docs/DESKTOP_SIGNING.md. End-user install walkthroughs: louis.romanek.us.

Docker

Docker includes Node, yt-dlp, and ffmpeg — you only need Docker and a Yoto client ID.

git clone https://github.com/stuartromanek/louis.git
cd louis
cp .env.example .env
# Paste Louis's bundled LOUIS_YOTO_CLIENT_ID or your own (see Self-host).
# Recommended: LOUIS_YOUTUBE_API_KEY
docker compose up -d --build

Open Louis at the same origin other devices will use (this machine: http://localhost:4000; phones/tablets: http://<host-ip-or-name>:4000). Health: GET /api/health.

Prebuilt images (multi-arch linux/amd64 + linux/arm64 on each v* release):

docker pull stuartromanek/louis:latest
# or pin: stuartromanek/louis:vX.Y.Z

Equivalent on GHCR: ghcr.io/stuartromanek/louis:latest (and :vX.Y.Z).

Docker Hub anonymous pull rate limits can apply on busy hosts; GHCR is an equivalent fallback with the same tags.

Homelab one-click: Portainer app template, Coolify, and CasaOS notes — docs/HOSTING.md. Cut releases: docs/RELEASE.md.

Home Assistant

Install Louis from Supervisor as a custom add-on (wraps the same GHCR image; options map to LOUIS_*; audio under /data/audio; UI on host port 4000, not ingress).

  1. Settings → Add-ons → Add-on store → ⋮ → Repositories → add https://github.com/stuartromanek/louis
  2. Install Louis. Defaults use Louis’s bundled Yoto client and http://homeassistant.local:4000/api/yoto/auth/callback — no yoto.dev app needed if you keep both. Change the redirect off that URI only with your own public client (see DOCS.md). youtube_api_key is recommended (Data API search); leave empty to search with bundled yt-dlp. youtube_safe_search (none / moderate / strict) applies to typed search only when a key is set.
  3. Open http://homeassistant.local:4000 (or your host:port)

Full options, redirect URI, and cookie_secure notes: homeassistant/louis/DOCS.md. Sources live under homeassistant/; root repository.yaml + louis/ symlinks are for Supervisor discovery.

Self-host

1. Yoto client ID

Louis ships a public PKCE client ID (not a secret): PK00MDKCVwWvOG8o3px3qSl57FhfUZxm. Paste it into LOUIS_YOTO_CLIENT_ID when you open Louis at a redirect already registered on Louis’s Yoto app — same value as desktop Use default client / the HA add-on default. There is no silent fallback if the env var is empty.

Redirect (exact) Client
http://127.0.0.1:4010/api/yoto/auth/callback Louis bundled (desktop) — see DESKTOP.md
http://homeassistant.local:4000/api/yoto/auth/callback Louis bundled (HA default)

For any other origin (NAS IP, custom hostname, HTTPS domain), create your own public client at yoto.dev so you can register that exact /api/yoto/auth/callback, then use that client ID instead:

Setting Value
Allowed Callback URLs (yoto.dev field name) http://<host-ip-or-name>:4000/api/yoto/auth/callback or https://your-domain/api/yoto/auth/callback — same origin you open Louis
Scopes offline_access user:content:view user:content:manage user:icons:manage

Ports: desktop OAuth is 4010 (127.0.0.1 only). Docker / Home Assistant / reverse proxy (Nginx Proxy Manager, etc.) use 4000, or your HTTPS hostname with no port when TLS terminates at the proxy. Paste the callback into Allowed Callback URLs — not Login URI / Logout URI.

2. YouTube API (recommended)

A YouTube Data API v3 key is recommended for faster search and safeSearch=moderate on typed search. In Google Cloud Console: create or pick a project, enable YouTube Data API v3, then create an API key. If Google asks What data will you be accessing?, choose Public data (not User data) — Louis only needs an API key for public video search, not Google user OAuth. Restricting the key to YouTube Data API v3 is optional but smart. Walkthrough: YouTube Data API getting started.

Search still works without a key (bundled yt-dlp): slower, no safeSearch, and search can break on a different week than download. Leave LOUIS_YOUTUBE_API_KEY unset to use that path.

3. Environment

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).

Required

Variable Notes
LOUIS_YOTO_CLIENT_ID Public PKCE client ID — Louis bundled (PK00…) for pre-registered redirects, or your own from yoto.dev

Yoto

Variable Notes
LOUIS_YOTO_REDIRECT_URI Optional pin; must match the portal. Unset: Louis uses the Host the browser actually used. Prefer a hostname over a DHCP IP. Other devices cannot use the Docker host’s localhost
LOUIS_COOKIE_SECURE OAuth cookie Secure flag. Docker image defaults false (LAN HTTP). Node-without-Docker: when unset, secure iff NODE_ENV=production. Set true behind HTTPS

YouTube / audio

Variable Notes
LOUIS_YOUTUBE_API_KEY Recommended. YouTube Data API v3 (faster typed search). Unset: search without a key
LOUIS_YOUTUBE_SAFE_SEARCH Typed-search content filtering when a Data API key is set: none, moderate (default), or strict. Desktop: Settings → Advanced; HA: youtube_safe_search option
LOUIS_AUDIO_WORK_DIR Default /data/audio in Docker. Preview/save cache plus logs/pipeline.jsonl (YouTube extract diagnostics). For a long local capture, point this at a stable path rather than OS tmp.
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). Does not sweep logs/.
LOUIS_YTDLP_PATH Optional pin. Docker ships yt-dlp on PATH; Settings → Advanced can install a newer nightly into the audio volume / desktop app data (preferred when its version is newer)
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

Advanced / debug

Variable Notes
LOUIS_ENABLE_DEBUG_ROUTES true enables debug API routes
LOUIS_PIPELINE_LOG Pipeline journal on by default (logs/pipeline.jsonl under the audio work dir). Set 0 to disable
LOUIS_PIPELINE_LOG_VERBOSE 1 adds yt-dlp -v and larger stderr excerpts for a short capture window

After capturing, copy logs/ off the volume if needed and run:

npm run diagnose:pipeline -- /data/audio
npm run diagnose:pipeline -- --video dQw4w9wgXcQ --since 7d
docker run -p 4000:4000 --env-file .env louis:local

4. Deploy constraints

  • Single instance — save-job progress is in memory
  • HTTPS in production — Docker image defaults OAuth cookies to LOUIS_COOKIE_SECURE=false (LAN HTTP). Node-without-Docker defaults to secure when NODE_ENV=production. Set true behind TLS / reverse proxy; keep false for plain HTTP (Portainer LAN, Home Assistant)
  • Persistent disk — recommended for the audio cache under LOUIS_AUDIO_WORK_DIR (cache/preview/, cache/save/, logs/). Stale jobs/ dirs and old cache files are swept on startup and after downloads; pipeline logs rotate by size and are not swept with the cache. Compose uses the named volume louis-audio

Native development

For local Node (without Docker), install these first:

  • Node.js 22+ (also used as yt-dlp’s JS runtime for YouTube signing)
  • yt-dlp — required for search (without a Data API key) and for save; keep it current
  • ffmpeg — required for save
  • Optional: LOUIS_YOUTUBE_API_KEY (faster search + LOUIS_YOUTUBE_SAFE_SEARCH); LOUIS_YTDLP_COOKIES_FILE as above

Self-host web UI has no YouTube key field — set LOUIS_YOUTUBE_API_KEY and LOUIS_YOUTUBE_SAFE_SEARCH in .env (desktop app: Settings → Advanced).

npm install
cp .env.example .env
npm run dev

Dev server: port 4000. Tests: npm test (see CONTRIBUTING.md).

Production without Docker:

npm run build
npm run start

License & notices

MIT — see LICENSE.

Fonts (LT Saeada, self-hosted), OpenMoji icons, and SND UI sounds are used; see THIRD_PARTY_NOTICES.md.

Security reports: SECURITY.md.

About

Louis! A Yoto Make Your Own (MYO) client backed by YouTube

Topics

Resources

Contributing

Security policy

Stars

48 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages