diff --git a/.github/workflows/autopep8.yml b/.github/workflows/autopep8.yml new file mode 100644 index 0000000..bc96e60 --- /dev/null +++ b/.github/workflows/autopep8.yml @@ -0,0 +1,31 @@ +name: autopep8 +on: pull_request +jobs: + autopep8: + # Check if the PR is not raised by this workflow and is not from a fork + if: startsWith(github.head_ref, 'autopep8-patches') == false && github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + - name: autopep8 + id: autopep8 + uses: peter-evans/autopep8@v1 + with: + args: --exit-code --recursive --in-place --aggressive --aggressive . + - name: Set autopep8 branch name + id: vars + run: echo ::set-output name=branch-name::"autopep8-patches/${{ github.head_ref }}" + - name: Create Pull Request + if: steps.autopep8.outputs.exit-code == 2 + uses: peter-evans/create-pull-request@v3 + with: + commit-message: autopep8 action fixes + title: Fixes by autopep8 action + body: This is an auto-generated PR with fixes by autopep8. + labels: autopep8, automated pr + branch: ${{ steps.vars.outputs.branch-name }} + - name: Fail if autopep8 made changes + if: steps.autopep8.outputs.exit-code == 2 + run: exit 1 \ No newline at end of file diff --git a/.github/workflows/poetry-publish.yml b/.github/workflows/poetry-publish.yml new file mode 100644 index 0000000..8815573 --- /dev/null +++ b/.github/workflows/poetry-publish.yml @@ -0,0 +1,14 @@ +name: Python package +on: + push: + tags: + - "v*.*.*" +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build and publish to pypi + uses: JRubics/poetry-publish@v1.10 + with: + pypi_token: ${{ secrets.PYPI_TOKEN }} \ No newline at end of file diff --git a/itchiodl/__init__.py b/itchiodl/__init__.py index c1757b0..e2e7914 100644 --- a/itchiodl/__init__.py +++ b/itchiodl/__init__.py @@ -1,4 +1,4 @@ from .login import LoginWeb, LoginAPI from .bundle import Bundle from .library import Library -from .game import Game \ No newline at end of file +from .game import Game diff --git a/itchiodl/bundle.py b/itchiodl/bundle.py index c8d1597..b7ea42d 100644 --- a/itchiodl/bundle.py +++ b/itchiodl/bundle.py @@ -1,14 +1,15 @@ import requests from bs4 import BeautifulSoup as soup + class Bundle: def __init__(self, login, url): self.login = login self.url = url - + def load_games(self): i = 1 - + r = self.login.get(self.url) s = soup(r.text, "html.parser") pages = int(s.select("span.pager_label a")[-1].text) @@ -18,7 +19,8 @@ class Bundle: print(f"Processing Page {i} of {pages}") def load_game(self, i): - # Load 1 game. This will refresh the game afterwards, as the csrf token will update + # Load 1 game. This will refresh the game afterwards, as the csrf token + # will update r = self.login.get(f"{self.url}?page={i}") s = soup(r.text, "html.parser") for g in s.select("div.game_row"): @@ -26,14 +28,16 @@ class Bundle: if f := g.find("form"): print(f"Processing {name}") - game_id = f.find("input", {"name":"game_id"})["value"] - csrf_token = f.find("input", {"name":"csrf_token"})["value"] + game_id = f.find("input", {"name": "game_id"})["value"] + csrf_token = f.find("input", {"name": "csrf_token"})["value"] - data = {"action":"claim", "game_id":game_id, "csrf_token":csrf_token} + data = { + "action": "claim", + "game_id": game_id, + "csrf_token": csrf_token} r = self.login.post(f"{self.url}?page={i}", data=data) return False - #else: + # else: # print(f"Skipping {name} - Already in Library") return True - diff --git a/itchiodl/bundle_tool/__main__.py b/itchiodl/bundle_tool/__main__.py index c47ae89..9d07c6d 100644 --- a/itchiodl/bundle_tool/__main__.py +++ b/itchiodl/bundle_tool/__main__.py @@ -1,15 +1,17 @@ from getpass import getpass import itchiodl + def main(): user = input("Username: ") password = getpass("Password: ") - + l = itchiodl.LoginWeb(user, password) url = input("Bundle URL: ") b = itchiodl.Bundle(l, url) b.load_games() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/itchiodl/downloader/__main__.py b/itchiodl/downloader/__main__.py index 151b3a5..da04d48 100644 --- a/itchiodl/downloader/__main__.py +++ b/itchiodl/downloader/__main__.py @@ -3,13 +3,17 @@ from getpass import getpass import itchiodl + def main(): parser = argparse.ArgumentParser( prog='python -m hstp', description='Build an ' ) - parser.add_argument("-k", "--api-key", help="Use API key instead of username/password") + parser.add_argument( + "-k", + "--api-key", + help="Use API key instead of username/password") args = parser.parse_args() @@ -26,5 +30,6 @@ def main(): lib.load_games() lib.download_library() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/itchiodl/game.py b/itchiodl/game.py index 55c2bfe..469b8c4 100644 --- a/itchiodl/game.py +++ b/itchiodl/game.py @@ -5,10 +5,10 @@ import os import urllib - import itchiodl.utils -class Game: + +class Game: def __init__(self, data): self.data = data["game"] self.name = self.data["title"] @@ -17,7 +17,7 @@ class Game: self.id = data["id"] self.game_id = data["game_id"] - + matches = re.match(r"https://(.+)\.itch\.io/(.+)", self.link) self.game_slug = matches.group(2) self.publisher_slug = matches.group(1) @@ -26,11 +26,14 @@ class Game: def load_downloads(self, token): self.downloads = [] - r = requests.get(f"https://api.itch.io/games/{self.game_id}/uploads?download_key_id={self.id}", headers={"Authorization": token}) + r = requests.get( + f"https://api.itch.io/games/{self.game_id}/uploads?download_key_id={self.id}", + headers={ + "Authorization": token}) j = r.json() for d in j["uploads"]: self.downloads.append(d) - + def download(self, token): if os.path.exists(f"{self.publisher_slug}/{self.game_slug}.json"): print(f"Skipping Game {self.name}") @@ -52,14 +55,17 @@ class Game: continue # Get UUID - r = requests.post(f"https://api.itch.io/games/{self.game_id}/download-sessions", headers={"Authorization": token}) + r = requests.post( + f"https://api.itch.io/games/{self.game_id}/download-sessions", + headers={ + "Authorization": token}) j = r.json() # Download url = f"https://api.itch.io/uploads/{d['id']}/download?api_key={token}&download_key_id={self.id}&uuid={j['uuid']}" # response_code = urllib.request.urlopen(url).getcode() try: - itchiodl.utils.download(url, path, self.name +" - "+file) + itchiodl.utils.download(url, path, self.name + " - " + file) except itchiodl.utils.NoDownloadError as e: print("Http response is not a download, skipping") @@ -87,11 +93,9 @@ class Game: Error Reason: {e.reason} This game/asset has been skipped please download manually ---------------------------------------------------------\n """) - + continue - - with open(f"{self.publisher_slug}/{self.game_slug}.json", "w") as f: json.dump({ "name": self.name, diff --git a/itchiodl/library.py b/itchiodl/library.py index 112a0a7..a88d11c 100644 --- a/itchiodl/library.py +++ b/itchiodl/library.py @@ -3,6 +3,7 @@ import json from itchiodl.game import Game + class Library: def __init__(self, login): self.login = login @@ -10,10 +11,12 @@ class Library: def load_game_page(self, page): print("Loading page", page) - r = requests.get(f"https://api.itch.io/profile/owned-keys?page={page}", headers={"Authorization": self.login}) + r = requests.get( + f"https://api.itch.io/profile/owned-keys?page={page}", + headers={ + "Authorization": self.login}) j = json.loads(r.text) - for s in j["owned_keys"]: self.games.append(Game(s)) diff --git a/itchiodl/login.py b/itchiodl/login.py index 3e09ba9..e0b9121 100644 --- a/itchiodl/login.py +++ b/itchiodl/login.py @@ -3,30 +3,37 @@ import json from bs4 import BeautifulSoup as soup import requests + def LoginWeb(user, password): - session = requests.Session() - - # GET the page first so we have a valid CSRF token value - login1 = session.get("https://itch.io/login") - s = soup(login1.text, "html.parser") - csrf_token = s.find("input", {"name": "csrf_token"})["value"] - - # Now POST the login - r = session.post("https://itch.io/login", {"username": user, "password":password, "csrf_token": csrf_token}) + session = requests.Session() - if r.status_code != 200: - raise RuntimeError + # GET the page first so we have a valid CSRF token value + login1 = session.get("https://itch.io/login") + s = soup(login1.text, "html.parser") + csrf_token = s.find("input", {"name": "csrf_token"})["value"] + + # Now POST the login + r = session.post("https://itch.io/login", + {"username": user, + "password": password, + "csrf_token": csrf_token}) + + if r.status_code != 200: + raise RuntimeError + + return session - return session def LoginAPI(user, password): - r = requests.post("https://api.itch.io/login", {"username": user, "password":password, "source": "desktop"}) + r = requests.post("https://api.itch.io/login", + {"username": user, + "password": password, + "source": "desktop"}) if r.status_code != 200: raise RuntimeError t = json.loads(r.text) - + if not t["success"]: raise RuntimeError - + return t["key"]["key"] - \ No newline at end of file diff --git a/itchiodl/utils.py b/itchiodl/utils.py index 782d2f8..6f65703 100644 --- a/itchiodl/utils.py +++ b/itchiodl/utils.py @@ -4,14 +4,17 @@ import os from clint.textui import progress + class NoDownloadError(Exception): pass + def download(url, path, desc): print(f"Downloading {desc}") rsp = requests.get(url, stream=True) - if rsp.headers.get('content-length') is None or rsp.headers.get("Content-Disposition") is None: + if rsp.headers.get( + 'content-length') is None or rsp.headers.get("Content-Disposition") is None: raise NoDownloadError("Http response is not a download, skipping") cd = rsp.headers.get("Content-Disposition") @@ -26,7 +29,10 @@ def download(url, path, desc): print(f"{filename} exists but is incomplete, downloading again") with open(f"{path}/{filename}", "wb") as f: - for chunk in progress.bar(rsp.iter_content(chunk_size=1024), expected_size=(total_length/1024) + 1): + for chunk in progress.bar( + rsp.iter_content( + chunk_size=1024), expected_size=( + total_length / 1024) + 1): if chunk: f.write(chunk) f.flush()