Skip to main content

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

Project description

🎬🎵 MediaHound

CI CodeQL PyPI 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)

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 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.

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 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 photos → Take Photo, and they upload straight into your catalog. Uploads are token-protected (only the phone that scanned the code can add photos) 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: grab MediaHound-macOS.zip / MediaHound-Windows.zip from the Releases page, unzip, and open it. (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]" then mediahound gui opens 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}/, 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.3.0.tar.gz (106.2 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.3.0-py3-none-any.whl (95.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for mediahound-0.3.0.tar.gz
Algorithm Hash digest
SHA256 160f8ae1f811078440de66cb6da41261be355776c9a6abee105da44f645398fb
MD5 cbaea9b1cf72a1c4db7281f10dd2c2d8
BLAKE2b-256 9bcc63c339b36ee4344dea0be71ab5a244c7e98387a5d599d2969a24b4e60145

See more details on using hashes here.

Provenance

The following attestation bundles were made for mediahound-0.3.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.3.0-py3-none-any.whl.

File metadata

  • Download URL: mediahound-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 95.7 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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3d65c37d08ca6271719ffd6622b9932d028fa86498edebe4668ee81af851a73a
MD5 b06fcdc63c3c0e7fbc81e9d2c1958b2e
BLAKE2b-256 0df5563e7fb554942afbd332d635df330a9be84fade46259a54082ddb140b80f

See more details on using hashes here.

Provenance

The following attestation bundles were made for mediahound-0.3.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