4 KiB
N64 Tracker
Flask app for tracking a personal N64 game collection: ownership status, loose cartridge prices (scraped from PriceCharting), rarity percentages (scraped from RarityGuide), and cover art (LaunchBox games DB, cached locally as WebP thumbnails). Runs as one Docker container on the user's Unraid home server.
Architecture
app.py— Flask routes for the game list/stats API, session-based password auth (single shared password, SHA-256 hashed in/app/data/settings.json), and three scrapers:scrape_pricecharting()— loose cartridge prices, paginated via a cursor-based form POST loop.scrape_rarityguide()— rarity % + label, fuzzy-matched to game titles viadifflib(handles minor title spelling differences between sources).scrape_game_info()— cover art + metadata from LaunchBox's games DB, only fetched lazily on first view/ownership toggle of a game, not upfront for the whole collection.- Thumbnails:
generate_thumbnails()runs in a background thread at startup, convertingCoverart/*.pngto 300px-wide WebP in/app/data/thumbs— serving thumbnails instead of full PNGs is what keeps the game grid fast. BASE_PREFIX = '/n64'— the app expects to run under this subpath behind a reverse proxy; all routes are prefixed with it.
static/— frontend (vanilla JS/HTML/CSS), served by Flask directly.games.jsonat the repo root is a seed/reference copy, not the live database — the actual live data is/app/data/games.jsonon the bind-mounteddata/volume (gitignored, server-only).Coverart/— ~1,600 PNG cover art images (~1.6GB). Deliberately excluded from git (.gitignore) — this is static reference data that doesn't change, and 1.6GB in git history would make every clone of this repo download all of it. It lives only on the server; the deploy workflow explicitly excludes it from the rsync--deleteso a deploy never wipes it (see below).
Local development
pip install -r requirements.txt
python3 app.py
Runs on port 5000. Needs Coverart/ populated locally to see cover art
(not in git — copy it from the server if working on cover-art-related
features, or work without it since it degrades gracefully).
Deployment
Target: Unraid server, container path /mnt/user/appdata/n64-tracker,
built from Dockerfile/docker-compose.yml in this repo. Container name
n64-tracker-n64-tracker-1, host port 8585 → container port 5000.
Deploy mechanism: Forgejo push-to-deploy, same pattern as the other
three projects on this box — see card-grader's CLAUDE.md for the full
pipeline mechanics. Push to main on the forgejo remote
(https://git.hippofam.com/ninja_hippo/n64-tracker.git) triggers
.forgejo/workflows/deploy.yml, which syncs into
/mnt/user/appdata/n64-tracker (excluding .git, .env, data/, and
Coverart/ — that last exclusion is load-bearing, since Coverart/
isn't tracked in git at all and a --delete sync without excluding it
would wipe the images on every deploy) and runs docker compose up -d --build.
Persistent state, never touched by deploys: data/ (games.json,
settings.json, thumbnail cache) and Coverart/ are both server-only.
.env holds ADMIN_PASSWORD/SECRET_KEY.
Migration notes (2026-08-25):
- This container originally used a Docker named volume
(
n64-tracker_game-data) instead of a bind mount, which meant Unraid's Appdata Backup plugin (only knows about/mnt/user/appdata) silently never backed up the live game database. Migrated to a bind mount (./data:/app/data) with existing data copied over and verified intact before the named volume was deleted. ADMIN_PASSWORDandSECRET_KEYwere previously hardcoded in plain text directly indocker-compose.yml, which would have landed in git history the moment this repo was created. Moved to a gitignored.envfile referenced viaenv_file: .envbefore this repo was ever initialized — neither secret is in git history.