mirror of
https://github.com/Cockatrice/Magic-Spoiler.git
synced 2026-04-18 13:08:40 -05:00
commit
3f976fe9d5
|
|
@ -30,7 +30,7 @@
|
|||
"Creature"
|
||||
]
|
||||
},
|
||||
"example card details":{
|
||||
"example card details": {
|
||||
"meta": {
|
||||
"values": "All fields (including loyalty) are string or array of strings except CMC which is int",
|
||||
"required fields (all cards)": [
|
||||
|
|
@ -42,8 +42,7 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"blank card (do not use this key, just the object) - remove unneeded keys":
|
||||
{
|
||||
"blank card (do not use this key, just the object) - remove unneeded keys": {
|
||||
"name": "",
|
||||
"manaCost": "",
|
||||
"number": "",
|
||||
|
|
@ -58,49 +57,364 @@
|
|||
"toughness": ""
|
||||
}
|
||||
},
|
||||
"cards": [
|
||||
{
|
||||
"name": "Nicol Bolas, God-Pharaoh",
|
||||
"manaCost": "4UBR",
|
||||
"number": "140",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Planeswalker - Bolas",
|
||||
"url": "https://i.imgur.com/DTNXG2z.png",
|
||||
"text": "+2: Target opponent exiles cards from the top of his or her library until he or she exiles a nonland card. Until end of turn, you may cast that card without paying its mana cost.\n+1: Each opponent exiles two cards from his or her hand.\n-4: Nicol Bolas, God-Pharaoh deals 7 damage to target opponent or creature an opponent controls.\n-12: Exile each nonland permanent your opponents control.",
|
||||
"loyalty": "7",
|
||||
"cmc": 7
|
||||
},
|
||||
{
|
||||
"name": "Bontu's Last Reckoning",
|
||||
"manaCost": "1BB",
|
||||
"number": "60",
|
||||
"rarity": "Rare",
|
||||
"type": "Sorcery",
|
||||
"url": "https://i.imgur.com/HOjh3pE.png",
|
||||
"text": "Destroy all creatures. Lands you control don't untap during your next untap step.",
|
||||
"cmc": 3
|
||||
},
|
||||
{
|
||||
"name": "Samut, the Tested",
|
||||
"manaCost": "2RG",
|
||||
"number": "144",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Planeswalker - Samut",
|
||||
"url": "https://i.imgur.com/4tRYs5z.png",
|
||||
"text": "+1: Up to one target creature gains double strike until end of turn.\n-2: Samut, the Tested deals 2 damage divided as you choose among one or two target creatures and/or players.\n-7: Search your library for up to two creature and/or planeswalker cards, put them onto the battlefield, then shuffle your library.",
|
||||
"loyalty": "4",
|
||||
"cmc": 4
|
||||
},
|
||||
{
|
||||
"name": "Nicol Bolas, the Deceiver",
|
||||
"manaCost": "5UBR",
|
||||
"number": "205",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Planeswalker - Bolas",
|
||||
"url": "http://mythicspoiler.com/hou/cards/nicolbolasthedeceiver.jpg",
|
||||
"text": "+3: Each opponent loses 3 life unless that player sacrifices a nonland permanent or discards a card.\n-3: Destroy a creature. Draw a card.\n-11: Nicol Bolas, the Deciever deals 7 damage to each opponent. You draw seven cards. ",
|
||||
"loyalty": "X",
|
||||
"cmc": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
"HOU": {
|
||||
"cards": [
|
||||
{
|
||||
"name": "Nicol Bolas, God-Pharaoh",
|
||||
"manaCost": "4UBR",
|
||||
"number": "140",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Planeswalker - Bolas",
|
||||
"url": "https://i.imgur.com/DTNXG2z.png",
|
||||
"text": "+2: Target opponent exiles cards from the top of his or her library until he or she exiles a nonland card. Until end of turn, you may cast that card without paying its mana cost.\n+1: Each opponent exiles two cards from his or her hand.\n-4: Nicol Bolas, God-Pharaoh deals 7 damage to target opponent or creature an opponent controls.\n-12: Exile each nonland permanent your opponents control.",
|
||||
"loyalty": "7",
|
||||
"cmc": 7
|
||||
},
|
||||
{
|
||||
"name": "Bontu's Last Reckoning",
|
||||
"manaCost": "1BB",
|
||||
"number": "60",
|
||||
"rarity": "Rare",
|
||||
"type": "Sorcery",
|
||||
"url": "https://i.imgur.com/HOjh3pE.png",
|
||||
"text": "Destroy all creatures. Lands you control don't untap during your next untap step.",
|
||||
"cmc": 3
|
||||
},
|
||||
{
|
||||
"name": "Samut, the Tested",
|
||||
"manaCost": "2RG",
|
||||
"number": "144",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Planeswalker - Samut",
|
||||
"url": "https://i.imgur.com/4tRYs5z.png",
|
||||
"text": "+1: Up to one target creature gains double strike until end of turn.\n-2: Samut, the Tested deals 2 damage divided as you choose among one or two target creatures and/or players.\n-7: Search your library for up to two creature and/or planeswalker cards, put them onto the battlefield, then shuffle your library.",
|
||||
"loyalty": "4",
|
||||
"cmc": 4
|
||||
},
|
||||
{
|
||||
"name": "Nicol Bolas, the Deceiver",
|
||||
"manaCost": "5UBR",
|
||||
"number": "205",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Planeswalker - Bolas",
|
||||
"url": "http://mythicspoiler.com/hou/cards/nicolbolasthedeceiver.jpg",
|
||||
"text": "+3: Each opponent loses 3 life unless that player sacrifices a nonland permanent or discards a card.\n-3: Destroy a creature. Draw a card.\n-11: Nicol Bolas, the Deciever deals 7 damage to each opponent. You draw seven cards. ",
|
||||
"loyalty": "?",
|
||||
"cmc": 8
|
||||
}
|
||||
]
|
||||
},
|
||||
"XLN": {
|
||||
"cards": [
|
||||
{
|
||||
"name": "Haven Raptor",
|
||||
"manaCost": "2GG",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Dinosaur",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/havenraptor.jpg",
|
||||
"text": "Tough - Whenever Haven Raptor is dealt damage, draw a card.",
|
||||
"cmc": 4,
|
||||
"power": "4",
|
||||
"toughness": "5"
|
||||
},
|
||||
{
|
||||
"name": "Tiehana, Voice of Thunder",
|
||||
"manaCost": "",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Legendary Creature - Merfolk",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/tiehanavoiceofthunder.jpg",
|
||||
"text": "Tiehana, Voice of Thunder's power and toughness are equal to the number of cards in your hand.\nYou have no maximum hand size.\nWhen Tiehana enters the battlefield, draw a card for each creature you control.",
|
||||
"cmc": 0,
|
||||
"power": "*",
|
||||
"toughness": "*"
|
||||
},
|
||||
{
|
||||
"name": "Ogre Hand Ravager",
|
||||
"manaCost": "3BB",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Orc Pirate Wizard",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/ogrehandravager.jpg",
|
||||
"text": "When Ogre Hand Ravager enters the battlefield, each player loses a third of his or her life, rounded up.",
|
||||
"cmc": 5,
|
||||
"power": "4",
|
||||
"toughness": "4"
|
||||
},
|
||||
{
|
||||
"name": "Bishop of Rebirth",
|
||||
"manaCost": "3WW",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Vampire Cleric",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/bishopofrebirth.jpg",
|
||||
"text": "Whenever Bishop of Rebirth attacks, you may return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.",
|
||||
"cmc": 5,
|
||||
"power": "3",
|
||||
"toughness": "4"
|
||||
},
|
||||
{
|
||||
"name": "Ashes of the Abhorrent",
|
||||
"manaCost": "1W",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Enchantment",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/ashesoftheabhorrent.jpg",
|
||||
"text": "Players can't cast spells from graveyards or active abilities of cards in graveyards.\nWhenever a creature dies, you gain 1 life.",
|
||||
"cmc": 2
|
||||
},
|
||||
{
|
||||
"name": "Deeproot Champion",
|
||||
"manaCost": "1G",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Merfolk Champion",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/deeprootchampion.jpg",
|
||||
"text": "Whenever you cast a noncreature spell, put a +1/+1 counter on Deeproot Champion.",
|
||||
"cmc": 2,
|
||||
"power": "1",
|
||||
"toughness": "1"
|
||||
},
|
||||
{
|
||||
"name": "Dreamcaller Siren",
|
||||
"manaCost": "3UU",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Siren Pirate",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/dreamcallersiren.jpg",
|
||||
"text": "Flash\nFlying\nDreamcaller Siren can only block creatures with flying.\nWhen Dreamcaller Siren enters the battlefield, if you control another Pirate, tap up to two target nonland permanents.",
|
||||
"cmc": 5,
|
||||
"power": "3",
|
||||
"toughness": "3"
|
||||
},
|
||||
{
|
||||
"name": "Goring Ceratops",
|
||||
"manaCost": "5WW",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Dinosaur",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/goringceratops.jpg",
|
||||
"text": "Double strike\nWhenever Goring Ceratops attacks, other creatures you control gain double strike until end of turn.",
|
||||
"cmc": 7,
|
||||
"power": "?",
|
||||
"toughness": "?"
|
||||
},
|
||||
{
|
||||
"name": "Hostage Taker",
|
||||
"manaCost": "2UB",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Human Pirate",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/hostagetaker.jpg",
|
||||
"text": "When Hostage Taker enters the battlefield, exile target artifact or creature until Hostage Taker leaves the battlefield. You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell.",
|
||||
"cmc": 4,
|
||||
"power": "2",
|
||||
"toughness": "1"
|
||||
},
|
||||
{
|
||||
"name": "Emperor's Vnguard",
|
||||
"manaCost": "5G",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Unknown",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/emperorsvanguard.jpg",
|
||||
"text": "Whenever Emperor's Vanguard ???",
|
||||
"cmc": 6,
|
||||
"power": "4",
|
||||
"toughness": "3"
|
||||
},
|
||||
{
|
||||
"name": "Drowned Catacomb",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Land",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/drownedcatacomb.jpg",
|
||||
"text": "Drowned Catacomb enters the battlefield tapped unless you control an Island or Swamp.\n{T}: Add {U} or {B} to your mana pool.",
|
||||
"cmc": 0
|
||||
},
|
||||
{
|
||||
"name": "Dragonskull Summit",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Land",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/dragonskullsummit.jpg",
|
||||
"text": "Dragonskull Summit enters the battlefield tapped unless you control a Swamp or Mountain.\n{T}: Add {B} or {R} to your mana pool.",
|
||||
"cmc": 0
|
||||
},
|
||||
{
|
||||
"name": "Ruin Raider",
|
||||
"manaCost": "2B",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Orc Pirate",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/ruinraider.jpg",
|
||||
"text": "Raid - At the beginning of your end step, if you attacked with a creature this turn, reveal the top card of your library and put that card into your hand. You lose life equal to that card's converted mana cost.",
|
||||
"cmc": 3,
|
||||
"power": "?",
|
||||
"toughness": "?"
|
||||
},
|
||||
{
|
||||
"name": "Vraska's Contempt",
|
||||
"manaCost": "2BB",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Instant",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/vraskascontempt.jpg",
|
||||
"text": "Exile target creature or planeswalker. You gain 2 life.",
|
||||
"cmc": 4
|
||||
},
|
||||
{
|
||||
"name": "Rowdy Crew",
|
||||
"manaCost": "3RR",
|
||||
"number": "0",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Creature - Goblin Pirate",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/rowdycrew.jpg",
|
||||
"text": "Trample\nWhen Rowdy Crew enters the battlefield, draw three cards, then discard 2 cards at random. If two cards that share a type are discarded this way, put two +1/+1 counters on Rowdy Crew.",
|
||||
"cmc": 5,
|
||||
"power": "3",
|
||||
"toughness": "3"
|
||||
},
|
||||
{
|
||||
"name": "Bloodstained Paladin",
|
||||
"manaCost": "3B",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Vampire Knight",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/bloodstainedpaladin.jpg",
|
||||
"text": "Flash\nBloodstained Paladin enters the battlefield with a +1/+1 counter on it for each creature that died this turn.",
|
||||
"cmc": 4,
|
||||
"power": "3",
|
||||
"toughness": "4"
|
||||
},
|
||||
{
|
||||
"name": "Arcane Adaptation",
|
||||
"manaCost": "2U",
|
||||
"number": "44",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Enchantment",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/arcaneadaptation.jpg",
|
||||
"text": "As Arcane Adaptation enters the battlefield, choose a creature type.\nCreatures you control are the chosen type in addition to their other types. The same is true for creature spells you control and creature cards you own that aren't on the battlefield.",
|
||||
"cmc": 3
|
||||
},
|
||||
{
|
||||
"name": "Captain Lannery Storm",
|
||||
"manaCost": "2R",
|
||||
"number": "0",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Legendary Creature - Human Pirate",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/captainlannerystorm.jpg",
|
||||
"text": "Haste\nWhenever Captain Lannery Storm attacks, create a colorless Treasure artifact token with \"{T}, Sacrifice this artifact: Add one mana of any color to your mana pool.\"\nWhenever you sacrifice a Treasure, Captain Lannery Storm gets +1/+0 until end of turn.",
|
||||
"cmc": 3,
|
||||
"power": "2",
|
||||
"toughness": "2"
|
||||
},
|
||||
{
|
||||
"name": "Daring Saboteur",
|
||||
"manaCost": "1U",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Creature - Human Pirate",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/daringsaboteur.jpg",
|
||||
"text": "{2}{U}: Daring Saboteur can't be blocked this turn.\nWhenever Daring Saboteur deals combat damage to a player, you may draw a card. If you do, discard a card.",
|
||||
"cmc": 2,
|
||||
"power": "?",
|
||||
"toughness": "?"
|
||||
},
|
||||
{
|
||||
"name": "Sorcerous Spyglass",
|
||||
"manaCost": "2",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Artifact",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/sorcerousspyglass.jpg",
|
||||
"text": "As Sorcerous Spyglass enters the battlefield, look at an opponent's hand, then choose any card name.\nActivated abilities of sources with the chosen name can't be activated unless they're mana abilities.",
|
||||
"cmc": 2
|
||||
},
|
||||
{
|
||||
"name": "Sunpetal Grove",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Land",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/sunpetalgrove.jpg",
|
||||
"text": "Sunpetal Grove enters the battlefield tapped unless you control a Forest or a Plains.\n{T}: Add {G} or {W} to your mana pool.",
|
||||
"cmc": 0
|
||||
},
|
||||
{
|
||||
"name": "Vanquisher's Banner",
|
||||
"manaCost": "5",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Artifact",
|
||||
"url": "http://mythicspoiler.com/ixa/cards/vanquishersbanner.jpg",
|
||||
"text": "As Vanquisher's Banner enters the battlefield, choose a creature type.\nCreatures you control of the chosen type get +1/+1.\nWhenever you cast a creature spell of the chosen type, draw a card.",
|
||||
"cmc": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"C17": {
|
||||
"cards": [
|
||||
{
|
||||
"name": "Taigam, Ojutai Master",
|
||||
"manaCost": "2WU",
|
||||
"number": "46",
|
||||
"rarity": "Rare",
|
||||
"type": "Legendary Creature - Human Monk",
|
||||
"url": "http://mythicspoiler.com/c17/cards/taigamojutaimaster.jpg",
|
||||
"text": "Instant, sorcery, and Dragon spells you control can't be countered by spells or abilities.\nWhenever you cast an instant or sorcery spell from your hand, if Taigam, Ojutai Master attacked this turn, that spell gains rebound. (Exile the spell as it resolves. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.)",
|
||||
"cmc": 4,
|
||||
"power": "3",
|
||||
"toughness": "4"
|
||||
},
|
||||
{
|
||||
"name": "Ramos, Dragon Engine",
|
||||
"manaCost": "6",
|
||||
"number": "",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Legendary Artifact Creature - Dragon",
|
||||
"url": "http://mythicspoiler.com/c17/cards/ramosdragonengine.jpg",
|
||||
"text": "Flying\nWhenever you cast a spell, put a +1/+1 counter on Ramos, Dragon Engine for each of that spell's colors.\nRemove five +1/+1 counters from Ramos: Add {W}{W}{U}{U}{B}{B}{R}{R}{G}{G} to your mana pool. Activate this ability only once each turn.",
|
||||
"cmc": 6,
|
||||
"power": "4",
|
||||
"toughness": "4"
|
||||
},
|
||||
{
|
||||
"name": "O-Kagachi, Vengeful Kami",
|
||||
"manaCost": "1WUBRG",
|
||||
"number": "0",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Legendary Creature - Dragon Spirit",
|
||||
"url": "http://mythicspoiler.com/c17/cards/okagachivengefulkami.jpg",
|
||||
"text": "Flying, trample\nWhenever O-Kagachi, Vengeful Kami deals combat damage to a player, if that player attacked you during his or her last turn, exile target nonland permanent that player controls.",
|
||||
"cmc": 6,
|
||||
"power": "6",
|
||||
"toughness": "6"
|
||||
},
|
||||
{
|
||||
"name": "The Ur-Dragon",
|
||||
"manaCost": "4WUBRG",
|
||||
"number": "0",
|
||||
"rarity": "Mythic Rare",
|
||||
"type": "Legendary Creature - Dragon Avatar",
|
||||
"url": "http://mythicspoiler.com/c17/cards/theurdragon.jpg",
|
||||
"text": "Eminence - As long as The Ur-Dragon is the command zone or on the battlefield, other Dragon spells you cast cost {1} less to cast.\nFlying\nWhenever one or more Dragons you control attack, draw that many cards, then you may put a permanent card from your hand onto the battlefield.",
|
||||
"cmc": 9,
|
||||
"power": "10",
|
||||
"toughness": "10"
|
||||
},
|
||||
{
|
||||
"name": "Wasitora, Nekoru Queen",
|
||||
"manaCost": "2BRG",
|
||||
"number": "0",
|
||||
"rarity": "Rare",
|
||||
"type": "Legendary Creature - Cat Dragon",
|
||||
"url": "http://mythicspoiler.com/c17/cards/wasitoranekoruqueen.jpg",
|
||||
"text": "Flying, trample\nWhenever Wasitora, Nekoru Queen deals combat damage to a player, that player sacrifices a creature. If the player can't, you create a 3/3 black, red, and green Cat Dragon creature token with flying.",
|
||||
"cmc": 5,
|
||||
"power": "5",
|
||||
"toughness": "4"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
92
main.py
92
main.py
|
|
@ -17,8 +17,8 @@ with open('set_info.json') as data_file:
|
|||
setinfos = json.load(data_file)
|
||||
|
||||
with open('cards_manual.json') as data_file:
|
||||
manual_cards = json.load(data_file)
|
||||
manual_cards = manual_cards['cards']
|
||||
manual_sets = json.load(data_file)
|
||||
#manual_cards = manual_cards['cards']
|
||||
|
||||
with open('cards_corrections.json') as data_file:
|
||||
card_corrections = json.load(data_file)
|
||||
|
|
@ -30,27 +30,29 @@ errorlog = []
|
|||
|
||||
#TODO insert configparser to add config.ini file
|
||||
|
||||
for argument in sys.argv:
|
||||
# comment this out for now because multiple sets
|
||||
#for argument in sys.argv:
|
||||
#we can modify any of the variables from the set infos file or the presets above at runtime
|
||||
#works only for first-level variables currently (editing masterpieces
|
||||
#syntax is variable="new value"
|
||||
for setinfo in setinfos:
|
||||
if setinfo in argument.split("=")[0]:
|
||||
setinfos[setinfo] = argument.split("=")[1]
|
||||
for preset in presets:
|
||||
if preset in argument.split("=")[0]:
|
||||
presets[preset] = argument.split("=")[1]
|
||||
#for setinfo in setinfos:
|
||||
#for setinfo in setinfo:
|
||||
# if setinfo in argument.split("=")[0]:
|
||||
# setinfos[setinfo] = argument.split("=")[1]
|
||||
#for preset in presets:
|
||||
# if preset in argument.split("=")[0]:
|
||||
# presets[preset] = argument.split("=")[1]
|
||||
|
||||
def save_allsets(AllSets):
|
||||
#TODO Create AllSets.json for Oracle
|
||||
print "Saving AllSets"
|
||||
|
||||
def save_masterpieces(masterpieces):
|
||||
with open('out/' + setinfos['masterpieces']['setname'] + '.json', 'w') as outfile:
|
||||
def save_masterpieces(masterpieces, setinfo):
|
||||
with open('out/' + setinfo['masterpieces']['setname'] + '.json', 'w') as outfile:
|
||||
json.dump(masterpieces, outfile, sort_keys=True, indent=2, separators=(',', ': '))
|
||||
|
||||
def save_setjson(mtgs):
|
||||
with open('out/' + setinfos['setname'] + '.json', 'w') as outfile:
|
||||
def save_setjson(mtgs, filename):
|
||||
with open('out/' + filename + '.json', 'w') as outfile:
|
||||
json.dump(mtgs, outfile, sort_keys=True, indent=2, separators=(',', ': '))
|
||||
|
||||
def save_errorlog(errorlog):
|
||||
|
|
@ -66,36 +68,48 @@ def save_errorlog(errorlog):
|
|||
json.dump(errorlog, outfile, sort_keys=True, indent=2, separators=(',', ': '))
|
||||
|
||||
def save_xml(xmlstring, outfile):
|
||||
with open(outfile,'w+') as xmlfile:
|
||||
if os.path.exists(outfile):
|
||||
append_or_write = 'w'
|
||||
else:
|
||||
append_or_write = 'w'
|
||||
with open(outfile,append_or_write) as xmlfile:
|
||||
xmlfile.write(xmlstring.encode('utf-8'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
AllSets = spoilers.get_allsets() #get AllSets from mtgjson
|
||||
if presets['oldRSS']:
|
||||
mtgs = {"cards":[]}
|
||||
else:
|
||||
mtgs = spoilers.scrape_mtgs('http://www.mtgsalvation.com/spoilers.rss') #scrape mtgs rss feed
|
||||
mtgs = spoilers.parse_mtgs(mtgs) #parse spoilers into mtgjson format
|
||||
mtgs = spoilers.correct_cards(mtgs, manual_cards, card_corrections, delete_cards) #fix using the fixfiles
|
||||
scryfall = spoilers.get_scryfall('https://api.scryfall.com/cards/search?q=++e:' + setinfos['setname'].lower())
|
||||
mtgs = spoilers.get_image_urls(mtgs, presets['isfullspoil'], setinfos['setname'], setinfos['setlongname'], setinfos['setsize']) #get images
|
||||
mtgjson = spoilers.smash_mtgs_scryfall(mtgs, scryfall)
|
||||
[mtgjson, errors] = spoilers.errorcheck(mtgjson) #check for errors where possible
|
||||
errorlog += errors
|
||||
spoilers.write_xml(mtgjson, setinfos['setname'], setinfos['setlongname'], setinfos['setreleasedate'])
|
||||
save_xml(spoilers.pretty_xml(setinfos['setname']), 'out/spoiler.xml')
|
||||
mtgs = spoilers.add_headers(mtgjson, setinfos)
|
||||
AllSets = spoilers.make_allsets(AllSets, mtgjson, setinfos['setname'])
|
||||
if 'masterpieces' in setinfos: #repeat all of the above for masterpieces
|
||||
#masterpieces aren't in the rss feed, so for the new cards, we'll go to their individual pages on mtgs
|
||||
#old cards will get their infos copied from mtgjson (including fields that may not apply like 'artist')
|
||||
#the images will still come from mtgs
|
||||
masterpieces = spoilers.make_masterpieces(setinfos['masterpieces'], AllSets, mtgjson)
|
||||
[masterpieces, errors] = spoilers.errorcheck(masterpieces)
|
||||
combinedjson = {}
|
||||
for setinfo in setinfos:
|
||||
if presets['oldRSS'] or 'noRSS' in setinfo and setinfo['noRSS']:
|
||||
mtgs = { "cards":[] }
|
||||
else:
|
||||
mtgs = spoilers.scrape_mtgs('http://www.mtgsalvation.com/spoilers.rss') #scrape mtgs rss feed
|
||||
mtgs = spoilers.parse_mtgs(mtgs) #parse spoilers into mtgjson format
|
||||
mtgs = spoilers.correct_cards(mtgs, manual_sets[setinfo['setname']]['cards'], card_corrections, delete_cards) #fix using the fixfiles
|
||||
scryfall = spoilers.get_scryfall('https://api.scryfall.com/cards/search?q=++e:' + setinfo['setname'].lower())
|
||||
mtgs = spoilers.get_image_urls(mtgs, presets['isfullspoil'], setinfo['setname'], setinfo['setlongname'], setinfo['setsize']) #get images
|
||||
mtgjson = spoilers.smash_mtgs_scryfall(mtgs, scryfall)
|
||||
[mtgjson, errors] = spoilers.errorcheck(mtgjson) #check for errors where possible
|
||||
errorlog += errors
|
||||
spoilers.write_xml(masterpieces, setinfos['masterpieces']['setname'], setinfos['masterpieces']['setlongname'], setinfos['masterpieces']['setreleasedate'])
|
||||
AllSets = spoilers.make_allsets(AllSets, masterpieces, setinfos['masterpieces']['setname'])
|
||||
save_masterpieces(masterpieces)
|
||||
spoilers.write_xml(mtgjson, setinfo['setname'], setinfo['setlongname'], setinfo['setreleasedate'])
|
||||
#save_xml(spoilers.pretty_xml(setinfo['setname']), 'out/spoiler.xml')
|
||||
mtgjson = spoilers.add_headers(mtgjson, setinfo)
|
||||
AllSets = spoilers.make_allsets(AllSets, mtgjson, setinfo['setname'])
|
||||
if 'masterpieces' in setinfo: #repeat all of the above for masterpieces
|
||||
#masterpieces aren't in the rss feed, so for the new cards, we'll go to their individual pages on mtgs
|
||||
#old cards will get their infos copied from mtgjson (including fields that may not apply like 'artist')
|
||||
#the images will still come from mtgs
|
||||
masterpieces = spoilers.make_masterpieces(setinfo['masterpieces'], AllSets, mtgjson)
|
||||
[masterpieces, errors] = spoilers.errorcheck(masterpieces)
|
||||
errorlog += errors
|
||||
spoilers.write_xml(masterpieces, setinfo['masterpieces']['setname'], setinfo['masterpieces']['setlongname'], setinfo['masterpieces']['setreleasedate'])
|
||||
AllSets = spoilers.make_allsets(AllSets, masterpieces, setinfo['masterpieces']['setname'])
|
||||
save_masterpieces(masterpieces, setinfo)
|
||||
combinedjson[setinfo['masterpieces']['setname']] = masterpieces
|
||||
save_setjson(mtgjson, setinfo['setname'])
|
||||
combinedjson[setinfo['setname']] = mtgjson
|
||||
save_setjson(combinedjson, 'spoiler')
|
||||
spoilers.write_combined_xml(combinedjson, setinfos)
|
||||
save_xml(spoilers.pretty_xml('out/spoiler.xml'), 'out/spoiler.xml')
|
||||
save_errorlog(errorlog)
|
||||
save_allsets(AllSets)
|
||||
save_setjson(mtgjson)
|
||||
#save_setjson(mtgjson)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
[{
|
||||
"setname": "HOU",
|
||||
"setlongname": "Hour of Devastation",
|
||||
"blockname": "Amonkhet",
|
||||
|
|
@ -15,4 +15,22 @@
|
|||
"mtgsurl": "http://www.mtgsalvation.com/spoilers/181-amonkhet-invocations",
|
||||
"mtgscardpath": "http://www.mtgsalvation.com/cards/amonkhet-invocations/"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"setname": "XLN",
|
||||
"setlongname": "Ixalan",
|
||||
"blockname": "Ixalan",
|
||||
"setsize": 279,
|
||||
"setreleasedate": "2017-09-29",
|
||||
"settype": "expansion",
|
||||
"noRSS": true
|
||||
},
|
||||
{
|
||||
"setname": "C17",
|
||||
"setlongname": "Commander 2017",
|
||||
"setsize": 309,
|
||||
"setreleasedate": "2017-09-29",
|
||||
"settype": "commander",
|
||||
"noRSS": true,
|
||||
"noBooster": true
|
||||
}]
|
||||
181
spoilers.py
181
spoilers.py
|
|
@ -191,10 +191,14 @@ def parse_mtgs(mtgs, manual_cards=[], card_corrections=[], delete_cards=[], spli
|
|||
cardtypes = card['type'].replace('Legendary ','').split(' - ')[0].split(' ')[:-1]
|
||||
if '-' in card['type']:
|
||||
subtype = card['type'].split(' - ')[1].strip()
|
||||
else:
|
||||
subtype = False
|
||||
#if u"—" in card['type']:
|
||||
# subtype = card['type'].split(' — ')[1].strip()
|
||||
if subtype:
|
||||
subtypes = subtype.split(' ')
|
||||
else:
|
||||
subtypes = False
|
||||
if card['cmc'] == '':
|
||||
card['cmc'] = 0
|
||||
cardjson = {}
|
||||
|
|
@ -291,7 +295,7 @@ def correct_cards(mtgjson, manual_cards=[], card_corrections=[], delete_cards=[]
|
|||
card['colorIdentity'] += CID
|
||||
else:
|
||||
card['colorIdentity'] = [CID]
|
||||
|
||||
print mtgjson
|
||||
for card in mtgjson['cards']:
|
||||
isManual = False
|
||||
for manualCard in manual_cards:
|
||||
|
|
@ -447,7 +451,8 @@ def get_scryfall(setUrl):
|
|||
setDone = True
|
||||
print setUrl
|
||||
print setcards
|
||||
print 'No data - ' + set
|
||||
print 'No Scryfall data'
|
||||
scryfall = ['']
|
||||
#noset.append(set)
|
||||
time.sleep(.1)
|
||||
if setcards.has_key('has_more'):
|
||||
|
|
@ -458,10 +463,11 @@ def get_scryfall(setUrl):
|
|||
setDone = True
|
||||
else:
|
||||
setDone = True
|
||||
|
||||
scryfall = convert_scryfall(scryfall[0])
|
||||
return {'cards': scryfall}
|
||||
print
|
||||
if not scryfall[0] == '':
|
||||
scryfall = convert_scryfall(scryfall[0])
|
||||
return {'cards': scryfall}
|
||||
else:
|
||||
return {'cards': []}
|
||||
|
||||
def convert_scryfall(scryfall):
|
||||
cards2 = []
|
||||
|
|
@ -881,8 +887,146 @@ def write_xml(mtgjson, setname, setlongname, setreleasedate, split_cards=[]):
|
|||
print 'Newest: ' + str(newest)
|
||||
print 'Runtime: ' + str(datetime.datetime.today().strftime('%H:%M')) + ' on ' + str(datetime.date.today())
|
||||
|
||||
def pretty_xml(setcode):
|
||||
prettyxml = xml.dom.minidom.parse('out/' + setcode + '.xml') # or xml.dom.minidom.parseString(xml_string)
|
||||
def write_combined_xml(mtgjson, setinfos):
|
||||
if not os.path.isdir('out/'):
|
||||
os.makedirs('out/')
|
||||
cardsxml = open('out/spoiler.xml', 'w+')
|
||||
cardsxml.truncate()
|
||||
cardsxml.write("<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<cockatrice_carddatabase version='3'>\n"
|
||||
"<sets>\n")
|
||||
for setcode in mtgjson:
|
||||
setobj = mtgjson[setcode]
|
||||
if 'cards' in setobj and len(setobj['cards']) > 0:
|
||||
cardsxml.write("<set>\n<name>"
|
||||
+ setcode +
|
||||
"</name>\n"
|
||||
"<longname>"
|
||||
+ setobj['name'] +
|
||||
"</longname>\n"
|
||||
"<settype>"
|
||||
+ setobj['type'].title() +
|
||||
"</settype>\n"
|
||||
"<releasedate>"
|
||||
+ setobj['releaseDate'] +
|
||||
"</releasedate>\n"
|
||||
"</set>\n")
|
||||
cardsxml.write(
|
||||
"</sets>\n"
|
||||
"<cards>\n")
|
||||
count = 0
|
||||
dfccount = 0
|
||||
newest = ''
|
||||
related = 0
|
||||
for setcode in mtgjson:
|
||||
setobj = mtgjson[setcode]
|
||||
for card in setobj["cards"]:
|
||||
if 'layout' in card and card['layout'] == 'split':
|
||||
if 'b' in card["number"]:
|
||||
continue
|
||||
if count == 0:
|
||||
newest = card["name"]
|
||||
count += 1
|
||||
name = card["name"]
|
||||
if card.has_key("manaCost"):
|
||||
manacost = card["manaCost"].replace('{', '').replace('}', '')
|
||||
else:
|
||||
manacost = ""
|
||||
if card.has_key("power") or card.has_key("toughness"):
|
||||
if card["power"]:
|
||||
pt = str(card["power"]) + "/" + str(card["toughness"])
|
||||
else:
|
||||
pt = 0
|
||||
else:
|
||||
pt = 0
|
||||
if card.has_key("text"):
|
||||
text = card["text"]
|
||||
else:
|
||||
text = ""
|
||||
cardcmc = str(card['cmc'])
|
||||
cardtype = card["type"]
|
||||
if card.has_key("names"):
|
||||
if "layout" in card:
|
||||
if card["layout"] != 'split':
|
||||
if len(card["names"]) > 1:
|
||||
if card["names"][0] == card["name"]:
|
||||
related = card["names"][1]
|
||||
text += '\n\n(Related: ' + card["names"][1] + ')'
|
||||
dfccount += 1
|
||||
elif card['names'][1] == card['name']:
|
||||
related = card["names"][0]
|
||||
text += '\n\n(Related: ' + card["names"][0] + ')'
|
||||
else:
|
||||
for cardb in setobj['cards']:
|
||||
if cardb['name'] == card["names"][1]:
|
||||
cardtype += " // " + cardb['type']
|
||||
manacost += " // " + (cardb["manaCost"]).replace('{', '').replace('}', '')
|
||||
cardcmc += " // " + str(cardb["cmc"])
|
||||
text += "\n---\n" + cardb["text"]
|
||||
name += " // " + cardb['name']
|
||||
else:
|
||||
print card["name"] + " has multiple names and no 'layout' key"
|
||||
|
||||
|
||||
tablerow = "1"
|
||||
if "Land" in cardtype:
|
||||
tablerow = "0"
|
||||
elif "Sorcery" in cardtype:
|
||||
tablerow = "3"
|
||||
elif "Instant" in cardtype:
|
||||
tablerow = "3"
|
||||
elif "Creature" in cardtype:
|
||||
tablerow = "2"
|
||||
|
||||
if 'number' in card:
|
||||
if 'b' in card['number']:
|
||||
if 'layout' in card:
|
||||
if card['layout'] == 'split':
|
||||
#print "We're skipping " + card['name'] + " because it's the right side of a split card"
|
||||
continue
|
||||
|
||||
cardsxml.write("<card>\n")
|
||||
cardsxml.write("<name>" + name.encode('utf-8') + "</name>\n")
|
||||
cardsxml.write('<set rarity="' + card['rarity'] + '" picURL="' + card["url"] + '">' + setcode + '</set>\n')
|
||||
cardsxml.write("<manacost>" + manacost.encode('utf-8') + "</manacost>\n")
|
||||
cardsxml.write("<cmc>" + cardcmc + "</cmc>\n")
|
||||
if card.has_key('colors'):
|
||||
colorTranslate = {
|
||||
"White": "W",
|
||||
"Blue": "U",
|
||||
"Black": "B",
|
||||
"Red": "R",
|
||||
"Green": "G"
|
||||
}
|
||||
for color in card['colors']:
|
||||
cardsxml.write('<color>' + colorTranslate[color] + '</color>\n')
|
||||
if name + ' enters the battlefield tapped' in text:
|
||||
cardsxml.write("<cipt>1</cipt>\n")
|
||||
cardsxml.write("<type>" + cardtype.encode('utf-8') + "</type>\n")
|
||||
if pt:
|
||||
cardsxml.write("<pt>" + pt + "</pt>\n")
|
||||
if card.has_key('loyalty'):
|
||||
cardsxml.write("<loyalty>" + str(card['loyalty']) + "</loyalty>\n")
|
||||
cardsxml.write("<tablerow>" + tablerow + "</tablerow>\n")
|
||||
cardsxml.write("<text>" + text.encode('utf-8') + "</text>\n")
|
||||
if related:
|
||||
# for relatedname in related:
|
||||
cardsxml.write("<related>" + related.encode('utf-8') + "</related>\n")
|
||||
related = ''
|
||||
|
||||
cardsxml.write("</card>\n")
|
||||
|
||||
cardsxml.write("</cards>\n</cockatrice_carddatabase>")
|
||||
|
||||
print 'XML COMBINED STATS'
|
||||
print 'Total cards: ' + str(count)
|
||||
if dfccount > 0:
|
||||
print 'DFC: ' + str(dfccount)
|
||||
print 'Newest: ' + str(newest)
|
||||
print 'Runtime: ' + str(datetime.datetime.today().strftime('%H:%M')) + ' on ' + str(datetime.date.today())
|
||||
|
||||
def pretty_xml(infile):
|
||||
prettyxml = xml.dom.minidom.parse(infile) # or xml.dom.minidom.parseString(xml_string)
|
||||
pretty_xml_as_string = prettyxml.toprettyxml(newl='')
|
||||
return pretty_xml_as_string
|
||||
|
||||
|
|
@ -942,7 +1086,7 @@ def make_masterpieces(headers, AllSets, spoil):
|
|||
mpsjson = {
|
||||
"name": headers['setlongname'],
|
||||
"alternativeNames": headers['alternativeNames'],
|
||||
"code": "MPS_AKH",
|
||||
"code": headers['setname'],
|
||||
"releaseDate": headers['setreleasedate'],
|
||||
"border": "black",
|
||||
"type": "masterpiece",
|
||||
|
|
@ -950,6 +1094,14 @@ def make_masterpieces(headers, AllSets, spoil):
|
|||
}
|
||||
return mpsjson
|
||||
|
||||
def set_has_cards(setinfo, manual_cards, mtgjson):
|
||||
if setinfo['setname'] in manual_cards or setinfo['setname'] in mtgjson:
|
||||
return True
|
||||
for card in manual_cards['cards']:
|
||||
if set in card:
|
||||
if set == setinfo['setname']:
|
||||
return True
|
||||
|
||||
def get_allsets():
|
||||
class MyOpener(urllib.FancyURLopener):
|
||||
version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko / 20071127 Firefox / 2.0.0.11'
|
||||
|
|
@ -962,14 +1114,15 @@ def get_allsets():
|
|||
|
||||
def add_headers(mtgjson, setinfos):
|
||||
mtgjson2 = {
|
||||
"block": setinfos['blockname'],
|
||||
"border": "black",
|
||||
"code": setinfos['setname'],
|
||||
"magicCardsInfoCode": setinfos['setname'].lower(),
|
||||
"name": setinfos['setlongname'],
|
||||
"releaseDate": setinfos['setreleasedate'],
|
||||
"type": setinfos['settype'],
|
||||
"booster": [
|
||||
"cards": mtgjson['cards']
|
||||
}
|
||||
if not 'noBooster' in setinfos:
|
||||
mtgjson2['booster'] = [
|
||||
[
|
||||
"rare",
|
||||
"mythic rare"
|
||||
|
|
@ -990,6 +1143,6 @@ def add_headers(mtgjson, setinfos):
|
|||
"land",
|
||||
"marketing"
|
||||
],
|
||||
"cards": mtgjson['cards']
|
||||
}
|
||||
if 'blockname' in setinfos:
|
||||
mtgjson2['block'] = setinfos['blockname']
|
||||
return mtgjson2
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user