lazy-catalog
A zero-dependency catalogue for a film folder, combining TMDB metadata with ffprobe facts read out of the files themselves.
Point it at a folder of films and series and it gives you back a readable table, an offline web page, and a CLI that tells you what to watch. Runtimes, genres and ratings come from TMDB. Resolution, codecs and embedded subtitles come from ffprobe reading your actual files. A background job notices when the folder changes and rebuilds the catalogue, and a new title is not published until its file size stops changing, so a half-finished download never lands in it.
The rule underneath is that the model is never asked for a fact. It cleans up folder names nobody can read and writes mood tags, which are opinions. Everything else is verified: 299 tests, no Python dependencies, and a pick command where the model chooses a number from a list it was handed, so it cannot recommend a film you do not own. Suggest does the opposite, reading the library as a taste profile and checking every candidate on TMDB before you see it.
What is left is macOS-only, the release-name parser is rules rather than certainty, and the optional pieces fail quietly: no Ollama means no mood tags, no subliminal means no subtitles.
- 1 299 tests Covered by unittest, run with a single discover command.
- 2 Facts from files Codecs, resolution and subtitles come from ffprobe, not from a filename.
- 3 Picks by number The model chooses from a list it is handed, so it cannot invent a title.
- 4 Never a fact The model only cleans up unreadable folder names and writes mood tags.
Overview
What I built
- +A browsable offline library page plus a CONTENTS.md, both rebuilt when the folder changes.
- +A watchlist of films you do not own, each one looked up on TMDB before it is shown to you.
- +Subtitle downloads capped at 25 files a run, because the free providers are rate limited.
What I rebuilt
- ~Defaulted to a 12B model: a 24B measured 20 GB resident and 12.7 s a call against 8.6 GB and 4.7 s.
- ~Switched reasoning off after gemma4 spent 126 tokens and 11.6 s on a one-word answer with it on.
- ~Rewrote release-name parsing to end a title at season markers in any spelling and read a bracketed year as the release year.
Known limitations
- !macOS only, and the library page needs the CLI running: a page opened off disk cannot launch VLC.
- !A year in a title is guesswork: (2017) wins over a bare number, and a year that has not happened is part of the name.
- !Ollama and subliminal are optional, and each one degrades gracefully when missing.
Decisions
- 1. The model never states a fact I chose take runtimes, genres and ratings from TMDB and codecs from ffprobe, Instead of asking the local model for a runtime or a rating, Because a small model asked for a runtime will confidently invent one, and a wrong fact is not an opinion..
- 2. Serve the page, do not open it I chose serve the library page from 127.0.0.1 and keep it running until you stop it, Instead of opening the generated page straight off disk with file://, Because VLC registers no URL scheme on macOS, so a file:// page has no way to launch anything..
- 3. Let the model choose an index I chose hand the model the catalogue as a numbered list and check every number against it, Instead of letting the model name a title from memory, Because every number is checked, so it cannot recommend a film that is not in the library..
Timeline
| Version | Date | Description |
|---|---|---|
| v0.1.0 | 2026-08 | Library scanner, release-name parser, cache and the CONTENTS.md renderer. |
| v0.1.0 | 2026-08 | TMDB and ffprobe enrichment, the web view, lazy-pick, the watcher and .nfo sidecars. |
| v0.2.0 | 2026-09 | lazy-suggest added: films you do not own, each verified against TMDB and saved to a watchlist. |
| v0.2.0 | 2026-09 | Trash-backed delete, Rotten Tomatoes scores, and versioned enrichment. |