mdcor
A small CLI that cleans up messy media metadata using an LLM. It walks a folder
of audio/video files, asks an LLM (OpenAI / DeepSeek / Claude compatible) to
re-derive correct tags from the filename and any existing (often wrong) tags,
writes them back in place via ffmpeg, and groups related files into .m3u8
playlists.
Designed to make structure out of chaos — e.g. a file named
AI Lil Peep - BeamerToy [x123].mp3 with tags pointing at the wrong artist
gets corrected to artist Lil Peep, title BeamerToy, and grouped into a
playlist with its siblings.
How it works
folder ─► walk (recursive) ─► ffprobe read every file ─► build Library
─► LLM correction (fed library context as a prior)
─► snap spelling variants to canonical forms
─► ffmpeg rewrite (stream copy, no re-encode)
─► group by artist/album/show ─► .m3u8 playlists
The engine runs in two phases so it can be library-aware:
- Read everything, then build a
Libraryof the existing artists, albums, genres, and pre-existing.m3u8playlists under the folder. - Correct each file: feed the LLM the established spellings as a prior ("you already have Lil Peep, Chief Kelly, ... — prefer these exact spellings"), then snap variants to a single canonical form so they group together. Finally write tags and emit playlists.
Unifying messy spellings (the headline feature)
Files scraped from YouTube often have the uploader tagged as the artist while the real artist is buried in the filename, with arbitrary casing/punctuation. mdcor collapses all of these onto one identity for grouping:
| On disk / in tags | Canonical key | Final artist | Playlist |
|---|---|---|---|
AI Lil Peep - Song A.mp3 |
lil peep |
Lil Peep |
Lil Peep - Singles |
Lil Peep - Song B.mp3 |
lil peep |
Lil Peep |
(same playlist) |
lil peep - Song C.mp3 |
lil peep |
Lil Peep |
(same playlist) |
Lil Peep feat. Lil Tracy - D.mp3 |
lil peep |
Lil Peep |
(same playlist) |
Canonicalization is deterministic and conservative: lowercase, strip
parenthetical/junk tokens ((Official), [HD], lyrics, amv, ...), drop
AI / The prefixes, take the primary artist before x / & / feat.
separators, and remove punctuation. The display spelling is the most frequent
original form, tie-broken toward the shorter/cleaner one.
The same idea applies to video shows: Some Show S01E01 and SomesHow s01e02 both land in Some Show S01.m3u8.
Package layout
- audio and video are root-level packages; each format (mp3, flac, mp4, mkv, ...) is a subpackage that registers itself at init time.
- ffmpeg is the universal backend:
ffprobereads tags,ffmpeg -codec copyrewrites them. One metadata schema, every format. - engine is the core: it depends only on the
llm,audio, andvideointerfaces plus a small FS walker, so it's fully testable with no network and no real media. - llm has an OpenAI-compatible client (covers OpenAI + DeepSeek) and a native Anthropic Claude client.
Install / build
make build # → bin/mdcor
Requires ffmpeg and ffprobe on PATH.
Usage
./bin/mdcor \
--provider deepseek \
--model deepseek-chat \
--api-key $DEEPSEEK_API_KEY
Flags
| Flag | Description |
|---|---|
--provider |
openai, deepseek, or claude (default openai) |
--url |
Base URL override (otherwise provider default) |
--model |
Model name (required) |
--api-key |
API key, or set MDCOR_API_KEY |
--folder |
Target folder. If omitted, processes ~/Music and ~/Videos recursively |
No other flags. Recursive walking is always on.
Examples
# Claude, native Messages API
./bin/mdcor --provider claude --model claude-3-5-sonnet-20241022 --api-key $ANTHROPIC_API_KEY
# OpenAI-compatible custom gateway
./bin/mdcor --provider openai --url https://gateway.local/v1 --model gpt-4o-mini --folder ~/Music
Supported formats
- Audio: mp3, mp2, flac, ogg, oga, m4a, m4b, m4r, alac, opus, wav, wave
- Video: mp4, m4v, mov, mkv, mka, webm, avi
Development
make test # go test -cover ./...
make lint # gofumpt + golangci-lint
Integration tests use small (~12-16KB) real media clips checked into
testdata/media/ (2-second excerpts of representative messy filenames). Unit
tests use a fake LLM and a fake ffmpeg runner, so they never touch the network
or the real ffmpeg binary.