mirror of
https://github.com/ldericher/fftcgtool
synced 2025-01-15 15:02:59 +00:00
image loader
This commit is contained in:
parent
73e88349b6
commit
3df5911123
6 changed files with 148 additions and 20 deletions
27
fftcg/book.py
Normal file
27
fftcg/book.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import queue
|
||||||
|
|
||||||
|
from .imageloader import ImageLoader
|
||||||
|
|
||||||
|
|
||||||
|
class Book:
|
||||||
|
def __init__(self, cards):
|
||||||
|
self.__cards = cards
|
||||||
|
|
||||||
|
def __get_pages(self, grid):
|
||||||
|
# cards per sheet
|
||||||
|
r, c = grid
|
||||||
|
capacity = r*c - 1
|
||||||
|
# flat copy
|
||||||
|
cards = self.__cards
|
||||||
|
|
||||||
|
# while there are cards
|
||||||
|
while cards:
|
||||||
|
# get a chunk
|
||||||
|
yield cards[:capacity]
|
||||||
|
# remove that chunk
|
||||||
|
cards = cards[capacity:]
|
||||||
|
|
||||||
|
def populate(self, grid, resolution, threadnum=16):
|
||||||
|
card_queue = queue.Queue()
|
||||||
|
for i, card in enumerate(self.__cards):
|
||||||
|
card_queue.put((i, card))
|
|
@ -2,40 +2,64 @@ import re
|
||||||
|
|
||||||
|
|
||||||
class Card:
|
class Card:
|
||||||
def __init__(self, data):
|
__ELEMENTS_MAP = {
|
||||||
|
'火': "Fire",
|
||||||
|
'氷': "Ice",
|
||||||
|
'風': "Wind",
|
||||||
|
'土': "Earth",
|
||||||
|
'雷': "Lightning",
|
||||||
|
'水': "Water",
|
||||||
|
'光': "Light",
|
||||||
|
'闇': "Darkness"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, data, language="EN"):
|
||||||
if not data:
|
if not data:
|
||||||
self.__opus = "0"
|
self.__opus = "0"
|
||||||
self.__serial = "000"
|
self.__serial = "000"
|
||||||
self.__rarity = "X"
|
self.__rarity = "X"
|
||||||
self.__element = None
|
self.__elements = None
|
||||||
self.__description = None
|
self.__text = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if str(data["Code"])[0].isnumeric():
|
if str(data["Code"])[0].isnumeric():
|
||||||
|
# card code starts with a number
|
||||||
self.__opus, self.__serial, self.__rarity = \
|
self.__opus, self.__serial, self.__rarity = \
|
||||||
re.match(r'([0-9]+)-([0-9]+)([CRHLS])', data["Code"]).groups()
|
re.match(r'([0-9]+)-([0-9]+)([CRHLS])', data["Code"]).groups()
|
||||||
|
|
||||||
elif str(data["Code"]).startswith("PR"):
|
elif str(data["Code"]).startswith("PR"):
|
||||||
|
# card code starts with "PR"
|
||||||
self.__opus, self.__serial = \
|
self.__opus, self.__serial = \
|
||||||
re.match(r'(PR)-([0-9]+)', data["Code"]).groups()
|
re.match(r'(PR)-([0-9]+)', data["Code"]).groups()
|
||||||
self.__rarity = ""
|
self.__rarity = ""
|
||||||
|
|
||||||
elif str(data["Code"]).startswith("B"):
|
elif str(data["Code"]).startswith("B"):
|
||||||
|
# card code starts with "B"
|
||||||
self.__opus, self.__serial = \
|
self.__opus, self.__serial = \
|
||||||
re.match(r'(B)-([0-9]+)', data["Code"]).groups()
|
re.match(r'(B)-([0-9]+)', data["Code"]).groups()
|
||||||
self.__rarity = ""
|
self.__rarity = ""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# card code not recognized
|
||||||
self.__opus, self.__serial, self.__rarity = \
|
self.__opus, self.__serial, self.__rarity = \
|
||||||
"?", "???", "?"
|
"?", "???", "?"
|
||||||
|
|
||||||
self.__name = data["Name_EN"]
|
self.__elements = [Card.__ELEMENTS_MAP[element] for element in data["Element"].split("/")]
|
||||||
self.__element = data["Element"].split("/")
|
self.__name = data[f"Name_{language}"]
|
||||||
self.__description = data["Text_EN"]
|
self.__text = data[f"Text_{language}"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"'{self.__name}' ({self.__element}, {self.get_id()})"
|
return f"'{self.__name}' ({'/'.join(self.__elements)}, {self.get_code()})"
|
||||||
|
|
||||||
# 6-048C
|
# 6-048C
|
||||||
def get_id(self):
|
def get_code(self):
|
||||||
return f"{self.__opus}-{self.__serial}{self.__rarity}"
|
return f"{self.__opus}-{self.__serial}{self.__rarity}"
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
return self.__text
|
||||||
|
|
||||||
|
def get_elements(self):
|
||||||
|
return self.__elements
|
||||||
|
|
|
@ -2,19 +2,26 @@ import requests
|
||||||
|
|
||||||
from .card import Card
|
from .card import Card
|
||||||
|
|
||||||
APIURL = "https://fftcg.square-enix-games.com/de/get-cards"
|
|
||||||
|
|
||||||
|
class Cards(list):
|
||||||
|
__API_URL = "https://fftcg.square-enix-games.com/de/get-cards"
|
||||||
|
|
||||||
class Cards:
|
|
||||||
def __init__(self, params):
|
def __init__(self, params):
|
||||||
# supported params:
|
list.__init__(self)
|
||||||
# [str] text (required)
|
|
||||||
# [array] type, element, cost, rarity, power, category_1, set
|
|
||||||
# [str] language, code, multicard="○"|"", ex_burst="○"|"", special="《S》"|""
|
|
||||||
# [int] exactmatch=0|1
|
|
||||||
|
|
||||||
req = requests.post(APIURL, json=params)
|
if isinstance(params, dict):
|
||||||
self.__content = [Card(card_data) for card_data in req.json()["cards"]]
|
# 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
|
||||||
|
|
||||||
|
req = requests.post(Cards.__API_URL, json=params)
|
||||||
|
self.extend([Card(card_data) for card_data in req.json()["cards"]])
|
||||||
|
|
||||||
|
elif isinstance(params, list):
|
||||||
|
self.extend(params)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "\n".join(str(card) for card in self.__content)
|
return "\n".join(str(card) for card in self)
|
||||||
|
|
61
fftcg/imageloader.py
Normal file
61
fftcg/imageloader.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
import queue
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class ImageLoader(threading.Thread):
|
||||||
|
# Card faces by Square API
|
||||||
|
__FACE_URL = "https://fftcg.cdn.sewest.net/images/cards/full/{}_{}.jpg"
|
||||||
|
|
||||||
|
# Card back image by Aurik
|
||||||
|
__BACK_URL = "http://cloud-3.steamusercontent.com/ugc/948455238665576576/85063172B8C340602E8D6C783A457122F53F7843/"
|
||||||
|
|
||||||
|
def __init__(self, card_queue, resolution, language):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
|
self.__queue = card_queue
|
||||||
|
self.__resolution = resolution
|
||||||
|
self.__language = language
|
||||||
|
self.__images = {}
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
while not self.__queue.empty():
|
||||||
|
# take next card
|
||||||
|
i, card = self.__queue.get()
|
||||||
|
|
||||||
|
# fetch card image (retry on fail)
|
||||||
|
while True:
|
||||||
|
logger.info("get image for card {}".format(card))
|
||||||
|
try:
|
||||||
|
res = requests.get(ImageLoader.__FACE_URL.format(card.get_code(), self.__language))
|
||||||
|
image = Image.open(io.BytesIO(res.content))
|
||||||
|
image.convert("RGB")
|
||||||
|
image = image.resize(self.__resolution, Image.BICUBIC)
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# put image in correct position
|
||||||
|
self.__images[i] = image
|
||||||
|
|
||||||
|
# image is processed
|
||||||
|
self.__queue.task_done()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def spawn(cls, cards, resolution, language="eg", num_threads=16):
|
||||||
|
card_queue = queue.Queue()
|
||||||
|
for i, card in enumerate(cards):
|
||||||
|
card_queue.put((i, card))
|
||||||
|
|
||||||
|
for _ in range(num_threads):
|
||||||
|
cls(card_queue, resolution, language).start()
|
||||||
|
|
||||||
|
return card_queue
|
||||||
|
|
||||||
|
def get_images(self):
|
||||||
|
return self.__images
|
|
@ -1,4 +1,4 @@
|
||||||
from roman import toRoman
|
import roman
|
||||||
|
|
||||||
from .cards import Cards
|
from .cards import Cards
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from .cards import Cards
|
||||||
class Opus(Cards):
|
class Opus(Cards):
|
||||||
def __init__(self, number):
|
def __init__(self, number):
|
||||||
if isinstance(number, int):
|
if isinstance(number, int):
|
||||||
number = f"Opus {toRoman(number)}"
|
number = f"Opus {roman.toRoman(number)}"
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"text": "",
|
"text": "",
|
||||||
|
@ -15,3 +15,8 @@ class Opus(Cards):
|
||||||
}
|
}
|
||||||
|
|
||||||
Cards.__init__(self, params)
|
Cards.__init__(self, params)
|
||||||
|
|
||||||
|
# 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])
|
||||||
|
|
4
main.py
4
main.py
|
@ -1,12 +1,16 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from fftcg.opus import Opus
|
from fftcg.opus import Opus
|
||||||
|
from fftcg.imageloader import ImageLoader
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
opus = Opus(14)
|
opus = Opus(14)
|
||||||
print(opus)
|
print(opus)
|
||||||
|
|
||||||
|
queue = ImageLoader.spawn(opus, (429, 600))
|
||||||
|
queue.join()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in a new issue