mirror of
https://github.com/ldericher/fftcgtool
synced 2025-01-15 15:02:59 +00:00
cleanup, FFDECKS query
This commit is contained in:
parent
cb7b8c0620
commit
06d83af5c1
7 changed files with 74 additions and 39 deletions
|
@ -17,12 +17,15 @@ class Book:
|
||||||
cards.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0])
|
cards.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0])
|
||||||
|
|
||||||
# all card face URLs
|
# 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
|
# card back URL
|
||||||
urls.append(CARD_BACK_URL)
|
urls.append(CARD_BACK_URL)
|
||||||
|
|
||||||
# multi-threaded download
|
# multi-threaded download
|
||||||
images = ImageLoader.load(urls, language, num_threads)
|
images = ImageLoader.load(urls, num_threads)
|
||||||
# card back Image
|
# card back Image
|
||||||
back_image = images.pop(-1)
|
back_image = images.pop(-1)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Card:
|
||||||
self.__index = index
|
self.__index = index
|
||||||
|
|
||||||
@classmethod
|
@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:
|
if not data:
|
||||||
return cls(
|
return cls(
|
||||||
code=Code(""),
|
code=Code(""),
|
||||||
|
@ -63,7 +63,10 @@ class Card:
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
code=Code(data["Code"]),
|
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}"],
|
name=data[f"Name_{language}"],
|
||||||
text=text,
|
text=text,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,34 +1,17 @@
|
||||||
import requests
|
|
||||||
|
|
||||||
from .card import Card
|
from .card import Card
|
||||||
|
|
||||||
|
|
||||||
class Cards(list[Card]):
|
class Cards(list[Card]):
|
||||||
__API_URL = "https://fftcg.square-enix-games.com/de/get-cards"
|
def __init__(self, name, cards: list[Card] = None):
|
||||||
|
if cards is None:
|
||||||
def __init__(self, name):
|
cards = []
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
super().__init__(cards)
|
||||||
self.__name = name
|
self.__name = name
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"[{', '.join(str(card) for card in self)}]"
|
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
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self.__name
|
return self.__name
|
||||||
|
|
|
@ -4,7 +4,7 @@ import re
|
||||||
|
|
||||||
|
|
||||||
class Code:
|
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_PROMO = re.compile(r"(PR)-([0-9]+)")
|
||||||
__RE_BOSS = re.compile(r"(B)-([0-9]+)")
|
__RE_BOSS = re.compile(r"(B)-([0-9]+)")
|
||||||
|
|
||||||
|
@ -32,7 +32,10 @@ class Code:
|
||||||
"?", "???", "?"
|
"?", "???", "?"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
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:
|
def __hash__(self) -> hash:
|
||||||
return hash(str(self))
|
return hash(str(self))
|
||||||
|
|
|
@ -10,12 +10,10 @@ from fftcg.utils import RESOLUTION
|
||||||
|
|
||||||
|
|
||||||
class ImageLoader(threading.Thread):
|
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__()
|
super().__init__()
|
||||||
|
|
||||||
self.__queue = url_queue
|
self.__queue = url_queue
|
||||||
self.__resolution = resolution
|
|
||||||
self.__language = language
|
|
||||||
self.__images = {}
|
self.__images = {}
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
|
@ -34,7 +32,7 @@ class ImageLoader(threading.Thread):
|
||||||
|
|
||||||
# unify images
|
# unify images
|
||||||
image.convert("RGB")
|
image.convert("RGB")
|
||||||
image = image.resize(self.__resolution, Image.BICUBIC)
|
image = image.resize(RESOLUTION, Image.BICUBIC)
|
||||||
break
|
break
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
pass
|
pass
|
||||||
|
@ -46,14 +44,14 @@ class ImageLoader(threading.Thread):
|
||||||
self.__queue.task_done()
|
self.__queue.task_done()
|
||||||
|
|
||||||
@classmethod
|
@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()
|
url_queue = queue.Queue()
|
||||||
for url in urls:
|
for url in urls:
|
||||||
url_queue.put(url)
|
url_queue.put(url)
|
||||||
|
|
||||||
loaders = []
|
loaders = []
|
||||||
for _ in range(num_threads):
|
for _ in range(num_threads):
|
||||||
loader = cls(url_queue, RESOLUTION, language)
|
loader = cls(url_queue)
|
||||||
loaders.append(loader)
|
loaders.append(loader)
|
||||||
loader.start()
|
loader.start()
|
||||||
|
|
||||||
|
@ -65,7 +63,10 @@ class ImageLoader(threading.Thread):
|
||||||
images |= loader.images
|
images |= loader.images
|
||||||
|
|
||||||
# sort images to match the initial "urls" list
|
# sort images to match the initial "urls" list
|
||||||
images = [images[url] for url in urls]
|
images = [
|
||||||
|
images[url]
|
||||||
|
for url in urls
|
||||||
|
]
|
||||||
|
|
||||||
return images
|
return images
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import requests
|
||||||
import roman
|
import roman
|
||||||
|
|
||||||
|
from . import Card
|
||||||
from .cards import Cards
|
from .cards import Cards
|
||||||
from .ttsdeck import TTSDeck
|
from .ttsdeck import TTSDeck
|
||||||
|
|
||||||
|
|
||||||
class Opus(Cards):
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
params: dict[str, any]
|
||||||
if opus_id.isnumeric():
|
if opus_id.isnumeric():
|
||||||
name = f"Opus {opus_id}"
|
name = f"Opus {opus_id}"
|
||||||
self.__number = opus_id
|
self.__number = opus_id
|
||||||
|
@ -32,8 +36,22 @@ class Opus(Cards):
|
||||||
self.__filename = "?"
|
self.__filename = "?"
|
||||||
params = {"set": "?"}
|
params = {"set": "?"}
|
||||||
|
|
||||||
super().__init__(name)
|
# required params:
|
||||||
self._load(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
|
# remove reprints
|
||||||
for card in self:
|
for card in self:
|
||||||
|
@ -69,7 +87,10 @@ class Opus(Cards):
|
||||||
|
|
||||||
# simple cases: create lambdas for base elemental decks
|
# simple cases: create lambdas for base elemental decks
|
||||||
base_elements = ["Fire", "Ice", "Wind", "Earth", "Lightning", "Water"]
|
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 |= {
|
filters |= {
|
||||||
# light/darkness elemental deck
|
# light/darkness elemental deck
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from .carddb import CardDB
|
from .carddb import CardDB
|
||||||
from .cards import Cards
|
from .cards import Cards
|
||||||
from .code import Code
|
from .code import Code
|
||||||
|
@ -13,7 +17,10 @@ class TTSDeck(Cards):
|
||||||
|
|
||||||
# get cards from carddb
|
# get cards from carddb
|
||||||
carddb = CardDB.get()
|
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 used
|
||||||
unique_face_urls = set([
|
unique_face_urls = set([
|
||||||
|
@ -27,6 +34,20 @@ class TTSDeck(Cards):
|
||||||
for i, url in enumerate(unique_face_urls)
|
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
|
@property
|
||||||
def tts_object(self) -> dict[str, any]:
|
def tts_object(self) -> dict[str, any]:
|
||||||
# build the "CustomDeck" dictionary
|
# build the "CustomDeck" dictionary
|
||||||
|
|
Loading…
Reference in a new issue