1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

mtg-sdk-pythonで四苦八苦する①

Last updated at Posted at 2022-03-30

はじめに

MTG Arenaでエクスポートしたデッキリスト(以下、デッキリスト)を画像出力したい。
リミテッド向けとして、以下の特徴を持つとする。

  • 日本語版のカード画像を表示する。
  • クリーチャー呪文/非クリーチャー呪文/土地で分ける。
  • マナ総量で列を分ける。
  • カード名だけでなく、カードのイラストが上1/3くらい見えるようにする。
  • デッキに複数枚あるカードは複数枚表示する(基本土地を除く)。

上記を全て満たすデッキ画像生成器を寡聞にして知らないので自作する。
カード画像はMTG DevelopersGathererの画像の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

Kaito Shizuki
アイエエエ!? 英語版!? 英語版ナンデ!?

他言語版のimage_urlcard.foreign_namesから取得する

card.foreign_namesは辞書foreign_name(仮名)を要素とするリストである。
MTG DevelopersのDocsには「各オブジェクトはキーlanguagenamemultiverseidを持つ」的なことが書かれているが、実際はもっと多い。例えば《漆月魁渡》の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_nameforeign_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

Korvold, Fae-Cursed King
まあ、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

Befriending the Moths // Imperial Moth Befriending the Moths // Imperial Moth
は??

日本語版カードが印刷されているにもかかわらず、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

Befriending the Moths // Imperial Moth

流石にもう問題は起こらないでしょう!

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

平地
ここまでやる必要はあるのだろうか。poslogithubは訝しんだ。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?