Repository that collects all sealed MTG products and maps their contents.
The goal of this project is to catalogue each Magic: The Gathering product and their contents.
Most of the data is loaded from upstream, using taw's repositories, magic-search-engine and magic-preconstructed-decks, often automatically through Github Actions. This data is then validated and rebuilt in a JSON file, and inserted in MTGJSON daily builds, it strictly has to adhere to MTGJSON coding styles and conventions.
Thanks to a series of scripts we're able to
- load new decks from taw automatically
- map the contents of each deck and booster to physical cards
- map products to third-party marketplaces
- keep track of various bonus cards and obscure products
- keep track of what the repository is missing
This is a massive undertaking of course, so any help and contribution to improve coverage is welcome! If you don't know where to start, keep reading and check the data/review.yaml file for suggestions.
- Python 3.x and a working virtual environment
- Install dependencies:
pip install -r requirements.txt
- (optional) Recent version of AllPrintings.json file
data/products/ # YAML files defining sealed products and their contents
scripts/ # Python scripts for compiling YAML → JSON
outputs/ # Generated JSON output (do not edit directly)
.github/ # GitHub Actions workflows
Each set has one file, named after its set code. Every product in it is defined by a list of identifiers, a category (deck, booster, box, etc) and a subtype (collector booster, draft booster, prerelease and so on), and its contents: key describes what users will find should they open the product (i.e. they will find 6 Booster Boxes in a Case).
A product whose contents are unknown has no contents: key. Scripts read and write these files through scripts/sealed_yaml.py. Contents used to live in separate data/contents/SETCODE.yaml files: scripts/convert_layout.py converts between the two layouts, and its fold command merges a data/contents/SETCODE.yaml left over from an older branch.
The daily rebuild, weekly rebuild and manual cache build share a
concurrency queue
and check out the latest main when they start. They publish with normal
fast-forward pushes. They never force-push or lock the branch against human edits.
If a push is rejected because main advanced during generation, the publisher
checks out the updated source and repeats that workflow's generation and output
validation. It pushes the rebuilt result to a unique
codex/generated/<mode>-<run>-<attempt> branch and opens a draft PR. An empty
rebuild needs no PR. Authentication, network, branch-protection and generation
failures fail the run instead of being hidden as successful fallback publication.
Fallback commits record their source revision in a Generated-from trailer. The
generated-content-freshness PR job compares it with current main; rebasing or
merging old generated files does not refresh this revision. Review the draft and
run its checks before marking it ready. If it becomes stale, rerun the originating
publishing workflow against current main, then close the superseded draft.
Repository setup: Actions needs permission to create pull requests (Settings →
Actions → General → Workflow permissions). The publishing workflows request only
contents: write and pull-requests: write. GitHub may require a maintainer to
approve checks on PRs opened with GITHUB_TOKEN. To enforce freshness at merge time,
require the freshness check and up-to-date branches in the branch rules; a passing
check alone cannot prevent main advancing afterward. This workflow change does
not modify repository rules. If PR creation fails, the run fails and the generated
branch remains available for recovery.
If you want to add a new sealed product or complete a product description, edit the set's file in data/products/. You can insert new entries anywhere in the file, as they will be kept sorted by the daily scripts.
For example, to add a completely new product, add an entry to data/products/SETCODE.yaml. Products are defined with a basic category, identifiers, and subtype structure, and their contents: use the content types documented below: card, pack, deck, sealed, variable, other, and copy.
data/products/SETCODE.yaml
My Set Booster Box:
category: BOOSTER_BOX
subtype: DRAFT
identifiers:
tcgplayerProductId: '1234'
contents:
sealed:
- count: 30
name: My Set Set Booster Pack
set: mysetcodeUse the Promo Pack creation and audit skill for seasonal booster pools and sealed products. The research ledger records source lists, printing decisions and coverage results.
Data from marketplaces is loaded every day and stored in data/review.yaml though scripts/load_new_products.py.
This file is then manually processed with scripts/check_product_fuzzy.py which maps a product id to each product defined, using fuzz matching, or create a new product entirely on the fly. This script will provide an interactive shell with a list of possible matches, as well as possible extra options in case the product does not end up being listed.
Example output:
Finding similar products for Chaos Vault - Adventures of the Little Witch - Foil {'cardKingdomId': '313132'}
0 - Secret Lair Drop Adventures of the Little Witch
1 - Secret Lair Drop Adventures of the Little Witch Rainbow Foil
2 - Adventures in the Forgotten Realms Theme Booster White
3 - Adventures in the Forgotten Realms Set Booster Box
4 - Adventures in the Forgotten Realms Theme Booster Set of 6
Select action ('h' for help):
The available actions are:
q- quit the script cleanlys- skip this product and leave it unreviewedi- ignore this product and never prompt it againm- list more possible matchesb- go back to the possible matchesu- undo the previous action and go the previous entryc- create a new product0/1/2/3/4- pick the product entry (0 is default)- use the exact name of the product to add the id
For the example above, the right keypress would be 1.
For simple random cards found in various products it is possible to define a minimal drop rate configuration, but this should be used only for specific product lines (i.e. Secret Lair bonus cards). See below for an example use case.
More generally, booster drop rates, EV, and pull probability are not tracked by this project. Please check out magic-search-engine.
A small amount of cards can be defined through the card tag, but the list shouldn't exceed 4-5 entries.
Full decklists are not tracked by this project. Please check out magic-preconstructed-decks.
A lot of scripts are run every day, through Github Actions, with the goal of pulling and associating as much data as possible automatically to minimize the chances of human errors.
- New decks, including SLD drops, are pulled from upstream and added through
scripts/import_new_decks.py - Card-to-product mapping is generated from
scripts/card_to_product_compiler.pyusing a recent MTGJSON version - New product ids are pulled from
scripts/load_new_products.py
These two fields in product descriptions define the type of sealed product for ease of cataloguing. The main difference between them is that the former define the type (i.e. Pack, Deck etc) within the same set, and the latter defines the higher level series (i.e. Jumpstart, Commander, Secret Lair) across multiple sets.
The most straightforward example is for Booster Packs: the category should intuitively be set to BOOSTER_PACK, and the subtype should be set to DRAFT, PLAY, SET, COLLECTOR depending on the product.
Using UNKNOWN/DEFAULT works too, but it's better to be as accurate as possible when adding new sets.
| Name | Description |
|---|---|
BOOSTER_PACK |
A single sealed pack containing a randomized assortment of cards |
BOOSTER_BOX |
A box containing multiple booster packs |
BOOSTER_CASE |
A sealed case containing multiple booster boxes |
DECK |
A pre-constructed, ready-to-play deck, usually distributed in larger displays (i.e. theme decks) |
DECK_BOX |
A pre-constructed, ready-to-play deck, usually distributed in small displays (i.e. commander decks) |
MULTI_DECK |
A product containing two or more pre-constructed decks |
BOX_SET |
A particular product containing a set amount of cards, sometimes with extra promotional material |
KIT |
A themed package with cards and materials, often for learning or events |
BUNDLE |
A combined product with multiple items (e.g., packs, promos, accessories) |
BUNDLE_CASE |
A sealed case containing multiple bundles |
LIMITED |
A limited-edition or exclusive release product (i.e. prerelease products) |
LIMITED_CASE |
A sealed case containing multiple limited-edition products |
SUBSET |
A product combining other products (i.e. two specific decks) |
| Name | Description |
|---|---|
DEFAULT |
The standard or default product type when no specific category applies |
SET |
A booster or product from the Set series, usually containing a card from The List |
COLLECTOR |
A premium booster or product with enhanced card treatments and rarities |
JUMPSTART |
A product designed for the Jumpstart format, combining two half-decks into one |
PROMOTIONAL |
A product containing promotional or limited-distribution cards |
THEME |
A themed product built around a specific mechanic, color, or concept |
TOURNAMENT |
A product designed for or distributed at organized tournament play |
WELCOME |
An introductory product given to new players to learn the game |
TOPPER |
A special bonus card or mini-pack included on top of a booster box |
PLANESWALKER |
A pre-constructed deck centered around a specific Planeswalker character |
CHALLENGE |
A product from the Challenge a God series |
EVENT |
A product tied to a specific in-store or organized play event |
CHAMPIONSHIP |
A product associated with championship-level tournaments or winners |
INTRO |
An introductory pre-constructed deck for beginner players |
COMMANDER |
A pre-constructed deck designed for the Commander format |
BRAWL |
A pre-constructed deck designed for the Brawl format |
ARCHENEMY |
A product designed for the Archenemy multiplayer format |
PLANECHASE |
A product designed for the Planechase multiplayer format |
STARTER |
A beginner-friendly starter product with everything needed to start playing |
DRAFT_SET |
A set or product specifically curated for draft-format play |
TWO_PLAYER_STARTER |
A starter product designed for two players to learn and play together |
DUEL |
A product from the Duel Decks series |
CLASH |
A product from the Battle Clash series |
BATTLE |
A product centered on battle or combat-themed gameplay |
GAME_NIGHT |
A multiplayer box set designed for casual game night sessions |
FROM_THE_VAULT |
A premium boxed set featuring reprints of iconic cards with special treatments |
SPELLBOOK |
A themed collection of reprinted cards with alternate art in a premium package |
SECRET_LAIR |
A limited-edition drop with uniquely styled cards, sold directly to consumers |
SECRET_LAIR_BUNDLE |
A bundle containing multiple Secret Lair drops |
COMMANDER_COLLECTION |
A product from the Commander Collection series |
COLLECTORS_EDITION |
A premium product aimed at collectors with special finishes or exclusive cards |
GUILD_KIT |
A themed deck built around a specific guild |
DECK_BUILDERS_TOOLKIT |
A starter product with cards, lands, and packs to help build custom decks |
LAND_STATION |
A box containing a large supply of basic land cards for events or deck building |
GIFT_BUNDLE |
A bundle packaged as a gift, typically with extra items like dice or promos |
FAT_PACK |
An older term for a bundle containing booster packs, lands, and a storage box |
MINIMAL |
A commander deck packaged in a basic cardboard box |
PREMIUM |
A higher-tier product featuring foil, alternate art, or enhanced materials |
ADVANCED |
A product designed for experienced or competitive players |
DRAFT |
A booster product used for drafting |
PLAY |
A booster product used for playing or drafting |
SEALED_SET |
A complete sealed set of cards from a specific release |
PRERELEASE |
A special product distributed at prerelease events before official launch |
OTHER |
A catch-all category for products that don't fit other classifications |
CHALLENGER |
A pre-constructed product from the Challenger Decks series |
SIX |
A product designed for the six-card or six-player format |
CONVENTION |
An exclusive product available only at conventions or special events |
REDEMPTION |
A product obtained through Magic Online Redemption Program |
A product's contents go under its contents: key and use the following types: card, pack, deck, sealed, variable, other, and copy.
A card object refers to a single card, usually a promotional card.
card objects have the following properties:
name: [str] Full card name
set: [str] Set code
number: [str or int] Collector number
foil: [bool, optional] True if card is traditional or etched foil
etched: [bool, optional] True if card is etched foil; takes precedence over foil when mapping the finish
token: [bool, optional] True if the collector number refers to a token instead of a card, ie. SLD 918 "Food"
uuid: [str, calculated] The card's MTGJson UUID. This is calculated by the compiler and you should not enter this in the YAML.
Tokens live in {set}.tokens rather than {set}.cards in MTGJson, so the flag
tells the compiler where to resolve the collector number, and is carried into the
output so consumers know to look for the UUID in the same place.
Example card YAML input:
Phyrexia All Will Be One Compleat Bundle:
contents:
card:
- set: one
name: Phyrexian Arena
number: 283
foil: true
...JSON result:
"contents": {
"card": [
{
"foil": true,
"name": "Phyrexian Arena",
"number": "283",
"set": "one",
"uuid": "dab29a67-28a9-5847-a77e-1f0771b55be1"
}
],
"...": []
}A pack object refers to an existing object in MTGJson {set}.booster. When pack is included in the product contents, the card_count parameter is required.
pack objects have the following properties:
set: [str] Set code
code: [str] Internal reference code targeting {set}.booster. 'default' refers to the draft booster for a given set.
Example pack YAML input:
Unlimited Edition Booster Pack:
contents:
pack:
- set: 2ed
code: defaultJSON result:
"contents": {
"pack": [
{
"code": "default",
"set": "2ed"
}
]
}A deck object refers to a precompiled deck object. When deck is included in the product contents, the card_count parameter is required.
deck objects have the following properties:
set: [str] Set code
name: [str] The deck's name matching its MTGJson object.
Example deck YAML input:
7th Edition Theme Deck Armada:
contents:
deck:
- set: 7ed
name: ArmadaJSON result:
"contents": {
"deck": [
{
"name": "Armada",
"set": "7ed"
}
]
}A sealed object refers to another sealed object.
sealed objects have the following properties:
set: [str] Set code
name: [str] The product name
count: [int] Count of included products
uuid: [str, calculated] The MTGJson UUID of the linked product. This is calculated by the compiler and should not be added in the YAML.
Example sealed YAML input:
Unlimited Edition Booster Box:
contents:
sealed:
- count: 36
name: Unlimited Edition Booster Pack
set: 2edJSON result:
"contents": {
"sealed": [
{
"count": 36,
"name": "Unlimited Booster Pack",
"set": "2ed",
"uuid": "3f7ddeba-52f5-5f8f-a013-309403c8a870"
}
]
}A variable object contains a list of possible contents for the product.
variable products are defined in two sections: the main variable key and the variable_mode key.
The variable key defines all the possible sub-components to be chosen from randomly.
The variable_mode key defines how the components will be randomized.
variable objects have the following properties:
configs: [list[object]] A list of the possible configuration objects
variable_mode objects have the following properties:
count: [int] The number of components to pull
replacement: [bool] Whether duplicates are allowed in the randomization
Example variable YAML input:
Amonkhet Booster Battle Pack:
contents:
card_count: 60
sealed:
- count: 2
name: Amonkhet Booster Pack
set: akh
variable:
- deck:
- name: Amonkhet Welcome Deck - White
set: w17
- deck:
- name: Amonkhet Welcome Deck - Blue
set: w17
- deck:
- name: Amonkhet Welcome Deck - Black
set: w17
- deck:
- name: Amonkhet Welcome Deck - Red
set: w17
- deck:
- name: Amonkhet Welcome Deck - Green
set: w17
variable_mode:
count: 2
replacement: falseJSON result:
"contents": {
"sealed": [
{
"count": 2,
"name": "Amonkhet Booster Pack",
"set": "akh",
"uuid": "bdfaa43d-9ebd-502f-856b-11eadc97b026"
}
],
"variable": [
{
"configs": [
{
"deck": [
{
"name": "Amonkhet Welcome Deck - White",
"set": "w17"
},
{
"name": "Amonkhet Welcome Deck - Blue",
"set": "w17"
}
]
},
{
"deck": [
{
"name": "Amonkhet Welcome Deck - White",
"set": "w17"
},
{
"name": "Amonkhet Welcome Deck - Black",
"set": "w17"
}
]
},
{"...": ""}
]
}
]
}An other object refers to things not included in MTGJson.
other objects have the following property:
name: [str] A string describing the object
Example other YAML input:
Unlimited Edition Starter Deck:
contents:
pack:
- set: 2ed
code: starter
other:
- name: Unlimited Edition Starter Deck RulebookJSON result:
"contents": {
"other": [
{
"name": "Unlimited Edition Starter Deck Rulebook"
}
],
"pack": [
{
"code": "starter",
"set": "2ed"
}
]
}A copy entry gives a product the same contents as another product in the same set, for example the minimal-packaging version of a deck.
Example copy YAML input:
Commander 2021 Commander Deck Lorehold Legacies Minimal Packaging:
contents:
copy: Commander 2021 Commander Deck Lorehold Legaciescopy must be the only key under contents:, and it resolves to the named product's own contents in the JSON output. The target must be in the same set file and have contents of its own, and a copy of a copy is not allowed.
By contributing to this repository, you agree that your contributions will be licensed under the MIT License.