From 04dc922230d0e3ec4964015b2631446b34c0e0d1 Mon Sep 17 00:00:00 2001 From: tretrauit Date: Tue, 23 Aug 2022 17:02:20 +0700 Subject: [PATCH] feat: support pre-downloading game & voicepacks --- setup.py | 4 +- worthless/classes/installer/game.py | 11 ++++-- worthless/classes/installer/resource.py | 2 +- worthless/cli.py | 43 ++++++++++++--------- worthless/installer.py | 51 +++++++++++++------------ 5 files changed, 62 insertions(+), 49 deletions(-) diff --git a/setup.py b/setup.py index 2442da4..53f183a 100644 --- a/setup.py +++ b/setup.py @@ -9,12 +9,12 @@ README = (HERE / "README.md").read_text() setup( name='worthless', - version='2.1.2-1', + version='2.2.0', packages=['worthless', 'worthless.classes', 'worthless.classes.launcher', 'worthless.classes.installer'], url='https://git.froggi.es/tretrauit/worthless-launcher', license='MIT License', author='tretrauit', - author_email='tretrauit@gmail.com', + author_email='tretrauit@cachyos.org', description='A worthless CLI launcher written in Python.', long_description=README, long_description_content_type="text/markdown", diff --git a/worthless/classes/installer/game.py b/worthless/classes/installer/game.py index 0c88bb3..61f1d8b 100644 --- a/worthless/classes/installer/game.py +++ b/worthless/classes/installer/game.py @@ -10,7 +10,10 @@ class Game: @staticmethod def from_dict(data): - diffs = [] - for diff in data['diffs']: - diffs.append(Diff.from_dict(diff)) - return Game(Latest.from_dict(data['latest']), diffs, data) + try: + diffs = [] + for diff in data['diffs']: + diffs.append(Diff.from_dict(diff)) + return Game(Latest.from_dict(data['latest']), diffs, data) + except (KeyError, ValueError): + return data diff --git a/worthless/classes/installer/resource.py b/worthless/classes/installer/resource.py index 4d345d8..5a2906a 100644 --- a/worthless/classes/installer/resource.py +++ b/worthless/classes/installer/resource.py @@ -27,4 +27,4 @@ class Resource: @staticmethod def from_dict(data): return Resource(Game.from_dict(data['game']), data['plugin'], data['web_url'], data['force_update'], - data['pre_download_game'], data['deprecated_packages'], data['sdk'], data) + Game.from_dict(data['pre_download_game']), data['deprecated_packages'], data['sdk'], data) diff --git a/worthless/cli.py b/worthless/cli.py index 60e616e..04a10a4 100755 --- a/worthless/cli.py +++ b/worthless/cli.py @@ -12,13 +12,16 @@ import worthless.constants as constants class UI: - def __init__(self, gamedir: str, noconfirm: bool, tempdir: str | Path = None) -> None: + def __init__(self, gamedir: str, noconfirm: bool, tempdir: str | Path = None, pre_download=False) -> None: self._vo_version = None self._noconfirm = noconfirm self._gamedir = gamedir self._launcher = Launcher(gamedir) self._installer = Installer(gamedir, data_dir=tempdir) self._patcher = Patcher(gamedir, data_dir=tempdir) + self._pre_download = pre_download + if self._pre_download: + print("Pre-download is enabled, use at your own risk!") def _ask(self, question): if self._noconfirm: @@ -130,11 +133,11 @@ class UI: async def download_game(self): print("Downloading full game (This will take a long time)...") - await self._installer.download_full_game() + await self._installer.download_full_game(self._pre_download) async def download_game_update(self): print("Downloading game update (This will take a long time)...") - await self._installer.download_game_update() + await self._installer.download_game_update(pre_download=self._pre_download) async def download_voiceover(self, languages: str): res_info = await self._launcher.get_resource_info() @@ -143,7 +146,7 @@ class UI: if not self._installer.voiceover_lang_translate(lng) == vo.language: continue print("Downloading voiceover pack for {} (This will take a long time)...".format(lng)) - await self._installer.download_full_voiceover(lng) + await self._installer.download_full_voiceover(lng, pre_download=self._pre_download) async def download_voiceover_update(self, languages: str): res_info = await self._launcher.get_resource_info() @@ -152,17 +155,20 @@ class UI: if not self._installer.voiceover_lang_translate(lng) == vo.language: continue print("Downloading voiceover update pack for {} (This will take a long time)...".format(lng)) - await self._installer.download_voiceover_update(lng) + await self._installer.download_voiceover_update(lng, pre_download=self._pre_download) async def install_game(self, forced: bool = False): res_info = await self._launcher.get_resource_info() - print("Latest game version: {}".format(res_info.game.latest.version)) + game = res_info.game + if self._pre_download: + game = res_info.pre_download_game + print("Latest game version: {}".format(game.latest.version)) if not self._ask("Do you want to install the game?"): print("Aborting game installation process.") return await self.download_game() - print("Game archive:", res_info.game.latest.get_name()) - await self._install_from_archive(self._installer.temp_path.joinpath(res_info.game.latest.get_name()), forced) + print("Game archive:", game.latest.get_name()) + await self._install_from_archive(self._installer.temp_path.joinpath(game.latest.get_name()), forced) async def install_voiceover(self, languages: str): res_info = await self._launcher.get_resource_info() @@ -172,13 +178,12 @@ class UI: continue if not self._ask("Do you want to install this voiceover pack? ({})".format(lng)): print("Aborting voiceover installation process.") - return + break print("Downloading voiceover pack (This will take a long time)...") - await self._installer.download_full_voiceover(lng) + await self._installer.download_full_voiceover(lng, pre_download=self._pre_download) await self._apply_voiceover_from_archive( self._installer.temp_path.joinpath(vo.get_name()) ) - break async def update_game(self): game_ver = await self._installer.get_game_version() @@ -188,15 +193,18 @@ class UI: print("Current game installation detected: {}".format(game_ver)) diff_archive = await self._installer.get_game_diff_archive() res_info = await self._launcher.get_resource_info() + game = res_info.game + if self._pre_download: + game = res_info.pre_download_game if not diff_archive: print("No game updates available.") return - print("Latest game version: {}".format(res_info.game.latest.version)) + print("Latest game version: {}".format(game.latest.version)) if not self._ask("Do you want to update the game?"): print("Aborting game update process.") return print("Downloading game update (This will take a long time)...") - await self._installer.download_game_update() + await self._installer.download_game_update(pre_download=self._pre_download) print("Installing game update...") await self.install_from_file(self._installer.temp_path.joinpath(diff_archive.get_name())) @@ -213,7 +221,7 @@ class UI: if self._installer.voiceover_lang_translate(lng, "locale") not in installed_voiceovers: await self.install_voiceover(lng) continue - diff_archive = await self._installer.get_voiceover_diff_archive(lng) + diff_archive = await self._installer.get_voiceover_diff_archive(lng, pre_download=self._pre_download) if not diff_archive: print("No voiceover updates available for {}.".format(lng)) continue @@ -221,7 +229,7 @@ class UI: print("Aborting this voiceover language update process.") continue print("Downloading voiceover update (This may takes some time)...") - await self._installer.download_voiceover_update(lng) + await self._installer.download_voiceover_update(lng, pre_download=self._pre_download) print("Installing voiceover update for {}...".format(lng)) await self._apply_voiceover_from_archive(self._installer.temp_path.joinpath(diff_archive.get_name())) @@ -293,14 +301,15 @@ async def main(): help="Update the voiceover pack only (or install if not found)") parser.add_argument("-Syu", "--update-all", action="store_true", help="Update the game and all installed voiceover packs (or install if not found)") + parser.add_argument("-Scc", "--clear-cache", action="store_true", help="Clear cache used by worthless") parser.add_argument("-Rs", "--remove", action="store_true", help="Remove the game (if installed)") parser.add_argument("-Rp", "--remove-patch", action="store_true", help="Revert the game patch (if patched)") parser.add_argument("-Rv", "--remove-voiceover", action="store_true", help="Remove a Voiceover pack (if installed)") parser.add_argument("-V", "--verify", action="store_true", help="Verify the game installation") + parser.add_argument("--predownload", action="store_true", help="Download the game for the next update", default=False) parser.add_argument("--get-game-version", action="store_true", help="Get the current game version") parser.add_argument("--no-overseas", action="store_true", help="Don't use overseas server") parser.add_argument("--check-telemetry", action="store_true", help="Check for the telemetry information") - parser.add_argument("--clear-cache", action="store_true", help="Clear cache used by worthless") parser.add_argument("--from-ver", action="store", help="Override the detected game version", type=str, default=None) parser.add_argument("--from-vo-ver", action="store", help="Override the detected game version for voiceover " "detection", type=str, default=None) @@ -310,7 +319,7 @@ async def main(): if args.temporary_dir: args.temporary_dir.mkdir(parents=True, exist_ok=True) - ui = UI(args.dir, args.noconfirm, args.temporary_dir) + ui = UI(args.dir, args.noconfirm, args.temporary_dir, args.predownload) if args.install and args.update: raise ValueError("Cannot specify both --install and --update arguments.") diff --git a/worthless/installer.py b/worthless/installer.py index 9fc1346..c9ba95d 100644 --- a/worthless/installer.py +++ b/worthless/installer.py @@ -389,19 +389,15 @@ class Installer: self._config.set_game_version(version) self._config.save() - async def download_full_game(self): - resource = await self._launcher.get_resource_info() - if resource is None: - raise RuntimeError("Failed to fetch game resource info.") - archive_name = resource.game.latest.path.split("/")[-1] - await self._download_file(resource.game.latest.path, archive_name, resource.game.latest.size) + async def download_full_game(self, pre_download=False): + game = await self._get_game(pre_download) + archive_name = game.latest.path.split("/")[-1] + await self._download_file(game.latest.path, archive_name, game.latest.size) - async def download_full_voiceover(self, language: str): - archive = await self._launcher.get_resource_info() - if archive is None: - raise RuntimeError("Failed to fetch game resource info.") + async def download_full_voiceover(self, language: str, pre_download=False): + game = await self._get_game(pre_download) translated_lang = self.voiceover_lang_translate(language) - for vo in archive.game.latest.voice_packs: + for vo in game.latest.voice_packs: if vo.language == translated_lang: await self._download_file(vo.path, vo.get_name(), vo.size) @@ -452,52 +448,57 @@ class Installer: raise ValueError("Could not fetch game resource") return game_resource - async def download_game_update(self, from_version: str = None): + async def _get_game(self, pre_download=False): + game_resource = await self._get_game_resource() + game = game_resource.game + if pre_download: + game = game_resource.pre_download_game + return game + + async def download_game_update(self, from_version: str = None, pre_download=False): if not from_version: from_version = await self._get_game_version() - version_info = await self._get_game_resource() - if self._version == version_info.game.latest.version: + game = await self._get_game(pre_download=pre_download) + if self._version == game.latest.version: raise ValueError("Game is already up to date.") - diff_archive = await self.get_game_diff_archive(from_version) + diff_archive = await self.get_game_diff_archive(from_version, pre_download) if diff_archive is None: raise ValueError("Game diff archive is not available for this version, please reinstall.") await self._download_file(diff_archive.path, diff_archive.name, diff_archive.size) - async def download_voiceover_update(self, language: str, from_version: str = None): + async def download_voiceover_update(self, language: str, from_version: str = None, pre_download=False): if not from_version: from_version = await self._get_game_version() - diff_archive = await self.get_voiceover_diff_archive(language, from_version) + diff_archive = await self.get_voiceover_diff_archive(language, from_version, pre_download) if diff_archive is None: raise ValueError("Voiceover diff archive is not available for this version, please reinstall.") await self._download_file(diff_archive.path, diff_archive.name, diff_archive.size) - async def get_voiceover_diff_archive(self, lang: str, from_version: str = None): + async def get_voiceover_diff_archive(self, lang: str, from_version: str = None, pre_download=False): """Gets a diff archive from `from_version` to the latest one If from_version is not specified, it will be taken from the game version. """ if not from_version: from_version = await self._get_game_version() - game_resource = await self._get_game_resource() - if not game_resource: - raise ValueError("Could not fetch game resource") + game = await self._get_game(pre_download=pre_download) translated_lang = self.voiceover_lang_translate(lang) - for v in game_resource.game.diffs: + for v in game.diffs: if v.version != from_version: continue for vo in v.voice_packs: if vo.language == translated_lang: return vo - async def get_game_diff_archive(self, from_version: str = None): + async def get_game_diff_archive(self, from_version: str = None, pre_download=False): """Gets a diff archive from `from_version` to the latest one If from_version is not specified, it will be taken from the game version. """ if not from_version: from_version = await self._get_game_version() - game_resource = await self._get_game_resource() - for v in game_resource.game.diffs: + game = await self._get_game(pre_download=pre_download) + for v in game.diffs: if v.version == from_version: return v