Eric WagonerandClaude Fable 5 fb9f654cd8 add-checked feedback returns: parse the Response before reading counts
Eric's report: no toast, no list update — yet the add landed. apiPost
returns the raw Response (every other caller only truth-checks it);
reading .added off it gave undefined and .skipped.length then THREW,
killing the toast, the panel close, and the refresh in an unhandled
rejection. The handler now parses the body first. Verified live: toast
fires with the count, panel closes, the catalog updates in place
(probe row removed from the data afterward).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
2026-08-09 12:20:32 -04:00

bggpipe — Shelf-to-BoardGameGeek Collection Pipeline

Unique visitors, last 7 days Unique installs, all-time

the bggpipe piper — a bagpiper whose bag is a board game box

Photograph your board game shelves. End up with your whole collection — including which edition of each game you own — cataloged on BoardGameGeek.

photos/ → [1 extract] → titles.json → [2 resolve] → matches.csv
        → [3 review] → matches.csv (approved) → [4 diff] → to_add.csv
        → [5 upload] → upload_log.csv
        → [6 enrich] → games.json

Status: battle-tested end to end. The full pipeline has run against a live BGG account: shelf photos → 136 identified games → 62 additions and 36 version updates on a real collection. The result is public — the author's collection on BGG is what this pipeline built — and this browsable library of the same shelves is what the enrich stage turns that data into. Still sensible on a first run: --dry-run, then --limit 1.

Why this exists

BGG has no bulk import and no write API. Cataloging a few hundred games by hand means hours of searching, clicking, and second-guessing which of five editions you own. I wanted to point a camera at my shelves instead. This pipeline replaces the typing with: take photos, run a command, settle a handful of ambiguous matches in a review step, done — and every judgment call along the way is yours, made in a review UI, never guessed by the machine.

How it works

  1. extract — Shelf photos go to a vision model (Claude by default; any OpenAI-compatible endpoint or a local Ollama model works), which reads game titles off spines and boxes along with edition cues: publisher, edition wording, print year, language.
  2. resolve — Titles are matched to BGG game IDs via the XML API2 (exact + fuzzy matching, popularity tiebreaks), then edition cues are matched against BGG's version list for each game. Anything uncertain is flagged rather than guessed.
  3. review — A local review step for ambiguous matches: pick the right game/version, or leave the version blank. Wrong guesses never reach your collection.
  4. diff — Your existing BGG collection is fetched and compared, per copy (owning one edition of a game doesn't hide a second edition you also own).
  5. upload — A Playwright browser session logs into your BGG account and adds each game (with its version, when known) politely and slowly. Dry-run mode, per-game logging, and resumability included.
  6. enrich — Full metadata for every game (designers, player counts, weight, rank, mechanics, artwork, version details) lands in data/games.json, feeding a browsable library of your shelves — which bggpipe export can publish as static pages on your own site.

Everything runs locally, every stage survives being killed mid-run, and all artifacts are flat CSV/JSON files you can inspect and edit. You can drive it from the terminal or from a local web app:

Pipeline dashboard: six stage cards with live counts and Run buttons, showing a completed pipeline — 136 titles extracted, 115 resolved, 62 games added, 136 in the library

See the full tour — all seven pages, the game-detail view, and the phone experience.

Requirements

  • Python 3.12+ and uv, on macOS, Linux, or Windows. (Development happens on macOS; Windows is untested but nothing is platform-specific. One caveat: the owner-only file permissions bggpipe sets on .env and browser session state are POSIX-only — on Windows, keep those files in a directory protected by your account.)
  • A vision model for extraction — an Anthropic API key by default, or any OpenAI-compatible endpoint including a free local Ollama model. Cost expectation: extracting the author's whole collection — 65 shelf photos, 136 games — cost under a dollar with the default model (Claude Sonnet), one-time. Re-runs are free: every photo's read is cached, and only new or replaced photos go back to the model.
  • A BoardGameGeek account and a registered BGG application — as of BGG's 2025 API policy, the XML API requires a Bearer token from a registered app. Register a free non-commercial application at boardgamegeek.com/applications (approval can take a week or more, so apply on day one), then create a token. Each user needs their own; tokens must not be shared. (You can start before it arrives.)

Why it asks for your BGG password — and where your credentials go

BGG has no write API: the only way to add games to a collection is the website itself. So the upload stage signs into boardgamegeek.com in a real browser window, on your machine, and clicks the same buttons you would — you can literally watch it work (the browser is visible by default). That's the whole reason the password is needed, and it's used for exactly that login and nothing else.

Everything stays on your computer. There is no bggpipe server, no telemetry, no analytics, and no account with me — I never see your credentials, your collection, or anything else, and the code is right here to check. Credentials live in a local .env file (owner-only permissions, never written to logs) and are sent only to boardgamegeek.com itself. The only other service the pipeline ever contacts is the vision provider you configure, which receives your shelf photos and nothing more — and with a local Ollama model, even those never leave the house.

Quick start

uv tool install git+https://git.kestrelsnest.social/eric/bggpipe
mkdir shelves && cd shelves   # any directory of your own — NOT a clone of this repo
bggpipe init                  # guided setup: folders, credentials, browser download
bggpipe web                   # the whole app at http://127.0.0.1:8377/

Drop shelf photos on the Photos page (or straight from your phone's camera) and follow the pipeline left to right. Your photos and every pipeline artifact live in the directory where you run it, and uv tool upgrade bggpipe picks up fixes without going anywhere near your data — which is also why running inside a clone of this repo is the one unsupported setup: this repo carries its author's live pipeline data at git-tracked paths, and a git pull on top of yours could destroy it. bggpipe warns if it catches you doing this.

The user's guide — credentials, every stage and flag, phone pairing, RPG handling, fixing misreads, uploading safely, and running before your token arrives.

Development

git clone https://git.kestrelsnest.social/eric/bggpipe.git
cd bggpipe
uv sync                       # installs Python deps
uv run pytest                 # offline test suite (recorded fixtures, no network)
uv run pytest --run-live      # + a read-only live-API smoke test (needs token)
uv run ruff check src tests   # lint
uv run bggpipe web --dev      # the app with code hot-reload

Run your own pipeline from a different directory — the clone's data/ is the author's live data. Architecture and contributor guidance: CLAUDE.md and the design contract; BGG automation notes: docs/bgg-upload-flow.md.

Questions, bugs, ideas

Email eric@ericwagoner.com, or find me on Mastodon at @eric@toots.kestrelsnest.social or Bluesky at @kestrelsnest.social — bug reports, confusions, and "it worked, here's my collection" notes all welcome. (This Gitea instance doesn't take public registrations, so there's no issue tracker to file into yet; if enough people show up, one will materialize.)

A note on being a good BGG citizen

This tool is not affiliated with or supported by BoardGameGeek. It uses only the sanctioned XML API2 for reads (with your own registered application token, per BGG's current policy) and drives the regular website for writes, deliberately slowly (one request every couple of seconds, slower for uploads). Please keep it that way: BGG is a community resource running on community goodwill. You are responsible for your own account — review the dry-run output before a real upload.

License

MIT — see LICENSE.

BoardGameGeek and BGG are trademarks of BoardGameGeek, LLC. bggpipe is an independent project, not affiliated with or endorsed by BoardGameGeek.

Mascot art by Juniper, used with pride.

S
Description
CLI and local browser pipeline for cataloging board game shelves into a BoardGameGeek collection: shelf photos → Claude vision title extraction → BGG XML API matching → human review → automated bulk upload.
Readme MIT
19 MiB
2026-08-09 20:35:42 +00:00
Languages
Python 82.3%
HTML 12.7%
CSS 4.1%
JavaScript 0.9%