Skip to content

Repository files navigation

Themearr

Automatic movie theme song downloader for Plex, Jellyfin, Emby and Kodi libraries.

Releases · Proxmox Scripts · Reddit


What it does

Themearr reads your movie library and helps you add a theme.mp3 to every movie folder — the file Plex, Jellyfin, Emby and Kodi use to play background music while browsing.

It reads your library from either Plex or Radarr. Radarr is a first-class source: if you don't use Plex, you never need a Plex account to run Themearr.

  • Read your library from Plex (OAuth sign-in) or Radarr (URL + API key)
  • Browse your whole library as a poster grid
  • Auto-search YouTube for each movie's theme
  • One-click download to theme.mp3
  • Automatic background downloading across the whole library
  • Paste any video URL to use a custom source
  • Downloaded status tracked per movie, verified against what's on disk
  • System page with health checks and scheduled tasks, arr-style
  • An API key plus a Radarr webhook, so a theme is fetched the moment a movie is imported

Disk usage

Themes are stored full-length as received — there is no trimming or re-encoding — which works out to roughly 3.7 GB of theme audio per 1,000 films (measured on one production install: 1,438 films, 5.28 GB). Plan for that growth on the volume holding your media folders. Plex users: Plex also imports a copy of each theme into its own metadata bundle, so the space is used twice on the volume holding Plex's metadata directory.

Downloads require a RapidAPI key

Theme audio is fetched through the youtube-mp36 API on RapidAPI — Themearr does not bundle or shell out to yt-dlp or ffmpeg. Downloads will not work until you add your RapidAPI key and username in Settings → RapidAPI. Signing in and browsing your library work without it — only downloading a theme.mp3 needs it. The free RapidAPI tier is quota-limited, so Themearr backs off automatically when the quota is exhausted.

Install

Proxmox LXC (one-line)

Run this on your Proxmox host:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/Themearr/ProxmoxVE/main/ct/themearr.sh)"

The installer generates an access token, prints it at the end, and saves a copy to /root/themearr.creds. Open http://<container-ip>:8080, enter the token, then pick your library source — sign in with Plex, or choose "I don't use Plex" to connect Radarr instead.

Docker

A multi-arch image (amd64 / arm64) is published to GHCR on every release.

# 1. Get the compose file
curl -fsSL https://raw.githubusercontent.com/Themearr/themearr/main/docker-compose.yml -o docker-compose.yml

# 2. Generate the required access token
echo "THEMEARR_AUTH_TOKEN=$(openssl rand -hex 32)" > .env

# 3. Edit docker-compose.yml — point the movie volume at your library:
#      - /path/to/your/movies:/movies
#    It must be WRITABLE (no ":ro") — Themearr writes theme.mp3 into movie folders.

docker compose up -d

Open http://127.0.0.1:8080 and enter the token from .env.

The compose file publishes the port to 127.0.0.1 only. For remote access, put a reverse proxy (Caddy/nginx) in front with its own TLS and auth.

Configuration

Access token (required)

The API refuses to start without THEMEARR_AUTH_TOKEN — there is no unauthenticated mode. The Proxmox installer generates one for you; for Docker you set it yourself (see above). Every client enters this token once.

Library paths & path mappings

This is the setting people most often get wrong, and it's what causes Skipping <title> — unresolved path during sync.

Themearr writes theme.mp3 into your movie folders, so it has to reach your files at a path it can see — which is usually not the path Plex reports.

  • Local Library Paths — where your movie folders live as Themearr sees them (e.g. /movies in Docker, or /mnt/media/Movies in an LXC).
  • Path Mappings — translate the path Plex reports into the path Themearr sees.

Example — Plex on Windows, Themearr in Docker:

Plex reports Themearr sees Mapping to add
P:\Movies\Heat (1995)\heat.mkv /movies/Heat (1995) P:\Movies/movies

If sync logs Skipping <title> — unresolved path: <path>, that logged path is exactly what Plex reported — map its parent folder to wherever it's mounted in Themearr. Windows-style (\) paths are handled automatically.

Also make sure the movie mount is writable — a read-only mount resolves fine but silently fails every download.

Health checks

System → Health flags the things that silently break downloads: a library path that is missing or read-only, a Plex server that is unreachable or has rejected its token, a missing RapidAPI key, an exhausted quota, and a stalled auto-download worker. Only problems are listed, so an empty page means everything is fine.

System → Tasks shows when the library last synced and lets you trigger a sync immediately with Run now.

For external monitoring, Themearr exposes an unauthenticated /health endpoint that returns {"status":"Healthy"} and nothing else — enough for Uptime Kuma or Gatus, without leaking any configuration.

Library source: Plex or Radarr

Themearr can read your movie list from either Plex or Radarr, chosen during setup and changeable later under Settings → Library source.

Radarr is what makes Themearr useful without Plex. It already knows every movie's folder, title, year and whether the film is downloaded — and because theme.mp3 is read by Jellyfin, Emby and Kodi as well as Plex, a Radarr-sourced library serves all of them. You need a Radarr URL and API key (Radarr → Settings → General → API Key).

Radarr is local and cheap to poll, so it syncs every 15 minutes rather than daily — a newly imported movie usually has its theme within minutes.

Movies Radarr is monitoring but has not downloaded yet are skipped: there is no film for a theme to accompany. Path Mappings work exactly as they do for Plex, and are often needed, since Radarr in a container reports its own paths.

If you already have Themearr set up

Note for existing installs (v1.42.0). You don't need to do anything. Themearr stays on Plex unless you change it — the library source defaults to Plex, your setup stays complete, and you won't be asked to run the wizard again.

Only read on if you plan to switch an existing library from Plex to Radarr.

Themearr identifies a movie by the folder its theme lives in, and both sources describe the same folders on disk, so anything both of them report keeps its downloaded status and its place in your history.

The catch is anything Radarr doesn't report. Radarr only knows about movies it manages, so hand-added rips, a second library, or anything imported before you started using Radarr are invisible to it — and the first Radarr sync removes those rows. Concretely:

  • Movies you've ignored are kept, whichever source you're on.
  • A removed movie's downloaded status is not lost in practice — status is read from whether a theme.mp3 is actually on disk, so if the file is still there it comes back as downloaded the moment the movie reappears.
  • Its history entries are orphaned, though. They still show the film's title and year on the History page, they just no longer link to a movie.

So switching is safe if Radarr manages your whole library, and lossy at the edges if it doesn't. Check Radarr's movie count against Themearr's before you switch.

API key and Radarr webhook

Settings → API key shows a key that external tools can use to talk to Themearr. Send it as an X-Api-Key header on any /api/… request:

curl -H "X-Api-Key: <your key>" http://themearr:8080/api/system/tasks

Warning: this is a full-access credential, not a read-only one. It authenticates exactly like the access token you sign in with, on every /api/* endpoint except the two that manage the key itself. Whoever holds it can reset setup, trigger an update that restarts the service, and overwrite your stored Plex token, Radarr API key, and RapidAPI key — the only thing it can't do is read or regenerate itself. Handle it with the same care as the access token, and don't paste it anywhere you wouldn't paste that.

It is separate from the access token you sign in with, so you can regenerate it without logging anyone out — and regenerating immediately stops the old one working.

Fetching themes the moment Radarr imports

Instead of waiting for the next sync, have Radarr tell Themearr directly. In Radarr: Settings → Connect → Add → Webhook, then set:

Field Value
Notification Triggers On Import (also tick On Upgrade if you want)
URL http://themearr:8080/api/webhook/radarr
Method POST
Headers X-Api-Key = your key from Settings → API key

Press Test — Themearr answers, so a wrong URL or key shows up immediately rather than at the next import.

Importing several movies at once is fine: Themearr collapses the burst into a single sync rather than one per movie.

Two caveats:

  • This is most useful when Radarr is your library source. If you use Plex as the source and Radarr only downloads, the webhook still fires — but Plex may not have scanned the new file yet, so the theme may still wait for a later sync.
  • Radarr builds from before custom webhook headers were added (upstream, late 2024) cannot send X-Api-Key.

Updating

  • In-app: Settings → Updates. Downloads the latest release, preserves your data, and restarts.
  • Docker: docker compose pull && docker compose up -d
  • Proxmox / bare metal: the in-app updater, or re-run the community install script.

Upgrading a pre-.NET-10 install: releases from v1.39.10 onward need the ASP.NET Core 10 runtime. Containers created earlier were provisioned with .NET 9, so install the runtime first:

curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh
bash /tmp/dotnet-install.sh --channel 10.0 --runtime aspnetcore --install-dir /usr/share/dotnet

If you forget, nothing breaks — the updater checks for the runtime before changing any files and aborts with these instructions, leaving your running install untouched. Docker is unaffected (the runtime ships in the image).

Tech stack

Layer Technology
API .NET 10 Web API (ASP.NET Core, LTS)
Frontend React 19 + Vite (static SPA, served by .NET)
Routing React Router
Database SQLite via Microsoft.Data.Sqlite
YouTube search YoutubeExplode
Theme download youtube-mp36 RapidAPI
Tests xUnit

Local development

Requirements

Run

# Terminal 1 — API (set any token you like for local dev)
THEMEARR_AUTH_TOKEN=dev-token-at-least-16-chars dotnet run --project src/Themearr.API

# Terminal 2 — Frontend (dev server with proxy to API)
cd src/Themearr.Web
npm install
npm run dev   # proxies /api to the .NET backend on :5000

Open http://localhost:3000. The frontend is a static SPA — in production it's built to src/Themearr.Web/out/ and served by the .NET app from wwwroot (with an SPA fallback, so deep links like /movies work).

Checks

dotnet test                  # .NET test suite

cd src/Themearr.Web
npm run lint                 # ESLint
npx tsc --noEmit             # typecheck
npm run build                # production build -> out/

Building a release

Push to main — GitHub Actions will automatically:

  1. Detect the semver bump from commit messages (feat: → minor, major: → major, else patch)
  2. Build the frontend (Vite) and publish .NET for linux-x64 and linux-arm64
  3. Bundle the frontend into each publish output
  4. Create a GitHub release with both tarballs plus SHA-256 checksums (verified by install.sh / deploy.sh)
  5. Build and push the multi-arch Docker image to ghcr.io/themearr/themearr (:latest and :vX.Y.Z)

Changes that don't affect the shipped app (docs, .gitignore, workflows) don't cut a release.

Versioning

Releases follow semantic versioning driven by commit message prefixes:

Prefix Bump
feat: minor
major: / BREAKING CHANGE major
anything else patch

License

MIT

About

Automatic movie theme song downloader for Plex, Jellyfin, Emby and Kodi. Reads your movie library from either Plex or Radarr and writes a theme.mp3 into every movie folder. Self-hosted, arr-style: health checks, scheduled tasks, an API key and a Radarr webhook. No Plex account required if you use Radarr.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages