From f375d5792d36b2d473f4e3fe4e191a53aafee90c Mon Sep 17 00:00:00 2001 From: LDericher <40151420+ldericher@users.noreply.github.com> Date: Wed, 4 Aug 2021 18:36:23 +0200 Subject: [PATCH] composition works --- fftcg/book.py | 54 +++++++++++++++++++++++++++++--------------- fftcg/card.py | 14 ++++++++---- fftcg/imageloader.py | 31 +++++++++++++++++-------- fftcg/opus.py | 35 ++++++++++++++++++++++------ main.py | 40 ++++++++++++++++++++++++++++---- 5 files changed, 129 insertions(+), 45 deletions(-) diff --git a/fftcg/book.py b/fftcg/book.py index 4e11a6f..8de14c5 100644 --- a/fftcg/book.py +++ b/fftcg/book.py @@ -1,25 +1,43 @@ -import queue +import logging + +from PIL import Image + +from .imageloader import ImageLoader + + +def chunks(whole: list, chunk_size): + # while there are elements + while whole: + # get a chunk + yield whole[:chunk_size] + # remove that chunk + whole = whole[chunk_size:] class Book: - def __init__(self, cards): - self.__cards = cards + def __init__(self, cards, grid, resolution, language, num_threads): + logger = logging.getLogger(__name__) - def __get_pages(self, grid): - # cards per sheet + images = ImageLoader.load(cards, resolution, language, num_threads) + images = [images[card] for card in cards] + + # shorthands + # rows and columns per sheet r, c = grid - capacity = r * c - 1 - # flat copy - cards = self.__cards + # width, height per card + w, h = resolution - # while there are cards - while cards: - # get a chunk - yield cards[:capacity] - # remove that chunk - cards = cards[capacity:] + self.__pages = [] + for images in chunks(images, r * c - 1): + page = Image.new("RGB", (c * w, r * h)) + logger.info(f"New image: {page.size[0]}x{page.size[1]}") - def populate(self, grid, resolution, threadnum=16): - card_queue = queue.Queue() - for i, card in enumerate(self.__cards): - card_queue.put((i, card)) + for i, image in enumerate(images): + x, y = (i % c) * w, (i // c) * h + page.paste(image, (x, y, x + w, y + h)) + + self.__pages.append(page) + + def save(self, filename): + for i, page in enumerate(self.__pages): + page.save(filename.format(i)) diff --git a/fftcg/card.py b/fftcg/card.py index 4f43d18..7d456a5 100644 --- a/fftcg/card.py +++ b/fftcg/card.py @@ -49,17 +49,21 @@ class Card: self.__text = data[f"Text_{language}"] def __str__(self): - return f"'{self.__name}' ({'/'.join(self.__elements)}, {self.get_code()})" + return f"'{self.__name}' ({'/'.join(self.__elements)}, {self.code})" # 6-048C - def get_code(self): + @property + def code(self): return f"{self.__opus}-{self.__serial}{self.__rarity}" - def get_name(self): + @property + def name(self): return self.__name - def get_text(self): + @property + def text(self): return self.__text - def get_elements(self): + @property + def elements(self): return self.__elements diff --git a/fftcg/imageloader.py b/fftcg/imageloader.py index d1c87e5..b5ab73f 100644 --- a/fftcg/imageloader.py +++ b/fftcg/imageloader.py @@ -24,15 +24,16 @@ class ImageLoader(threading.Thread): def run(self) -> None: logger = logging.getLogger(__name__) + while not self.__queue.empty(): # take next card - i, card = self.__queue.get() + card = self.__queue.get() # fetch card image (retry on fail) while True: - logger.info("get image for card {}".format(card)) + logger.info(f"get image for card {card}") try: - res = requests.get(ImageLoader.__FACE_URL.format(card.get_code(), self.__language)) + res = requests.get(ImageLoader.__FACE_URL.format(card.code, self.__language)) image = Image.open(io.BytesIO(res.content)) image.convert("RGB") image = image.resize(self.__resolution, Image.BICUBIC) @@ -41,21 +42,31 @@ class ImageLoader(threading.Thread): pass # put image in correct position - self.__images[i] = image + self.__images[card] = image # image is processed self.__queue.task_done() @classmethod - def spawn(cls, cards, resolution, language="eg", num_threads=16): + def load(cls, cards, resolution, language, num_threads): card_queue = queue.Queue() - for i, card in enumerate(cards): - card_queue.put((i, card)) + for card in cards: + card_queue.put(card) + loaders = [] for _ in range(num_threads): - cls(card_queue, resolution, language).start() + loader = cls(card_queue, resolution, language) + loaders.append(loader) + loader.start() - return card_queue + card_queue.join() - def get_images(self): + images = {} + for loader in loaders: + images = {**images, **loader.images} + + return images + + @property + def images(self): return self.__images diff --git a/fftcg/opus.py b/fftcg/opus.py index 3fc9aee..68bae21 100644 --- a/fftcg/opus.py +++ b/fftcg/opus.py @@ -1,3 +1,5 @@ +import logging + import roman from .cards import Cards @@ -5,18 +7,37 @@ from .cards import Cards class Opus(Cards): def __init__(self, number): - if isinstance(number, int): - number = f"Opus {roman.toRoman(number)}" + logger = logging.getLogger(__name__) + + if isinstance(number, str) and number.isnumeric(): + set_name = f"Opus {roman.toRoman(int(number))}" + number = str(number) + + elif number == "Boss Deck Chaos": + set_name = number + number = "B" + + else: + set_name = "?" + number = "?" params = { "text": "", - "element": ["fire"], - "set": [number], + # "element": ["fire"], + "set": [set_name], } Cards.__init__(self, params) + # filter out reprints + reprints = [card for card in self if not card.code.startswith(number)] + for reprint in reprints: + self.remove(reprint) + # sort every element alphabetically - self.sort(key=lambda x: x.get_code()) - self.sort(key=lambda x: x.get_name()) - self.sort(key=lambda x: "Multi" if len(x.get_elements()) > 1 else x.get_elements()[0]) + self.sort(key=lambda x: x.code) + self.sort(key=lambda x: x.name) + self.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0]) + + for card in self: + logger.info(f"imported card {card}") diff --git a/main.py b/main.py index 607e98f..78474b9 100755 --- a/main.py +++ b/main.py @@ -1,15 +1,45 @@ #!/usr/bin/env python3 +import argparse +import logging +import os -from fftcg.imageloader import ImageLoader from fftcg.opus import Opus +from fftcg.book import Book def main(): - opus = Opus(14) - print(opus) + # Setup CLI + parser = argparse.ArgumentParser( + description='Imports FFTCG cards for TT-Sim.') - queue = ImageLoader.spawn(opus, (429, 600)) - queue.join() + parser.add_argument( + 'opusid', + default="2", + metavar="OpusID", + nargs="?", + help='the Opus to import') + + parser.add_argument( + '-n', '--num_threads', + type=int, + default=20, + metavar="COUNT", + help='maximum number of concurrent requests') + + args = parser.parse_args() + + # Setup logging + logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(threadName)s %(message)s') + + opus = Opus(args.opusid) + + # output directory + if not os.path.exists("out"): + os.mkdir("out") + os.chdir("out") + + book = Book(opus, (7, 10), (429, 600), "eg", 16) + book.save(f"opus_{args.opusid}_{{}}.jpg") if __name__ == '__main__':