81 lines
4 KiB
Markdown
81 lines
4 KiB
Markdown
# 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.
|