diff --git a/fftcg/book.py b/fftcg/book.py index 070aa6d..f4bb98c 100644 --- a/fftcg/book.py +++ b/fftcg/book.py @@ -17,12 +17,15 @@ class Book: cards.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0]) # all card face URLs - urls = [f"https://fftcg.cdn.sewest.net/images/cards/full/{card.code}_{language}.jpg" for card in cards] + urls = [ + f"https://fftcg.cdn.sewest.net/images/cards/full/{card.code}_{language}.jpg" + for card in cards + ] # card back URL urls.append(CARD_BACK_URL) # multi-threaded download - images = ImageLoader.load(urls, language, num_threads) + images = ImageLoader.load(urls, num_threads) # card back Image back_image = images.pop(-1) diff --git a/fftcg/card.py b/fftcg/card.py index c166e87..b9d13a1 100644 --- a/fftcg/card.py +++ b/fftcg/card.py @@ -28,7 +28,7 @@ class Card: self.__index = index @classmethod - def from_data(cls, data: dict[str, any], language: str) -> Card: + def from_square_api_data(cls, data: dict[str, any], language: str) -> Card: if not data: return cls( code=Code(""), @@ -63,7 +63,10 @@ class Card: return cls( code=Code(data["Code"]), - elements=[Card.__ELEMENTS_MAP[element] for element in data["Element"].split("/")], + elements=[ + Card.__ELEMENTS_MAP[element] + for element in data["Element"].split("/") + ], name=data[f"Name_{language}"], text=text, ) diff --git a/fftcg/cards.py b/fftcg/cards.py index 8dc566e..bf72715 100644 --- a/fftcg/cards.py +++ b/fftcg/cards.py @@ -1,34 +1,17 @@ -import requests - from .card import Card class Cards(list[Card]): - __API_URL = "https://fftcg.square-enix-games.com/de/get-cards" - - def __init__(self, name): - super().__init__() + def __init__(self, name, cards: list[Card] = None): + if cards is None: + cards = [] + super().__init__(cards) self.__name = name def __str__(self) -> str: return f"[{', '.join(str(card) for card in self)}]" - def _load(self, params: dict[str, any]) -> None: - # required params: - # text - # supported params: - # [str] text, language, code, multicard="○"|"", ex_burst="○"|"", special="《S》"|"" - # [array] type, element, cost, rarity, power, category_1, set - # [int] exactmatch=0|1 - - if "text" not in params: - params["text"] = "" - - req = requests.post(Cards.__API_URL, json=params) - self.clear() - self.extend([Card.from_data(card_data, "EN") for card_data in req.json()["cards"]]) - @property def name(self) -> str: return self.__name diff --git a/fftcg/code.py b/fftcg/code.py index bc57360..a2085e3 100644 --- a/fftcg/code.py +++ b/fftcg/code.py @@ -4,7 +4,7 @@ import re class Code: - __RE_NUM = re.compile(r"([0-9]+)-([0-9]+)([CRHLS])") + __RE_NUM = re.compile(r"([0-9]+)-([0-9]+)([CRHLS]?)") __RE_PROMO = re.compile(r"(PR)-([0-9]+)") __RE_BOSS = re.compile(r"(B)-([0-9]+)") @@ -32,7 +32,10 @@ class Code: "?", "???", "?" def __str__(self) -> str: - return f"{self.__opus}-{self.__serial}{self.__rarity}" + return f"{self.__opus}-{self.__serial}" + + def __repr__(self) -> str: + return f"Code(\"{self.__opus}-{self.__serial}{self.__rarity}\")" def __hash__(self) -> hash: return hash(str(self)) diff --git a/fftcg/imageloader.py b/fftcg/imageloader.py index 50eb495..28459b8 100644 --- a/fftcg/imageloader.py +++ b/fftcg/imageloader.py @@ -10,12 +10,10 @@ from fftcg.utils import RESOLUTION class ImageLoader(threading.Thread): - def __init__(self, url_queue: queue.Queue, resolution: tuple[int, int], language: str): + def __init__(self, url_queue: queue.Queue): super().__init__() self.__queue = url_queue - self.__resolution = resolution - self.__language = language self.__images = {} def run(self) -> None: @@ -34,7 +32,7 @@ class ImageLoader(threading.Thread): # unify images image.convert("RGB") - image = image.resize(self.__resolution, Image.BICUBIC) + image = image.resize(RESOLUTION, Image.BICUBIC) break except requests.exceptions.RequestException: pass @@ -46,14 +44,14 @@ class ImageLoader(threading.Thread): self.__queue.task_done() @classmethod - def load(cls, urls: list[str], language: str, num_threads: int) -> list[Image.Image]: + def load(cls, urls: list[str], num_threads: int) -> list[Image.Image]: url_queue = queue.Queue() for url in urls: url_queue.put(url) loaders = [] for _ in range(num_threads): - loader = cls(url_queue, RESOLUTION, language) + loader = cls(url_queue) loaders.append(loader) loader.start() @@ -65,7 +63,10 @@ class ImageLoader(threading.Thread): images |= loader.images # sort images to match the initial "urls" list - images = [images[url] for url in urls] + images = [ + images[url] + for url in urls + ] return images diff --git a/fftcg/opus.py b/fftcg/opus.py index b3a8e11..3ab193c 100644 --- a/fftcg/opus.py +++ b/fftcg/opus.py @@ -1,16 +1,20 @@ import logging +import requests import roman +from . import Card from .cards import Cards from .ttsdeck import TTSDeck class Opus(Cards): - def __init__(self, opus_id: str): + __SQUARE_API_URL = "https://fftcg.square-enix-games.com/de/get-cards" + def __init__(self, opus_id: str): logger = logging.getLogger(__name__) + params: dict[str, any] if opus_id.isnumeric(): name = f"Opus {opus_id}" self.__number = opus_id @@ -32,8 +36,22 @@ class Opus(Cards): self.__filename = "?" params = {"set": "?"} - super().__init__(name) - self._load(params) + # required params: + # text + # supported params: + # [str] text, language, code, multicard="○"|"", ex_burst="○"|"", special="《S》"|"" + # [array] type, element, cost, rarity, power, category_1, set + # [int] exactmatch=0|1 + + if "text" not in params: + params["text"] = "" + + # get cards from square api + req = requests.post(Opus.__SQUARE_API_URL, json=params) + super().__init__(name, [ + Card.from_square_api_data(card_data, "EN") + for card_data in req.json()["cards"] + ]) # remove reprints for card in self: @@ -69,7 +87,10 @@ class Opus(Cards): # simple cases: create lambdas for base elemental decks base_elements = ["Fire", "Ice", "Wind", "Earth", "Lightning", "Water"] - filters = {elem: element_filter(elem) for elem in base_elements} + filters = { + elem: element_filter(elem) + for elem in base_elements + } filters |= { # light/darkness elemental deck diff --git a/fftcg/ttsdeck.py b/fftcg/ttsdeck.py index 5cf894a..91b976d 100644 --- a/fftcg/ttsdeck.py +++ b/fftcg/ttsdeck.py @@ -1,5 +1,9 @@ +from __future__ import annotations + import json +import requests + from .carddb import CardDB from .cards import Cards from .code import Code @@ -13,7 +17,10 @@ class TTSDeck(Cards): # get cards from carddb carddb = CardDB.get() - self.extend([carddb[code] for code in codes]) + self.extend([ + carddb[code] + for code in codes + ]) # unique face urls used unique_face_urls = set([ @@ -27,6 +34,20 @@ class TTSDeck(Cards): for i, url in enumerate(unique_face_urls) } + __FFDECKS_API_URL = "https://ffdecks.com/api/deck" + + @classmethod + def from_ffdecks_deck(cls, deck_id: str) -> TTSDeck: + req = requests.get(TTSDeck.__FFDECKS_API_URL, params={"deck_id": deck_id}) + codes = [ + Code(card["card"]["serial_number"]) + for card in req.json()["cards"] + ] + name = req.json()["name"] + description = req.json()["description"] + + return cls(codes, name, description) + @property def tts_object(self) -> dict[str, any]: # build the "CustomDeck" dictionary