Coder Social home page Coder Social logo

mtg-proxies's Introduction

Tests

MtG-Proxies

Create a high quality printable PDF from your decklist or a list of cards you want to proxy.

Features

  • High resolution prints
    In contrast to online tools that provide this service (e.g. MTG Press), this project creates the PDF file locally. This allows to use highest resolution Scryfall scans to create a large, high-dpi PDF file without regard for bandwidth limitations. For example, the generated PDF for a complete Commander decklist has a size of about 140MB.

  • Up-to-date card scans
    By directly utilizing the Scryfall API, all the latest sets are automatically availble as soon as they're available on Scryfall (which is usually incredibly fast). To not overrun Scryfall with requests, this project makes use of Scryfall bulk data to reduce API calls as much as possible. As requested by Scryfall, a small delay of 100ms is added between requests. However, as most work is done with a local copy of the bulk data, this is hardly noticeable.

  • Support for both text and Arena format decklists mtg-proxies can work with both text and Arena format decklists. The Arena format is recommended, as it allows you to keep the same prints when moving decklists between multiple tools. There are even cases (i.e. tokens) where the name alone is not sufficient to uniquely specify a card. The Arena format helps in these case, as set and collector number are unique identifiers. However, as tools often only work with one of these formats, mtg-proxies is a flexible as possible, even supporting mixed mode. This is especially when you are making quick additions to a decklist and don't want to search for set and collector numbers. The convert.py tool can be used to convert decklists between the two formats.

  • Sanity checks and recommender engine
    mtg-proxies warns you if you attempt to print a low-resolution scan and is able to offer alternatives. The convert.py tool can automatically selects the best print for each card in a decklist with high accuracy, eliminating the need to manually select good prints.

  • Token support
    The tokens.py tool appends the tokens created by the cards in a decklist to it, so you don't miss one accidentally. Caveat: This only works when Scryfall has the data on associated tokens. This is the case for cards printed or reprinted since Tenth Edition.

  • ManaStack and Archidekt integration Directly use ManaStack and Archidekt deck ids as input for many functions instead of local decklist files.

    Decks on Archidekt must be set to public to be read.

Usage

  1. Clone or download this repo.
git clone https://github.com/DiddiZ/mtg-proxies.git
cd mtg-proxies
  1. Install requirements. Requires at least Python 3.9.
# On Linux, use `python3` instead of `python`
python -m pip install -e .

You can also use a virtual environment.

  1. (Optional) Prepare your decklist in MtG Arena format. This is not required, but recommended as it allows for more control over the process.
COUNT FULL_NAME (SET) COLLECTOR_NUMBER

E.g.:

1 Alela, Artful Provocateur (ELD) 324
1 Korvold, Fae-Cursed King (ELD) 329
1 Liliana, Dreadhorde General (WAR) 97
1 Murderous Rider // Swift End (ELD) 287

Or use the convert.py tool to convert a plain decklist to Arena format:

python convert.py examples/decklist_text.txt examples/decklist.txt
  1. Create a PDF file.
python print.py examples/decklist.txt decklist.pdf

Examples:

  • Create separate outputs for front and back faces
python print.py examples/decklist.txt decklist_fronts.pdf --face front
python print.py examples/decklist.txt decklist_backs.pdf --face back

Updating

git pull --ff-only
python -m pip install -e .

Help

print

pipenv run python print.py [-h] [--dpi DPI] decklist outfile

Prepare a decklist for printing.

positional arguments:
  decklist_spec         path to a decklist in text/arena format, or manastack:{manastack_id}, or archidekt:{archidekt_id}
  outfile               output file. Supports pdf, png and jpg.

optional arguments:
  -h, --help            show this help message and exit
  --dpi DPI             dpi of output file (default: 300)
  --paper WIDTHxHEIGHT  paper size in inches or preconfigured format (default: a4)
  --scale FLOAT         scaling factor for printed cards (default: 1.0)
  --border_crop PIXELS  how much to crop inner borders of printed cards (default: 14)
  --background COLOR    background color, either by name or by hex code (e.g. black or "#ff0000", default: None)
  --cropmarks, --no-cropmarks
                        add crop marks (default: True)
  --faces {all,front,back}
                        which faces to print (default: all)

convert

usage: pipenv run python convert.py decklist outfile [OPTIONAL ARGUMENTS]

Convert a decklist from text format to arena format or vice-versa.

positional arguments:
  decklist_spec         path to a decklist in text/arena format, or manastack:{manastack_id}, or archidekt:{archidekt_id}
  outfile               output file

optional arguments:
  -h, --help            show this help message and exit
  --format {arena,text}
                        output format (default: arena)
  --clean               remove all non-card lines

tokens

usage: pipenv run python tokens.py decklist [OPTIONAL ARGUMENTS]

Append the tokens created by the cards in a decklist to it.

positional arguments:
  decklist_spec         path to a decklist in text/arena format, or manastack:{manastack_id}, or archidekt:{archidekt_id}

optional arguments:
  -h, --help            show this help message and exit
  --format {arena,text}
                        output format (default: arena)

Example:

python tokens.py examples/token_generators.txt

Deck Value Decomposition

usage: deck_value.py decklist [OPTIONAL ARGUMENTS]

Show deck value decomposition.

positional arguments:
  decklist_spec         path to a decklist in text/arena format, or manastack:{manastack_id}, or archidekt:{archidekt_id}

optional arguments:
  -h, --help            show this help message and exit
  --lump-threshold FLOAT
                        lump together cards with lesser proportional value

Example:

pipenv run python deck_value.py manastack:1234536
pipenv run python deck_value.py archidekt:365563

Acknowledgements

mtg-proxies's People

Contributors

diddiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

mtg-proxies's Issues

Propaganda // Propaganda has no "oracle_id"

Hello,
thank you for the software.
I am not sure if just my decklist had an issue, or something was missing elsewhere. Anyways, I got a keyerror and had to patch in a keycheck to scryfall.py:

def recommend_print(current=None, card_name=None, oracle_id=None, mode="best"):
    if current is not None and oracle_id is None:  # Use oracle id of current
        if "oracle_id" in current:
            oracle_id = current["oracle_id"]

CommandLine:

D:\GitHub\mtg-proxies>python print.py decks\shrines.txt decks\shrines.pdf
Parsing decklist ...
Traceback (most recent call last):
  File "D:\GitHub\mtg-proxies\print.py", line 60, in <module>
    decklist = parse_decklist_spec(args.decklist)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\GitHub\mtg-proxies\mtgproxies\cli.py", line 16, in parse_decklist_spec
    decklist, ok, warnings = parse_decklist(decklist_spec)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\GitHub\mtg-proxies\mtgproxies\decklists\decklist.py", line 137, in parse_decklist
    decklist, ok, warnings = parse_decklist_stream(f)
                             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\GitHub\mtg-proxies\mtgproxies\decklists\decklist.py", line 172, in parse_decklist_stream
    card, warnings_print = validate_print(card_name, set_id, collector_number)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\GitHub\mtg-proxies\mtgproxies\decklists\sanitizing.py", line 108, in validate_print
    recommendation = scryfall.recommend_print(card)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\GitHub\mtg-proxies\scryfall\scryfall.py", line 194, in recommend_print
    oracle_id = current["oracle_id"]
                ~~~~~~~^^^^^^^^^^^^^
KeyError: 'oracle_id'

DeckList, I hope the formating was not the issue and the issue is reproduceable, but maybe it has nothing to do with the list:

Deck
1 Sanctum of Tranquil Light (M21) 33
1 Sol Ring (SLD) 1011
1 Swords to Plowshares (SLD) 1021
1 Swan Song (HA3) 9
1 Sterling Grove (MH2) 293
1 Path to the World Tree (KHM) 186
1 Starfield Mystic (M20) 39
1 Weaver of Harmony (PRM) 98031
1 Sythis, Harvest's Hand (MH2) 377
1 Jukai Naturalist (NEO) 225
1 Sanctum Weaver (PRM) 91307
1 Sanctum of Stone Fangs (M21) 120
1 Go-Shintai of Lost Wisdom (NEO) 55
1 Go-Shintai of Boundless Vigor (NEO) 187
1 Arcane Signet (SLD) 201
1 Demonic Tutor (STA) 90
1 Herald of the Pantheon (C18) 151
1 Argothian Enchantress (G03) 2
1 Farseek (TSR) 363
1 Three Visits (CMR) 686
1 Fellwar Stone (SLD) 708
1 Shigeki, Jukai Visionary (NEO) 394
1 Heroic Intervention (AFC) 161
1 Negate (STA) 81
1 Mana Drain (CMR) 637
1 Enchantress's Presence (C18) 141
1 Teferi's Protection (STA) 11
1 Semblance Anvil (BRR) 115
1 Cloud Key (BRR) 75
1 Honden of Infinite Rage (CHK) 172
1 Starnheim Courser (KHM) 32
1 Sanctum of Shattered Heights (M21) 157
1 Sanctum of Fruitful Harvest (M21) 203
1 Go-Shintai of Ancient Wars (NEO) 144
1 Sisay, Weatherlight Captain (SLD) 9999
1 Tameshi, Reality Architect (NEO) 375
1 Mesa Enchantress (SLD) 282
1 Verduran Enchantress (SLD) 1004
1 Idyllic Tutor (SLD) 1020
1 Chromatic Lantern (BRR) 73
1 Honor-Worn Shaku (DMC) 187
1 Archon of Sun's Grace (THB) 298
1 Honden of Cleansing Fire (CHK) 14
1 Honden of Night's Reach (CHK) 116
1 Sanctum of Calm Waters (M21) 68
1 Go-Shintai of Shared Purpose (NEO) 14
1 Go-Shintai of Hidden Cruelty (NEO) 97
1 Go-Shintai of Life's Origin (NEC) 66
1 Calix, Destiny's Hand (THB) 257
1 Captain Sisay (SLD) 51
1 Honden of Seeing Winds (CHK) 69
1 Honden of Life's Web (CHK) 213
1 Elspeth Conquers Death (THB) 13
1 Shrine Steward (NEO) 259
1 Sanctum of All (PRM) 82020
1 Prismatic Geoscope (PLIST) 266
1 Time Wipe (PRM) 72219
1 Plea for Guidance (BNG) 24
1 Farewell (NEO) 365
1 Shark Typhoon (SLC) 2020
1 The Kami War // O-Kagachi Made Manifest (NEO) 362
1 Eerie Ultimatum (IKO) 332
1 Genesis Ultimatum (IKO) 336
1 Ruinous Ultimatum (IKO) 343
1 Brilliant Restoration (NEO) 363
1 The World Tree (KHM) 373
1 Field of the Dead (M20) 247
1 Indatha Triome (IKO) 309
1 Ketria Triome (IKO) 310
1 Raugrin Triome (IKO) 311
1 Savai Triome (IKO) 312
1 Zagoth Triome (IKO) 313
1 Command Tower (CC1) 8
1 Jetmir's Garden (SNC) 291
1 Raffine's Tower (SNC) 292
1 Spara's Headquarters (SNC) 293
1 Xander's Lounge (SNC) 294
1 Ziatora's Proving Ground (SNC) 295
1 Tundra (OLCG) 2015
1 Underground Sea (OLGC) 2016
1 Badlands (OLGC) 2016
1 Taiga (OLGC) 2017
1 Savannah (OLGC) 2017
1 Scrubland (OLGC) 2018
1 Volcanic Island (OLGC) 2018
1 Bayou (OLGC) 2019
1 Plateau (OLGC) 2018
1 Tropical Island (OLGC) 2019
1 Flooded Strand (EXP) 16
1 Polluted Delta (EXP) 17
1 Bloodstained Mire (EXP) 18
1 Wooded Foothills (EXP) 19
1 Windswept Heath (EXP) 20
1 Marsh Flats (EXP) 21
1 Scalding Tarn (EXP) 22
1 Verdant Catacombs (EXP) 23
1 Arid Mesa (EXP) 24
1 Misty Rainforest (EXP) 25
1 Krosan Verge (MOC) 412
1 Mana Confluence (EA2) 22
1 Watery Grave (PRM) 72299
1 Stomping Ground (PRM) 72313
1 Godless Shrine (PRM) 72305
1 Overgrown Tomb (PRM) 72315
1 Breeding Pool (PRM) 72311
1 Steam Vents (PRM) 72307
1 Sacred Foundry (PRM) 72309
1 Hallowed Fountain (PRM) 72297
1 Blood Crypt (PRM) 72301
1 Eidolon of Blossoms (PRM) 52318
1 Setessan Champion (THB) 331
1 Ghostly Prison (SLD) 424
1 Propaganda // Propaganda (SLD) 381
1 Out of Time (MH2) 442
1 Maelstrom Nexus (2XM) 206
1 Starfield of Nyx (ORI) 33
1 Sphere of Safety (SLD) 280
1 Collective Restraint (INV) 49

unrecognized arguments

I may just be formatting incorrectly but any attempts to print a page with different paper size and no border crop results in unrecognized arguments or errors on the arguments. What is the proper way to format a decklist for printing with those arguments supposed to be?

Scryfall API now requires headers

Not sure if this affects the execution of the app, just wanted to raise attention to this new change.

https://scryfall.com/blog/user-agent-and-accept-header-now-required-on-the-api-225

"We’ve made a change to our API that hardens requirements on the headers you send. You now must provide a User-Agent header and an Accept header.

Your User-Agent header should reflect the name and version of your application, e.g. MtgExampleApp/1.1. Do not allow HTTP libraries to choose the header for you. If you are connecting with browser-based JavaScript, do not change the browser User-Agent.
The Accept header must be present, but you can provide a generic preference. For example, both of these are okay: Accept: */* and Accept: application/json;q=0.9,*/*;q=0.8."

Frustration with Dual Faced Cards

I wish there was a way to have the backside of DFCs be appended to the end of the pdf, or added to a second pdf instead.
This would be helpful when printing proxies on cardstock, to separately print the backsides on regular paper and then glue them to the cardstock back

Thank you for the wonderful tool

FPDF error: 16-bit depth not supported

I'm getting this issue while trying to export a decklist.

RuntimeError: FPDF error: 16-bit depth not supported: /var/folders/g4/8203zyrj6jsbpj2dw625y1xh0000gn/T/scryfall_cache/png_front_73b58a5d-1eed-41ca-a2ea-d17763e7e22d.png

It seems to be always getting to card 30 while plotting, but I changed the printing in the decklist, I even moved it to position 31 to see if it would make any difference, but no luck, still errors out on card 30 during plotting.

EDIT1: I was able to identify the problematic card. Ghost Quarter - Secret Lair Drop - SLD(468)
The card may indeed be 16-bit, but why would that be a problem?

Token detection

Extract created tokes from decklist and add them to the list

Missing scaling of the card size

Some output deviced rescale the input image and therefore the print file needs to be rescaled accordingly.
Some print devices even upscale the image and crop it later to the correct size.

An argument (-scale), which allows to counteract this behaviour would be very handy.
The programm rescales the image relative to the output size, while remaining the desired output size of the image.

Input as total value:
python print.py -scale 95 examples/decklist.txt decklist.pdf
The resulting image will be rescaled to 95% of its original size

Input as relative value
python print.py -scale +3 examples/decklist.txt decklist.pdf
The resulting image will be rescaled to 103% of its original size

python print.py -scale -7 examples/decklist.txt decklist.pdf
The resulting image will be rescaled to 93% of its original size

Corner artifacts in scans - define crop style (?)

It is only possible to crop inner borders of the images.

a attribute, which defines the crop behaviour would allow to have also outer border crop.

e.g.
python print.py -border_crop 5 -crop_style INNER examples/decklist.txt decklist.pdf
crop only the inner border of the cards by 5px (default behaviour)

python print.py -border_crop 5 -crop_style ALL examples/decklist.txt decklist.pdf
crop all borders of the cards by 5px.

Choose background color

The background color is always white (default setting)

It would be nice to have an option to choose an arbitary color for the background.

e.g.

human readable input:
python print.py --background "black" examples/decklist.txt decklist.pdf
rgb-valued input:
python print.py --background (200,200,200) examples/decklist.txt decklist.pdf

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.