Appearance
Magic the Gathering ingest
How Magic was wired into the scan pipeline on 2026-05-07.
TL;DR
- Data source: Scryfall
default_cardsbulk (every printing, ~85 k physical cards after filtering MTGO/Arena digital-only rows). - No migration needed — the multi-TCG schema (
cards.tcg,cards.base_id,cards.variant) added for One Piece accommodates MTG unchanged. - MTG does not go through
tcg-vision(Tormknd's FAISS index doesn't cover Magic) — MTG scans resolve via/scan-lab(our pgvector + DINOv2 pipeline). - Observed throughput: ~213 cards/sec on ingest (Scryfall CDN at concurrency 12), ~9 cards/sec on embedding (DINOv2 small CPU).
How to run the ingest
bash
# Full catalog (Scryfall default-cards, ~85 k cards, ~13 GB images)
docker compose --env-file .env -f docker/compose.yml run --rm \
-e INGEST_TCG=mtg ingest
# Single set (smoke test — Limited Edition Alpha = 295 cards / ~80 MB)
docker compose --env-file .env -f docker/compose.yml run --rm \
-e INGEST_TCG=mtg -e INGEST_SETS=lea ingest
# Smaller alternative — `unique_artwork` (one row per illustration, ~57 k)
docker compose --env-file .env -f docker/compose.yml run --rm \
-e INGEST_TCG=mtg -e INGEST_MTG_BULK=unique_artwork ingestThen populate the embeddings:
bash
docker compose --env-file .env -f docker/compose.yml run --rm \
--entrypoint "node dist/embedCatalogLab.js" \
-e INGEST_LAB_TCG=mtg ingestMTG variants (vs One Piece)
Unlike OPTCG (which returns multiple entries sharing the same card_set_id for parallel arts), Scryfall already de-duplicates printings via distinct collector_number values within a set. So MTG doesn't need assignVariantIds — every Scryfall row is already a distinct printing with its own PK id.
The only case where one physical card spawns multiple rows: double-faced cards (transform / mdfc). The front gets the base id; the back gets ${baseId}_back with variant='back'. Both faces are indexed independently in card_embeddings_lab because they're independently scannable.
Why not tcg-vision?
tcg-vision (Tormknd's sidecar) ships a pre-computed FAISS index covering Pokémon (~26 k) + One Piece (~4 k) + a sliver of Lorcana. Magic isn't in it. Two options:
- Ask Tormknd to add Magic to his
tgc-appand rebuild his FAISS — no code on our side but we depend on his cadence. - Use our pgvector pipeline — ingest from Scryfall, embed with our
embeddings:sidecar, query via/scan-lab. Zero external dependency but it's the first real prod use of the pgvector path.
We took option 2 because (a) we wanted to validate our pipeline anyway to eventually retire the Tormknd dependency, and (b) Magic is the ideal opportunity — a full TCG that doesn't exist anywhere else on our backend.
See also
tcg-collector-backend/docs/INGEST.md§Magic the Gathering — full technical doc (schema, field mapping, quirks)- Card scan architecture — backend pipeline overview
- Plan — /scan-lab pgvector validation — Phase A → D history