Add support for downloading bundles

This commit is contained in:
Jack Wilsdon 2022-06-19 13:07:31 +01:00
parent 49ad7a719c
commit 9e006543c2
3 changed files with 27 additions and 7 deletions

View File

@ -2,7 +2,7 @@
Bulk download games from [itch.io](https://itch.io/).
- Can download game jams, browse pages (popular, newest, browse by tag...) and individual games.
- Can download game jams, browse pages (popular, newest, browse by tag...), bundles and individual games.
- Requires Python 3.8+, grab it from PyPI: `pip install itch-dl`
- For development, use [Poetry](https://python-poetry.org/).
@ -19,9 +19,9 @@ More arguments are available - check them out with `itch-dl --help`.
The downloader is able to grab more or less everything you can download via the itch app.
The input URL can be any "Browse" page (top, popular, newest, filtered by tags, etc) or any
game jam. The input can also be a path to a itch.io JSON file with game jam entries, or just
a list of itch.io game URLs (not browse/jam pages!) to download.
The input URL can be any "Browse" page (top, popular, newest, filtered by tags, etc), any
game jam or any bundle. The input can also be a path to a itch.io JSON file with game jam
entries, or just a list of itch.io game URLs (not browse pages!) to download.
**It's expected that the downloader output will not be complete** - logs are stupidly verbose
and it prints a report on failed downloads and external URLs (links to files that are not on

View File

@ -15,7 +15,7 @@ logging.getLogger().setLevel(logging.INFO)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Bulk download stuff from Itch.io.")
parser.add_argument("url_or_path",
help="itch.io URL or path to a game jam entries.json file")
help="itch.io URL or path to either a game jam entries.json file or bundle games.json file")
parser.add_argument("--api-key", metavar="key", default=None,
help="itch.io API key - https://itch.io/user/settings/api-keys")
parser.add_argument("--profile", metavar="profile", default=None,

View File

@ -83,6 +83,21 @@ def get_jobs_for_browse_url(url: str, client: ItchApiClient) -> List[str]:
return list(found_urls)
def get_jobs_for_bundle_json(bundle_json: dict) -> List[str]:
if 'games' not in bundle_json:
raise Exception("Provided JSON is not a valid itch.io bundle JSON.")
return [g['url'] for g in bundle_json['games']]
def get_bundle_json(bundle_id: str, client: ItchApiClient) -> dict:
r = client.get(f"{ITCH_URL}/bundle/{bundle_id}/games.json")
if not r.ok:
raise ItchDownloadError(f"Could not download game list: {r.status_code} {r.reason}")
return r.json()
def get_jobs_for_itch_url(url: str, client: ItchApiClient) -> List[str]:
if url.startswith("http://"):
logging.info("HTTP link provided, upgrading to HTTPS")
@ -116,7 +131,8 @@ def get_jobs_for_itch_url(url: str, client: ItchApiClient) -> List[str]:
return get_jobs_for_browse_url(clean_browse_url, client)
elif site in ("b", "bundle"): # Bundles
raise NotImplementedError("itch-dl cannot download bundles yet.")
bundle_json = get_bundle_json(url_path_parts[1], client)
return get_jobs_for_bundle_json(bundle_json)
elif site in ("j", "jobs"): # Jobs...
raise ValueError("itch-dl cannot download a job.")
@ -149,7 +165,7 @@ def get_jobs_for_itch_url(url: str, client: ItchApiClient) -> List[str]:
def get_jobs_for_path(path: str) -> List[str]:
try: # Game Jam Entries JSON?
try: # Game Jam Entries JSON or Bundle Games JSON?
with open(path, "rb") as f:
json_data = json.load(f)
@ -159,6 +175,10 @@ def get_jobs_for_path(path: str) -> List[str]:
if 'jam_games' in json_data:
logging.info("Parsing provided file as a Game Jam Entries JSON...")
return get_jobs_for_game_jam_json(json_data)
if 'games' in json_data:
logging.info("Parsing provided file as Bundle Games JSON...")
return get_jobs_for_bundle_json(json_data)
except json.JSONDecodeError:
pass # Not a valid JSON, okay...