Browse a Jellyfin music library, read artist / album / track metadata from the Last.fm API, keep a persistent local archive of every Last.fm response, review a field-level diff, and apply the accepted changes back to Jellyfin.
It is a self-hosted tool for one person with a music library and a taste for tidier metadata — not a sync service and not a plugin.
Library → candidates → diff → apply → undo.
Four properties hold on every write, and each is tested:
POST /Items/{id} is a
full overwrite, so a field omitted from the body is nulled. Anything you did not
select is carried through at its current value instead.Etag is refused with 409 and nothing is written, so a reviewed change
cannot silently undo an edit made in Jellyfin meanwhile.Two more things are deliberate, not missing:
You need Docker with Compose, a Jellyfin server, and a Last.fm API key:
| Requirement | Details |
|---|---|
| Jellyfin ≥ 10.9 | The item-update semantics the write path depends on. |
| Jellyfin administrator API key | Dashboard → API Keys. Metadata writes are gated on the RequiresElevation policy, so a non-admin key can browse but not save; GET /api/info tells you which one you have. |
| Last.fm API key | From https://www.last.fm/api/account/create. Read-only usage — no user auth. |
| Postgres 18 | Runs in the compose stack; required for the archive’s declarative monthly partitioning. |
git clone https://github.com/sredevopsorg/metaedit.git
cd metaedit
cp .env.example .env
# edit .env: JELLYFIN_URL, JELLYFIN_API_KEY, LASTFM_API_KEY, POSTGRES_PASSWORD
docker compose up -d --build
Then open http://127.0.0.1:8080.
The app binds to
127.0.0.1on purpose. It holds an administrator Jellyfin key and has no authentication of its own. Put an authenticating proxy in front of it before exposing it anywhere.
The stack runs Postgres, applies migrations as a one-shot migrate service, and only
then starts the app. The first visit is the Library screen: search for something, open
an item, fetch its Last.fm candidates, and review the diff.
Every knob the app reads is listed with its default in the
configuration reference; .env.example is the template to copy
and the reference is the complete list.
| Doc | For |
|---|---|
| Configuration | Every environment variable, its default, and what it changes |
| Development | Running from a checkout, checks, tests, the API contract |
| API | Every endpoint, and the rules the write path follows |
| Operations | The archive, the CLI, retention, live-verified server behaviour |
| Architecture | Status, the UI, layering, known deviations |
| ADRs | Why the design is what it is |
| Design notes | Implementation contracts fixed ahead of a phase |
| Changelog | What changed, release by release |
Metadata originates from Last.fm. The UI links every Last.fm-derived value back to the corresponding artist, album or track page on https://www.last.fm, as the API Terms of Service require. This tool is for personal, non-commercial use.