Turn photos of your media collection — movies, music, books, video games & audiobooks — into a sleek, searchable web catalog.
Project description
🎬🎵📚🎮🎧 MediaHound
Turn photos of your media collection into a sleek, searchable web catalog — movies, music, books, video games & audiobooks.
Point MediaHound at a folder of cover photos — DVDs, VHS, Blu-ray, CDs, vinyl, cassettes, books, game boxes, audiobooks — or import a CSV. It identifies each item, pulls in cover art, genres, the right creator (cast/artist/author/developer/narrator), studio/label/publisher, runtime/tracklist/ duration and ratings, writes a short enticing intro, estimates the used resale value, links where to watch (movies), listen (music), find (books), play/buy (games) or hear (audiobooks), and generates a polished static website you can search, filter by 🎬 Movies / 🎵 Music / 📚 Books / 🎮 Games / 🎧 Audiobooks, sort, curate, and print to a PDF inventory — with a password-protected admin mode.
Everything is zero-key by default: movies via TMDB / OMDb / Wikidata + JustWatch; music via MusicBrainz + Cover Art Archive; books via Open Library (scan an ISBN for the exact edition); video games via the Wikidata query service (scan a UPC) with PriceCharting used prices; audiobooks via Open Library + LibriVox (narrator + length). Keyless Spotify / Apple Music / YouTube Music, Open Library / Goodreads, Steam / eShop / PS Store, and Audible / Libro.fm links throughout. Adding a new media type is one entry in a shared registry — not a new branch everywhere.
▶ Live demo — explore a sample catalog in your browser (admin password: changeme — demo only; pick your own when you build).
🖥️ Download the desktop app — no Python, no terminal:
⬇ macOS
⬇ Windows
— unzip and open. (Or pip install mediahound — see below.)
The live demo runs the same static output MediaHound generates. Full-size screenshot.
ℹ️ The demo shows real cover art for every type — movie posters (IMDb/OMDb), album covers (Cover Art Archive / Apple), book & audiobook covers (Open Library), and game box art (Steam) — all hotlinked so you can see what a finished catalog looks like; no cover images are stored in this repo. Your real catalog pulls art from each type's provider, or falls back to the photos you take.
- Runs for anybody with zero API keys — open-source OCR + open data by default.
- Offline-first — never contacts the internet unless you explicitly ask (
--online). - Static output — deploy anywhere (Netlify, GitHub Pages, S3, Vercel) or just open the HTML file.
- No secrets in the repo — keys live in a gitignored
.env; your catalog is generated output.
MIT-licensed. Your photos, keys and catalog never get committed to this tool's repo.
See it in 30 seconds (no photos, no keys)
pip install mediahound
mediahound init demo
mediahound build --config demo/config.toml --mock # generates a sample catalog
cd demo && python3 -m http.server 8000 # open http://localhost:8000
That's the catalog above. Click 🔒 Admin and sign in with changeme (the demo-only
password — set your own in config.toml when you build your real catalog) to try the read/write
admin tools. Everything you see is generated by --mock — no internet, no API keys.
Prefer the bleeding edge or want to hack on it? Install from source instead:
git clone https://github.com/jchirayath/mediahound && cd mediahound && pip install -e .(Maintainers: see RELEASING.md for the publish flow.)
Cataloguing your own collection
The easy way — one command, no terminal after that
pip install mediahound
mediahound app # sets up a library and opens the editor in your browser
mediahound app creates a library folder (if needed), opens the catalog, and you click
➕ Add photos to drag-and-drop your cover pics — they're saved and identified automatically.
No config files, no separate build/serve commands. Everything stays on your computer.
Snap covers or barcodes on your phone:
mediahound app --phone # opens to your Wi-Fi and prints a QR code
Scan the QR with your phone (on the same Wi-Fi), tap ➕ Add by photo → Take Photo, and snap the cover — or the barcode (UPC/EAN/ISBN) for an exact-release match. MediaHound decodes the barcode server-side from the photo, so it works on any phone over plain Wi-Fi — no browser camera permission or HTTPS needed (handy, since the live in-browser scanner needs HTTPS and isn't supported on iOS). Uploads are token-protected (only the phone that scanned the code can add) and nothing leaves your network — use it on a network you trust.
Desktop app (no Python, no terminal)
Prefer to double-click an icon? A native app opens the editor in its own window:
- Download: ⬇ macOS (.app) · ⬇ Windows (.exe) — or pick a specific version on the Releases page. Unzip, and open. (Unsigned builds show a Gatekeeper/SmartScreen prompt the first time — right-click → Open on macOS, or More info → Run anyway on Windows.) To ship builds that open with no warning, see SIGNING.md.
- Or, if you've got Python:
pip install "mediahound[desktop]"thenmediahound guiopens the same native window. (Without the[desktop]extra it falls back to opening your browser.) - Build it yourself:
bash packaging/build-desktop.sh(uses PyInstaller; build on the OS you want). CI builds the macOS + Windows apps automatically — see.github/workflows/desktop.yml.
The app keeps your library in ~/MediaHound Library and works fully offline.
Share it — one-click Publish
When your catalog's ready, click 🌐 Publish in the admin console to deploy it to Netlify (free) and get a shareable link. Paste a Netlify access token once (stored in your OS keychain); only the finished site is uploaded — your source photos and config never leave your computer.
The CLI way (more control)
pip install "mediahound[ocr]" # adds the default OCR identifier
# Install the Tesseract engine for OCR:
# macOS: brew install tesseract
# Debian: sudo apt-get install tesseract-ocr
mediahound init mysite # scaffolds mysite/ (RawImages/{video,audio,books,games,audiobooks}/, config.toml, web template)
# Sort your cover photos by media type:
cp ~/Pictures/dvd-covers/*.jpg mysite/RawImages/video/ # 🎬 movies (DVD/VHS/Blu-ray/LaserDisc)
cp ~/Pictures/album-covers/*.jpg mysite/RawImages/audio/ # 🎵 music (CD/vinyl/cassette)
cp ~/Pictures/book-covers/*.jpg mysite/RawImages/books/ # 📚 books (scan the ISBN)
cp ~/Pictures/game-boxes/*.jpg mysite/RawImages/games/ # 🎮 games (scan the UPC)
cp ~/Pictures/audiobooks/*.jpg mysite/RawImages/audiobooks/ # 🎧 audiobooks
mediahound build --config mysite/config.toml --online # identify + enrich (see Providers below)
cd mysite && python3 -m http.server 8000
Raw-image folder convention
Photos are sorted into media-type subfolders so MediaHound knows how to identify each item:
| Folder | Media type | Identified/enriched via |
|---|---|---|
RawImages/video/ |
🎬 movies | TMDB / OMDb / Wikidata + JustWatch |
RawImages/audio/ |
🎵 music | MusicBrainz + Cover Art Archive + listen links |
RawImages/books/ |
📚 books | Open Library (ISBN or title+author) + find links |
RawImages/games/ |
🎮 video games | Wikidata (title/UPC) + where-to-play + PriceCharting |
RawImages/audiobooks/ |
🎧 audiobooks | Open Library + LibriVox (narrator/length) + listen links |
RawImages/ (root) |
defaults to movies | — |
(movies/, music/, book/, game/, audiobook/ are accepted aliases.) Add more photos anytime
and re-run build — only the new ones are processed (state is tracked by content hash in
data/manifest.json).
Or import from a CSV (no photos)
mediahound import catalog.csv --config mysite/config.toml # add rows offline
mediahound import catalog.csv --config mysite/config.toml --online # …and fetch cover art + metadata
mediahound export --config mysite/config.toml -o backup.csv # dump the whole catalog back to CSV
Columns (case-insensitive; extras ignored): media_type, title, artist, author, narrator, developer, director, year, format, label, publisher, studio, platforms, tracklist, genres, rating, barcode, isbn, pages, duration, cover_url, intro. Only title is required — any missing fields are left blank
(or filled by --online); even a one-column list of titles works. media_type is inferred
(audiobook if a narrator is given, game if a developer, book if an author, music if an
artist, else movie). See examples/sample-import.csv.
Prefer a UI? Under mediahound serve --admin the admin screen has an ⬆ Import list button —
paste or upload the same CSV, optionally tick enrich online, and the titles are added and the site
rebuilt in place.
Features
Five media types, one catalog
- 🎬 Movies · 🎵 Music · 📚 Books · 🎮 Video games · 🎧 Audiobooks — segmented tabs appear once your catalog has more than one type. Each renders the right fields (cast vs. artist vs. author vs. developer vs. narrator; studio vs. label vs. publisher; runtime vs. tracklist vs. duration) from a shared media-type registry — and you can move a title between any types in admin.
The catalog
- Search title / creator / genre / studio / intro — and song titles (a track on an album surfaces it); sort by title, year, recently-added, value or rating.
- Filters: format, genre, studio, streaming service, language, category, seen / unseen.
- Dense, aligned cards showing cover, title·year, ★rating · format · (runtime/tracks/duration), genres, the creator(s), studio/label/publisher, where-to-watch/listen/find/play, intro hook, and estimated resale value. Music cards expand to a collapsible tracklist.
- Clickable everything: a genre, person, studio, or narrator filters the grid to matching titles.
- Adjustable density — viewers pick how many per row; mobile-first below 640px (filters behind a toggle, condensed header, big tap targets, a two-column grid).
- Built for big libraries — the grid is virtualized (renders as you scroll), and large title-sorted lists get an A–Z jump rail so you never scroll through thousands of items.
Photos
- Multi-photo galleries — flip through every photo of a title with ‹ › arrows.
- Click-to-zoom lightbox; set any photo as the default; rotate photos (baked in on rebuild).
- Auto-uprights sideways/landscape cover photos to portrait.
Where to watch / listen / find / play & resale
- Where to watch — is it on Netflix / Amazon Prime / Hulu? A clickable ▶ badge + pills link straight to the title (via JustWatch, no key). A filter narrows to a specific service.
- Where to listen / find / play / hear — keyless deep links per type: music → Spotify / Apple / YouTube Music; books → Open Library / Goodreads / Google Books; games → Steam / eShop / PS Store / Xbox / MobyGames (chosen by platform); audiobooks → Audible / Libro.fm / LibriVox / Open Library.
- Resale value — a heuristic estimate plus a live link to eBay sold/completed listings. Music with a Discogs release id gets a condition-based Discogs price suggestion (token-gated); video games get a PriceCharting price-check link with platform-aware baselines (retro and Switch hold value).
📷 Barcode scanning (exact, not fuzzy)
- Identify the exact release from the UPC/EAN/ISBN barcode instead of fuzzy OCR. Three ways:
(1) photograph the barcode — drop/snap a photo of it via ➕ Add by photo (works from a phone
over plain Wi-Fi; the barcode is decoded server-side); (2) type the digits in 📷 Scan
barcode; (3) any barcode found in a photo on
mediahound build --onlineis preferred over OCR. Music → MusicBrainz/Discogs; books → Open Library (ISBN); games/movies → UPCItemDB → the normal identify-by-title path. The decoder (zxing-cpp) ships in the core install — no extra needed. (A live in-browser camera scanner also exists, but it requires HTTPS and isn't supported on iOS — photographing the barcode is the portable path.)
💿 Discogs (records & CDs)
- Import an existing Discogs collection in one step:
mediahound import-discogs <username>(or the 💿 Discogs admin button). Selectable as a music metadata provider ([music.metadata] provider = "discogs"). Token stored in your keychain via Settings → API keys.
⭐ Your personal catalog (admin-only, never published)
- Rate (★1–10), note, and tag/shelve any item; track lending (loan out → badge → returned), filter On loan / Available, and hit 🎲 Surprise me to pick something for tonight. All of this is stripped from the published site — it shows only in your local admin view.
🖨 Printable inventory (PDF)
mediahound export --format inventorywrites a clean, self-containedinventory.html— grouped by media type, with a per-type and grand-total estimated value — that you Print → Save as PDF (great for insurance or offline sharing). The admin ⤓ Export menu has a one-click 🖨 Printable inventory (PDF) button that does the same from the app (works on a static copy, zero dependencies).
🧾 Change log
- Every add / remove / change is recorded in a compact, append-only
data/events.jsonl(integer timestamp, one-char op, changed-field names only — tiny and privacy-safe; admin-only, never published). Review it withmediahound log.
🛟 Backup, export & feeds
mediahound backup/restorezip up (and re-create) your whole library —--no-photosfor a quick curation-only backup; secrets are never included. A ⬇ Backup button does the same from the app.- Export to Letterboxd (movies), JSON, CSV, or a printable inventory:
mediahound export --format letterboxd|json|csv|inventory(or the ⤓ Export menu). The published site also emitsfeed.json+feed.xmlof recently-added items so anyone can subscribe. - 📚 Library switcher — keep separate catalogs (e.g. movies vs. music, or per-family-member) and
open / create / switch between them from the admin UI with no restart (a recents list lives in
~/.config/mediahound/). Each library's data directory is set by its ownconfig.toml[paths].
Two views
- Default view — public, read-only.
- Admin view — password-protected, read/write. Edit a title's name, year, format, and the type-specific fields (artist/author/narrator/developer, studio/label/publisher); move a title between any media type (🎬 / 🎵 / 📚 / 🎮 / 🎧); mark seen/played/read; rotate / set-default / delete a photo; delete a title; and configure the library name, description, logo, which fields are shown, and default columns.
Editing & persisting your changes
Your edits are recorded as small corrections (keyed by title id). There are two ways to make
them permanent so they survive every future mediahound build — pick one:
A. Live admin server (recommended — zero manual steps)
mediahound serve --admin # serves the site at http://127.0.0.1:8765
Open the site, unlock admin, and edit. Every change is written straight into
data/corrections.json (and seen-overrides.json) as you go — the badge shows
“✓ Saved to disk.” Click ↻ Rebuild to re-bake the catalog and reload. Because the edit is
already in data/, the next mediahound build (and any re-query) keeps it — edits never revert.
The write API is localhost-only and refuses cross-origin requests; never expose it publicly.
B. Static export (for read-only/CDN hosting like Netlify or GitHub Pages)
When the site is served as plain files (no admin server), edits live in your browser. Click
Export changes / Export seen — the download is merged with the site's existing
data/corrections.json so nothing already saved is lost — then drop the file into data/ and run
mediahound build.
Either way the source of truth is
data/corrections.json. A title you fix only in the browser (without server-admin or an export) shows locally but reverts on the next rebuild, because the build regenerates the catalog fromdata/.
Manual identification
- Covers that couldn't be read are grouped on
identify.html, where you name them (queued for discovery on the next online build) or discard them (e.g. blank tapes).
How it compares
Most collection tools are single-category (often movies-only — and CLZ even charges per category), add items by barcode scan or manual entry, and keep your catalog in their cloud or a dated desktop app. MediaHound puts all five media types in one catalog — 🎬 movies, 🎵 music, 📚 books, 🎮 games, 🎧 audiobooks — identifies titles from photos of the covers (and scans barcodes — UPC/EAN/ISBN — and imports CSVs too), and generates a modern static website you own and host for free, offline-first and open-source. So it does everything the barcode apps do plus photo-identification — the only practical path for bulk-photographing a shelf and for VHS, which usually has no scannable barcode in the disc databases others rely on.
| MediaHound | CLZ / Libib | Tellico / GCstar | Plex / Jellyfin | |
|---|---|---|---|---|
| Movies + music + books + games + audiobooks | ✅ all five, one catalog | ◻︎ per-category (CLZ) / multi (Libib) | ✅ many types | ❌ video files only |
| Add by photo of cover | ✅ OCR/AI (+ barcode & CSV) | ❌ barcode/manual | ❌ search/manual | ❌ scans video files |
| Modern website you host free | ✅ | ❌ their cloud | ◻︎ dated HTML export | ❌ private server |
| Open-source / offline / no account | ✅ | ❌ | ✅ desktop | ✅ (Jellyfin) |
| For a physical shelf | ✅ | ✅ | ✅ | ❌ digital files |
See COMPARISON.md for the full, honest analysis — including when a barcode app (CLZ/Libib), an OSS desktop cataloger (Tellico/Data Crow), or a media server (Plex/Jellyfin) is the better choice.
Providers (how titles get identified & enriched)
Both paths are first-class — pick them per-site in config.toml. The default needs zero keys.
| Concern | Default (no key) | Optional upgrade |
|---|---|---|
| Identify title from a cover | tesseract — open-source OCR |
claude (Anthropic vision, also writes the intro) · ollama (local model) |
| Movie metadata + poster | wikidata — Wikidata + Wikipedia + Wikimedia |
tmdb (free key) · omdb (free key) |
| Music metadata + cover art | musicbrainz — MusicBrainz + Cover Art Archive |
discogs (token: higher rate limit + price suggestions) |
| Book metadata + cover | openlibrary — Open Library (ISBN or title+author) |
— |
| Game metadata + box art | wikidata — Wikidata query service (title/UPC) |
— |
| Audiobook metadata | openlibrary + librivox (narrator/length) |
— |
| Where to watch / listen / play | justwatch (movies) · keyless Spotify/Apple/YouTube, Open Library/Goodreads, Steam/eShop/PS Store, Audible/Libro.fm search |
Spotify / Apple Music keys (planned) |
| Resale | eBay sold-listings link + estimate · PriceCharting for games | Discogs price suggestion (token, music) |
Switch to a premium provider in config.toml:
[identify]
provider = "claude" # needs ANTHROPIC_API_KEY
[metadata]
provider = "tmdb" # needs TMDB_API_KEY (or use "omdb" + OMDB_API_KEY)
…and create a gitignored .env next to config.toml:
ANTHROPIC_API_KEY=sk-ant-...
TMDB_API_KEY=...
Robustness built in: results are cached (data/.metadata-cache.json) so rebuilds never re-hit a
rate-limited free key, providers fail soft (a bad lookup never drops a title), and a fuzzy match
that returns the wrong film is rejected so it can't corrupt your names.
Offline by default
mediahound build is offline — it regenerates the site from existing data and never contacts
the internet. Add --online to allow identification / metadata / where-to-watch lookups:
mediahound build --config mysite/config.toml # offline: just rebuild the site
mediahound build --config mysite/config.toml --online # online: identify + enrich new titles
mediahound build --config mysite/config.toml --online --refresh-streaming # also re-check where-to-watch
Useful flags: --mock (demo data), --force (reprocess everything), --limit N, --reidentify <sha256>.
Deploy
The generated site folder (mysite/) is plain static files (index.html, identify.html,
assets/, data/, posters/, originals/). It's just static files, so you can host it free
on GitHub Pages, Cloudflare Pages, Netlify, Vercel, Render, or Surge.sh — no server, database,
or build step required. Quickest:
cd mysite && npx netlify deploy --prod # Netlify
cd mysite && npx wrangler pages deploy . # Cloudflare Pages
cd mysite && npx surge . # Surge.sh
See DEPLOYMENT.md for the full free-hosting comparison plus GitHub Pages, Vercel and S3 instructions. The live demo above is itself hosted free on GitHub Pages via a workflow.
It even works by double-clicking index.html — the build embeds the catalog in data/bundle.js
so it loads without a web server.
🏠 Self-host a live, editable server (NAS / Docker)
Want the editable master on your home network — view + admin editing + phone uploads at
http://<nas-ip>:8080 — instead of (or alongside) the published static copy? Run MediaHound's
server on an always-on box. See docs/self-host.md for Docker, plus a
native QNAP recipe (no Container Station): a relocatable Python + pip install mediahound + a
watchdog cron for reboot/crash persistence, with MEDIAHOUND_TOKEN for a stable phone-pairing URL.
📱 Install it on your phone (PWA)
A published catalog is a Progressive Web App. Open it in your phone's browser and Add to Home
Screen (on iPhone: the Share button in Safari) — you get an app icon, a full-screen view, and
offline access (the catalog is cached). It auto-updates every time you republish. Browsing is
read-only; editing happens in the local app (mediahound app) and is then published.
Architecture
See ARCHITECTURE.md for the full picture. In short:
RawImages/*.jpg ─▶ identify (OCR / vision) ─▶ enrich (poster, genres, cast, studio, rating)
│
confidence too low? ──────┴─▶ data/unidentified.json → identify.html
▼
+ intro + resale + where-to-watch ─▶ data/collection.json ─▶ static site (index.html)
Python CLI (mediahound/) builds the data; a dependency-free vanilla-JS frontend (mediahound/web/) renders it.
Attribution & licensing
- Code: MIT (see LICENSE).
- Default data: Wikidata (CC0), Wikipedia text (CC BY-SA), images via Wikimedia Commons.
- If you enable TMDB: uses the TMDB API but is not endorsed or certified by TMDB.
- Where-to-watch data via JustWatch; resale links to eBay sold listings (estimates are heuristics).
Security
MediaHound output is a static site — read-only to everyone, with no server to attack. The admin
password is a convenience gate, not an access-control boundary (the published catalog can't be
changed without rebuilding + redeploying). API keys stay in a gitignored .env; only the password
hash ships. All rendered data is HTML-escaped and links are scheme-restricted. See
SECURITY.md for the full threat model and reporting instructions.
Privacy: MediaHound is offline-first with no account and no telemetry — your photos, catalog, and keys stay on your computer; data leaves only when you opt into online metadata, Publish, or phone upload. See PRIVACY.md.
Roadmap: shipped so far — barcode scanning, Discogs, backup/exports, personal catalog, Books / Video games / Audiobooks, album track-info, a printable PDF inventory, and a compact change log (designed in docs/design/). What's next (a value/insights dashboard, "for sale" mode, duplicate detection, …) is in docs/ROADMAP.md.
Contributions welcome — see CONTRIBUTING.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mediahound-0.8.0.tar.gz.
File metadata
- Download URL: mediahound-0.8.0.tar.gz
- Upload date:
- Size: 1.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80bc78e825f272790d9ad46f606a9536f88d8514c4c688698b1b9bf9dc322554
|
|
| MD5 |
b5c3b6cade6e6f2cbbfdb44cadb3f0b0
|
|
| BLAKE2b-256 |
abc37211602859854bf5e234a488bda7e2b6f636a2b901cb4153e279a5725f1b
|
File details
Details for the file mediahound-0.8.0-py3-none-any.whl.
File metadata
- Download URL: mediahound-0.8.0-py3-none-any.whl
- Upload date:
- Size: 979.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
467040e9a4ff2ff54a0b76161a35a34001c31986e9f3c8865ee3359c7533c5b2
|
|
| MD5 |
2c18fa498e300aaa20aa35440591eee1
|
|
| BLAKE2b-256 |
6256bf4b2f43a46a9ae1ee5b89a90d9ee3e5e1d400a9116561708aebe5a562ca
|