# 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 via `difflib` (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, converting `Coverart/*.png` to 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.json`** at the repo root is a **seed/reference copy**, not the live database — the actual live data is `/app/data/games.json` on the bind-mounted `data/` 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 `--delete` so a deploy never wipes it (see below). ## Local development ```bash 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_PASSWORD` and `SECRET_KEY` were previously hardcoded in plain text directly in `docker-compose.yml`, which would have landed in git history the moment this repo was created. Moved to a gitignored `.env` file referenced via `env_file: .env` before this repo was ever initialized — neither secret is in git history.