Searching for Magic Cards via the Command Line and Displaying Them Using iTerm's imgcat
I have a general tendency to google if someone has made a (usually Python) package for various things and when I recently began playing Magic: The Gathering again with some friends, I could not resist the urge to do so. It turns out that someone did make a package and in fact there are quite a few language bindings available.
Coincidentally, I remembered one of the useful tools packaged with the iTerm v3
console application for Mac: imgcat
(docs). It works like cat
but
for images as you can see below.
It would be kinda cool if we could combine the two to search for a given card by
name from the command line and use imgcat
to display it through the terminal.
The following Python script does exactly this in only 22 lines (excluding
comments and error-checking). Let us start with the standard Python header stuff
(shebang and file encoding) and
import the necessary packages.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Query a Magic: The Gathering card and show it via iTerm's imgcat script."""
import mtgsdk # The Magic: The Gathering SDK
import os # For expanding the userpath to the imgcat script
import subprocess # For calling imgcat
import sys # For getting the argument (card name) from the command line
import tempfile # For creating a temporary, named image file
import urllib.request # For downloading the card's image
We are using Python 3’s urllib
here since mtgsdk
targets python 3.4+. Next,
we need two functions: One for querying the database with the card name and one
for getting the image from the card’s image url.
def query_by_name(card_name):
"""Query the mtg database and return the first card with a url or None."""
return next((card for card in mtgsdk.Card.where(name=card_name).iter()
if card.image_url), None)
We use the Card API’s iter
method instead of all
to avoid downloading all
cards associated with the name at once. Combined with a generator expression for
filtering all the cards that do not have an associated url and the
next
function we
can get the first card that has an image url. We use the optional second
argument of next
to return None
if there are either no cards with the given
name or if none of them had a url associated with them. Otherwise, next
would
throw a
StopIteration
exception.
def get_card_image(image_url):
"""Return the image bytes from a given image url."""
return urllib.request.urlopen(image_url).read()
Getting the image contents of the card is pretty straight-forward. We use
urlopen
and read its entire contents.
if __name__ == '__main__':
card = query_by_name(sys.argv[1])
if card:
if not card.image_url:
sys.exit("Error: Card has no associated image url")
with tempfile.NamedTemporaryFile() as ntf:
ntf.write(get_card_image(card.image_url))
subprocess.check_call([os.path.expanduser('~/imgcat.sh'),
ntf.name])
else:
sys.exit("Error: Unable to find card '{0}'".format(sys.argv[1]))
First, we query the card name and ensure that both a card was found and one of
them had an image url associated with it. Then we create a temorary, named file
(since we need to refer to it by name for the imgcat
script), write the image
bytes to it and finally call the script using
subprocess.check_call
.
Notice that we also use
os.path.expanduser
to expand the tilde, since I have put the script in my home directory. This is
what it looks like in my terminal:
Notice that the script is devoid of error-checking. If you want a full-blown command line interface for querying magic cards, check this project out.