Skip to main content

Turn photos of your movie & music collection into a sleek, searchable web catalog.

Project description

🎬🎵 MediaHound

CI CodeQL License: MIT Python 3.11+ Live demo

Turn photos of your movie and music collection into a sleek, searchable web catalog.

Point MediaHound at a folder of cover photos — DVDs, VHS, Blu-ray, CDs, vinyl, cassettes — or import a CSV. It identifies each item, pulls in cover art, genres, cast/artist, studio/label, runtime/tracklist and ratings, writes a short enticing intro, estimates the used resale value, links where to watch (movies) or listen (music), and generates a polished static website you can search, filter by 🎬 Movies / 🎵 Music, sort, and curate — with a password-protected admin mode.

Movies are identified/enriched via TMDB / OMDb / Wikidata + JustWatch; music via MusicBrainz + Cover Art Archive (open, zero-key) with keyless Spotify / Apple Music / YouTube Music links.

Live demo — explore a sample catalog in your browser (admin password: changeme).

MediaHound screenshot

ℹ️ The demo shows real movie posters and album covers (hotlinked from IMDb/OMDb and Cover Art Archive / Apple) so you can see what a finished catalog looks like — no cover images are stored in this repo. Extra gallery photos are generated placeholders standing in for your own photos. Your real catalog pulls art from TMDB / OMDb / Wikidata (movies) and MusicBrainz / Cover Art Archive (music), 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)

git clone https://github.com/jchirayath/mediahound && cd mediahound
pip install -e .

mediahound init demo
mediahound build --config demo/config.toml --mock   # generates a 10-title sample catalog
cd demo && python3 -m http.server 8000              # open http://localhost:8000

That's the screenshot above. Click 🔒 Admin and sign in with changeme to try the read/write admin tools. Everything you see is generated by --mock — no internet, no API keys.

Once published to PyPI you can skip the clone and just pip install mediahound (then mediahound init …). Maintainers: see RELEASING.md for the one-time publish setup.


Cataloguing your own collection

pip install -e ".[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}/, 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)

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/ (root) defaults to movies

(movies/ and music/ 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, director, year, format, label, studio, genres, rating, barcode, 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 (music if an artist is given, 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

The catalog

  • Search title / genre / cast / studio / intro, sort by title, year, recently-added, value or rating.
  • Filters: format, genre, studio, streaming service, language, category, seen / unseen.
  • Dense, aligned cards showing poster, title·year, ★rating · format · runtime · language, genres, director + cast, studio, where-to-watch, intro hook, and estimated resale value.
  • Clickable everything: a genre, person, or studio filters the grid to matching titles.
  • Adjustable density — viewers pick how many movies per row; responsive on web & mobile.

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 & 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.
  • Resale value — a heuristic estimate plus a live link to eBay sold/completed listings.

Two views

  • Default view — public, read-only.
  • Admin view — password-protected, read/write. Edit a title's name, year, format, studio & distributor; move a title between 🎬 Movies and 🎵 Music; mark seen; 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 from data/.

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 movie-collection tools add items by barcode scan or manual entry and keep your catalog in their cloud or a dated desktop app. MediaHound is the only one that identifies titles from photos of the covers and generates a modern static website you own and host for free — offline-first and open-source. It's also one of the few that handles VHS (which usually has no scannable barcode in the disc databases others rely on).

MediaHound CLZ / Libib Tellico / GCstar Plex / Jellyfin
Add by photo of cover ✅ OCR/AI ❌ 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 (planned)
Where to watch / listen justwatch (movies) · keyless Spotify/Apple/YouTube search (music) Spotify / Apple Music keys (planned)
Resale eBay sold-listings link + estimate Discogs price (planned, 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.


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.

Contributions welcome — see CONTRIBUTING.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mediahound-0.2.0.tar.gz (91.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mediahound-0.2.0-py3-none-any.whl (83.6 kB view details)

Uploaded Python 3

File details

Details for the file mediahound-0.2.0.tar.gz.

File metadata

  • Download URL: mediahound-0.2.0.tar.gz
  • Upload date:
  • Size: 91.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mediahound-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a4ec91828f9651552759070f1c0895592bae8f996f924e3faf30c2848f530e49
MD5 82eb44225bac6684216b3d560cc3f891
BLAKE2b-256 7aeba94fa9602b9f9ee05ec34c664e525948b92fa8fcf85b0b0b89e39b996f4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for mediahound-0.2.0.tar.gz:

Publisher: publish.yml on jchirayath/mediahound

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mediahound-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: mediahound-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 83.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mediahound-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 97e343c90c0b15c37b894782a17bb33c87680ea8df3e83a65741c996c947af55
MD5 41994fdf0bf0c201ccfb102742aff843
BLAKE2b-256 d630c108650ee858d7503e5086d9712961d4ddf452cda40e9cb178d1d73f2a63

See more details on using hashes here.

Provenance

The following attestation bundles were made for mediahound-0.2.0-py3-none-any.whl:

Publisher: publish.yml on jchirayath/mediahound

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page