Skip to content

Commit

Permalink
Object oriented (#3)
Browse files Browse the repository at this point in the history
* initial commands

* using objects

* DownloadCard.force is False by default

* oopsie

* some commands

* something that doesn't work IDK

* CommandHandler

Finally a strategy that works

* docstrings

* makefile and readme fix

* CommandHandler.commands from list to dict

* /all
  • Loading branch information
NiiMiyo authored Sep 10, 2022
1 parent 224efaf commit cf1bd59
Show file tree
Hide file tree
Showing 19 changed files with 309 additions and 91 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pics/
build/
dist/
bin/
workpath_pyinstaller/

hd_cards_downloader_tracker
hd_fields_downloader_tracker
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
FILENAME = EDOPro-HD-Downloader
LICENSE = HdDownloader.LICENSE.txt
DISTPATH = ./bin
WORKPATH = ./workpath_pyinstaller

build:
pyinstaller main.py -y --distpath "$(DISTPATH)" -F --specpath "$(DISTPATH)" -n "$(FILENAME)" -c --clean
cp $(LICENSE) $(DISTPATH)/$(LICENSE)
pyinstaller main.py -y --distpath "$(DISTPATH)" -F --specpath "$(DISTPATH)" -n "$(FILENAME)" -c --clean --workpath "$(WORKPATH)"
cp "$(LICENSE)" "$(DISTPATH)/$(LICENSE)"

rm -rf build
rm -rf "$(WORKPATH)"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ If you run the program and read the instructions you should be fine.

But for short:

- Insert the name of your deck (without the `.ydk`) extension when asked to download all the images of the cards in it.
- Insert the name of your deck (without the `.ydk` extension) when asked to download all the images of the cards in it.

- Insert `/allcards` to download images for all cards. Will probably take a while.

Expand Down
16 changes: 9 additions & 7 deletions apiaccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
from urllib import request as __request
from json import loads as __loads

api_url = "https://db.ygoprodeck.com/api/v7/cardinfo.php"
__headers = {
API_URL = "https://db.ygoprodeck.com/api/v7/cardinfo.php"
"""Base API URL for YGOProDeck"""

API_HEADER = {
"User-Agent": "NiiMiyo-EDOPro-HD-Downloader/1.1.1"
}

"""Header JSON to be used in an API request"""

def __get_ids_from_api_response(response: __HTTPResponse) -> list[int]:
"""Returns only the ids of the cards requested"""
Expand All @@ -23,10 +25,10 @@ def __get_ids_from_api_response(response: __HTTPResponse) -> list[int]:

def get_all_cards() -> list[int]:
"""Returns the ids of all Yu-Gi-Oh! cards in
db.ygoprodeck.com database"""
`db.ygoprodeck.com` database"""

try:
request = __request.Request(api_url, headers=__headers)
request = __request.Request(API_URL, headers=API_HEADER)
response = __request.urlopen(request)
except Exception as e:
print(f"Error fetching db.ygoprodeck.com: {e}")
Expand All @@ -37,11 +39,11 @@ def get_all_cards() -> list[int]:

def get_all_fields() -> list[int]:
"""Returns the ids of all Yu-Gi-Oh! Field Spell cards in
db.ygoprodeck.comdatabase"""
`db.ygoprodeck.com` database"""

try:
params = r"?type=spell%20card&race=field"
request = __request.Request(api_url + params, headers=__headers)
request = __request.Request(API_URL + params, headers=API_HEADER)
response = __request.urlopen(request)
except Exception as e:
print(e)
Expand Down
35 changes: 35 additions & 0 deletions command_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Optional
from commands.typing import DownloaderCommand
from commands.utils import get_first_word


class CommandHandler:
"""Class to manage the use of commands"""

commands: dict[str, DownloaderCommand] = dict()
"""Dict of all available commands (maps command name to command).
If you add a command you should put it here using
`CommandHandler.add_command` and adding the import on `commands/setup.py`.
"""

@staticmethod
def find_command(user_input: str) -> Optional[DownloaderCommand]:
"""Gets the `DownloaderCommand` that matches user_input. For example,
`force` command for `"/force /allcards"` input.
"""

if not user_input: # If empty string or None
return None

command_used = get_first_word(user_input)
if command_used.startswith("/"):
return CommandHandler.commands.get(command_used[1:])
else:
return None

@staticmethod
def add_command(command: DownloaderCommand):
"""Adds a DownloaderCommand to be available to use on user input"""

CommandHandler.commands[command.name] = command
16 changes: 16 additions & 0 deletions commands/cmd_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from commands.typing import CommandReturn, DownloaderCommand
from command_handler import CommandHandler


def __cmd_all(_: str) -> CommandReturn:
allcards = CommandHandler.commands.get("allcards")
allfields = CommandHandler.commands.get("allfields")

return allcards.action(_) + allfields.action(_) # type: ignore


CommandHandler.add_command(DownloaderCommand(
name="all",
help_text="downloads all cards images and all fields artworks",
action=__cmd_all
))
17 changes: 17 additions & 0 deletions commands/cmd_allcards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from command_handler import CommandHandler
from commands.typing import CommandReturn, DownloadCard, DownloaderCommand
from apiaccess import get_all_cards


def __cmd_all_cards_action(_: str) -> CommandReturn:
return [
DownloadCard(c, False)
for c in get_all_cards()
]


CommandHandler.add_command(DownloaderCommand(
name="allcards",
help_text="downloads all cards",
action=__cmd_all_cards_action
))
17 changes: 17 additions & 0 deletions commands/cmd_allfields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from command_handler import CommandHandler
from commands.typing import DownloadCard, DownloaderCommand, CommandReturn
from apiaccess import get_all_fields


def __cmd_all_fields_action(_: str) -> CommandReturn:
return [
DownloadCard(c, True)
for c in get_all_fields()
]


CommandHandler.add_command(DownloaderCommand(
name="allfields",
help_text="downloads all fields artworks",
action=__cmd_all_fields_action
))
14 changes: 14 additions & 0 deletions commands/cmd_exit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from command_handler import CommandHandler
from commands.typing import CommandReturn, DownloaderCommand


def __cmd_exit_action(_: str) -> CommandReturn:
print("Bye bye <3")
exit(0)


CommandHandler.add_command(DownloaderCommand(
name="exit",
help_text="closes the program",
action=__cmd_exit_action
))
22 changes: 22 additions & 0 deletions commands/cmd_force.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from command_handler import CommandHandler
from commands.typing import CommandReturn, DownloadCard, DownloaderCommand
from commands.utils import get_args
from input_handler import handle_input

def __cmd_force_action(user_input: str) -> CommandReturn:
args = get_args(user_input)
cards = handle_input(args)
if cards is None:
return None

return [
DownloadCard(c.card_id, c.artwork, True)
for c in cards
]

CommandHandler.add_command(DownloaderCommand(
name="force",
shown_name="force <input>",
help_text="executes <input> ignoring trackers (example: /force /allcards)",
action=__cmd_force_action
))
32 changes: 32 additions & 0 deletions commands/cmd_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from commands.typing import CommandReturn, DownloaderCommand
from command_handler import CommandHandler


def __cmd_help_action(_: str) -> CommandReturn:
cmd_column_len = 0
lines: list[tuple[str, str]] = list()

command_list = sorted(
CommandHandler.commands.values(),
key=lambda cmd: cmd.name
)

for cmd in command_list:
sn = cmd.get_shown_name()

lines.append((sn, cmd.help_text))
cmd_column_len = max(cmd_column_len, len(sn))

print("\n".join(
f"/{sn.ljust(cmd_column_len)} - {ht}"
for sn, ht in lines
))

return []


CommandHandler.add_command(DownloaderCommand(
name="help",
help_text="see this text",
action=__cmd_help_action
))
9 changes: 9 additions & 0 deletions commands/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Yes, this is correct
# I'm sorry
def setup_commands():
import commands.cmd_allcards as _
import commands.cmd_allfields as _
import commands.cmd_exit as _
import commands.cmd_force as _
import commands.cmd_help as _
import commands.cmd_all as _
48 changes: 48 additions & 0 deletions commands/typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from typing import Callable, NamedTuple, Optional


class DownloadCard(NamedTuple):
"""Represents a card to be downloaded"""

card_id: int
artwork: bool
force: bool = False


CommandReturn = Optional[list[DownloadCard]]
"""Type a `CommandAction` should return"""

CommandAction = Callable[[str], CommandReturn]
"""Type a `DownloaderCommand.action` should be.
Should only return `None` in case the command fails. If the command does not
download cards, return an empty list.
"""


class DownloaderCommand(NamedTuple):
name: str
"""The name of the command, should be unique"""

help_text: str
"""Text the command shows on `/help`"""

action: CommandAction
"""Function that defines command execution"""

shown_name: Optional[str] = None
"""Name that will be shown on `/help`. If it's `None` then shows `name`"""

def match_string(self) -> str:
"""Returns `/command.name`"""
return f"/{self.name}"

def get_shown_name(self) -> str:
"""Returns `command.shown_name` if it's not None. Otherwise returns
`command.name`
"""

if self.shown_name is None:
return self.name
else:
return self.shown_name
22 changes: 22 additions & 0 deletions commands/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
def get_args(user_input: str) -> str:
"""Receives an user input and returns everything after the first
space character replacing double spaces with single spaces
"""

args = [
a for a in user_input.split(" ")
if a
]

return " ".join(args[1:])


def get_first_word(user_input: str) -> str:
"""Receives an user input and returns everything before the first
space character
"""

if not user_input:
return ""

return user_input.split(" ")[0]
17 changes: 10 additions & 7 deletions deckread.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from os.path import exists as __exists, join as __join
from typing import Optional

from commands.typing import CommandReturn, DownloadCard

deck_folder_path = "./deck/"

Expand All @@ -11,16 +12,15 @@ def __filter_card_id(cards: list[str]) -> list[int]:
ids: list[int] = list()
for c in cards:
try:
int(c)
except ValueError:
continue
else:
c = int(c)
if c not in ids:
ids.append(int(c))
except ValueError:
continue
return ids


def get_deck(deck_name: str) -> Optional[list[int]]:
def get_deck(deck_name: str) -> CommandReturn:
"""Reads a deck file and returns the
ids of the cards in it"""

Expand All @@ -30,4 +30,7 @@ def get_deck(deck_name: str) -> Optional[list[int]]:
return None
deck = open(deck_path, mode="r", encoding="utf8")
cards = __filter_card_id([l.strip() for l in deck.readlines()])
return cards
return [
DownloadCard(c, False)
for c in cards
]
14 changes: 8 additions & 6 deletions downloader.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
from urllib import request as __request
from os.path import join as __join

def download_image(card_id: int, is_artwork: bool = False):
from commands.typing import DownloadCard

def download_image(card: DownloadCard):
"""Downloads the card image or artwork and
puts in the specified folder"""

img_url = "https://storage.googleapis.com/ygoprodeck.com/pics"
if not is_artwork:
img_url += f"/{card_id}.jpg"
if not card.artwork:
img_url += f"/{card.card_id}.jpg"
store_at = "./pics/"
else:
img_url += f"_artgame/{card_id}.jpg"
img_url += f"_artgame/{card.card_id}.jpg"
store_at = "./pics/field/"

file_path = __join(store_at, f"{card_id}.jpg")
file_path = __join(store_at, f"{card.card_id}.jpg")
try:
__request.urlretrieve(img_url, file_path)
return True

except Exception as e:
print(f"Error downloading '{card_id}': {e}")
print(f"Error downloading '{card.card_id}': {e}")
return False
Loading

0 comments on commit cf1bd59

Please sign in to comment.