はじめに
MTG Arenaでエクスポートしたデッキリスト(以下、デッキリスト)を画像出力したい。
リミテッド向けとして、以下の特徴を持つとする。
- 日本語版のカード画像を表示する。
- クリーチャー呪文/非クリーチャー呪文/土地で分ける。
- マナ総量で列を分ける。
- カード名だけでなく、カードのイラストが上1/3くらい見えるようにする。
- デッキに複数枚あるカードは複数枚表示する(基本土地を除く)。
上記を全て満たすデッキ画像生成器を寡聞にして知らないので自作する。
カード画像はMTG DevelopersでGathererの画像のURLを取得してダウンロードする。
カード・タイプやマナ総量の確認にはpython-mtgaを使う。必然的に言語はPythonを使う。
MTG DevelopersのAPIエンドポイントへのアクセスはPython SDK(mtg-sdk-python)を使う。
ここで躓くよmtg-sdk-python
デッキリストには以下の情報が含まれる。
- カード名
- セットの略号
- コレクター番号
いずれもmtg-sdk-pythonで検索キーとして使用できる。
カード名を英語以外の言語(以下、他言語)で指定して検索する場合にはlanguage
の指定が必要なので注意(参考)。
また、戻り値はリストなので、カード情報はfor
文で回して参照する。
from mtgsdk import Card
cards = Card.where(language='Japanese').where(name='漆月魁渡').where(set='NEO').where(number=226).all()
for card in cards:
print(card.name, card.set, card.number, card.image_url)
Kaito Shizuki NEO 226 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=548538&type=card
他言語版のimage_url
はcard.foreign_names
から取得する
card.foreign_names
は辞書foreign_name
(仮名)を要素とするリストである。
MTG DevelopersのDocsには「各オブジェクトはキーlanguage
、name
、multiverseid
を持つ」的なことが書かれているが、実際はもっと多い。例えば《漆月魁渡》のforeign_name
は以下となっている。
{
'name': '漆月魁渡',
'text': 'あなたの終了ステップの開始時に、このターンに漆月魁渡が戦場に出た場合、これはフェイズ・アウトする。\n+1:カード1枚を引く。その後、このターンにあなたが攻撃していないかぎりカード1枚を捨てる。\n−2:「このクリーチャーはブロックされない。」を持つ青の1/1の忍者・クリーチャー・トークン 1体を生成する。\n−7:あなたは「あなたがコントロールしているクリーチャー1体がプレイヤー1人に戦闘ダメージを与えるたび、あなたのライブラリーから青や黒であるクリーチャー・カード1枚を探し、戦場に出す。その後、ライブラリーを切り直す。」を持つ紋章を得る。',
'type': '伝説のプレインズウォーカー — 魁渡',
'flavor': None,
'imageUrl': 'http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=550063&type=card',
'language': 'Japanese',
'multiverseid': 550063
}
つまり、foreign_name['language'] == 'Japanese'
であるforeign_name
のforeign_name['imageUrl']
を取得すればよい。
ついでに、カード名で検索する意味は無いので、セットの略号とコレクター番号だけで検索することにする。
from mtgsdk import Card
cards = Card.where(set='NEO').where(number=226).all()
for card in cards:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
print(foreign_name['name'], card.set, card.number, foreign_name['imageUrl'])
break
漆月魁渡 NEO 226 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=550063&type=card
from mtgsdk import Card
cards = Card.where(set='ELD').where(number=329).all() # フェイに呪われた王、コルヴォルド
for card in cards:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
print(foreign_name['name'], card.set, card.number, foreign_name['imageUrl'])
break
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: 'NoneType' object is not iterable
は?
card.foreign_names is None
であるカードが存在する
card.foreign_names is None
である可能性は、MTG Developersのドキュメントでも言及されている。
《フェイに呪われた王、コルヴォルド》はエルドレインの王権のBrawl Deck限定収録カードで、英語版しか印刷されていない。
そのため、Gathererにも英語版のカード画像だけしか無い。
MTG Arenaでは日本語版カード画像があるように見えるが、あれは多分レイアウトにアートとテキストを重ねて生成したもので、印刷用の日本語版カード画像というのはどこにも無い。
仕方が無いので、その場合は英語版のカード画像を取得するようにする。
from mtgsdk import Card
cards = Card.where(set='ELD').where(number=329).all()
for card in cards:
if card.foreign_names:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
print(foreign_name['name'], card.set, card.number, foreign_name['imageUrl'])
break
else:
print(card.name, card.set, card.number, card.image_url)
Korvold, Fae-Cursed King ELD 329 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=476047&type=card
まあ、Brawl Deck限定収録カードなんていうニッチなカードで試したのが良くなかったのだろう。
本流のセットのカードなら、そんな問題は起きまい。
from mtgsdk import Card
cards = Card.where(set='NEO').where(number=4).all() # 蛾との親睦
for card in cards:
if card.foreign_names:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
print(foreign_name['name'], card.set, card.number, foreign_name['imageUrl'])
break
else:
print(card.name, card.set, card.number, card.image_url)
Befriending the Moths // Imperial Moth NEO 4 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=548294&type=card
Befriending the Moths // Imperial Moth NEO 4 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=548295&type=card
日本語版カードが印刷されているにもかかわらず、card.foreign_names is None
であるカードが存在する
日本語版カード画像は存在するのだが、card.foreign_names is None
であるためmultiverseid
が特定できず、日本語版カード画像のURLを特定できない。
MTG Developers側の問題なので手の打ちようが無い。
2022/4/1時点で、カルドハイム以降の両面カードがcard.foreign_names is None
であるようだ。
セットの略号とコレクター番号だけではカード画像が一意に定まらない
ご丁寧にcard.name
も同じで、card.names
に至ってはNone
である。なんでや。
card.multiverseid
で区別することは可能だが、どちらが第一面かは分からない。
先にヒットした方が第一面であることをお祈りしましょう。
from mtgsdk import Card
cards = Card.where(set='NEO').where(number=4).all() # 蛾との親睦
name = None
image_url = None
for card in cards:
if card.foreign_names:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
name = foreign_name['name']
image_url = foreign_name['imageUrl']
break
else:
name = card.name
image_url = card.image_url
if image_url:
break
print(name, card.set, card.number, image_url)
Befriending the Moths // Imperial Moth NEO 4 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=548294&type=card
流石にもう問題は起こらないでしょう!
from mtgsdk import Card
cards = Card.where(set='MID').where(number=268).all() # 平地
name = None
image_url = None
for card in cards:
if card.foreign_names:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
name = foreign_name['name']
image_url = foreign_name['imageUrl']
break
else:
name = card.name
image_url = card.image_url
break
print(name, card.set, card.number, image_url)
平地 MID 268 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=538277&type=card
日本語版カード画像がカード裏面の画像であるカードが存在する
『イニストラード:真夜中の狩り』のフルアート基本土地でこの問題が起きる。『イニストラード:真紅の契り』もフルアート基本土地だけど、この問題は起きない。
『イニストラード:真夜中の狩り』のフルアート基本土地は日本語版が印刷されて...います。Gathererくん仕事して?
実際の画像データ以外からカード画像がカード裏面の画像かどうかを判別する方法は無い。
なので、どうしても回避したい場合は、ダウンロードしてハッシュ値確認でもするしかない。
from hashlib import md5
from urllib.request import urlopen
from mtgsdk import Card
CARD_BACK_IMAGE_MD5 = 'db0c48db407a907c16ade38de048a441' # あらかじめ確認したカード裏面の画像のMD5
cards = Card.where(set='MID').where(number=268).all()
name = None
image_url = None
for card in cards:
if card.foreign_names:
for foreign_name in card.foreign_names:
if foreign_name['language'] == 'Japanese':
image_url = foreign_name['imageUrl']
if image_url:
with urlopen(url=image_url) as response:
image_data = response.read()
if md5(image_data).hexdigest() != CARD_BACK_IMAGE_MD5:
name = foreign_name['name']
else:
image_url = None
break
if image_url is None:
name = card.name
image_url = card.image_url
if image_url:
break
print(name, card.set, card.number, image_url)
Plains MID 268 http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=538227&type=card