feat: support pre-downloading game & voicepacks

This commit is contained in:
tretrauit 2022-08-23 17:02:20 +07:00
parent 34a8ab6d99
commit 04dc922230
Signed by: tretrauit
GPG Key ID: CDDE1C97EE305DAF
5 changed files with 62 additions and 49 deletions

View File

@ -9,12 +9,12 @@ README = (HERE / "README.md").read_text()
setup( setup(
name='worthless', name='worthless',
version='2.1.2-1', version='2.2.0',
packages=['worthless', 'worthless.classes', 'worthless.classes.launcher', 'worthless.classes.installer'], packages=['worthless', 'worthless.classes', 'worthless.classes.launcher', 'worthless.classes.installer'],
url='https://git.froggi.es/tretrauit/worthless-launcher', url='https://git.froggi.es/tretrauit/worthless-launcher',
license='MIT License', license='MIT License',
author='tretrauit', author='tretrauit',
author_email='tretrauit@gmail.com', author_email='tretrauit@cachyos.org',
description='A worthless CLI launcher written in Python.', description='A worthless CLI launcher written in Python.',
long_description=README, long_description=README,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@ -10,7 +10,10 @@ class Game:
@staticmethod @staticmethod
def from_dict(data): def from_dict(data):
diffs = [] try:
for diff in data['diffs']: diffs = []
diffs.append(Diff.from_dict(diff)) for diff in data['diffs']:
return Game(Latest.from_dict(data['latest']), diffs, data) diffs.append(Diff.from_dict(diff))
return Game(Latest.from_dict(data['latest']), diffs, data)
except (KeyError, ValueError):
return data

View File

@ -27,4 +27,4 @@ class Resource:
@staticmethod @staticmethod
def from_dict(data): def from_dict(data):
return Resource(Game.from_dict(data['game']), data['plugin'], data['web_url'], data['force_update'], 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)

View File

@ -12,13 +12,16 @@ import worthless.constants as constants
class UI: 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._vo_version = None
self._noconfirm = noconfirm self._noconfirm = noconfirm
self._gamedir = gamedir self._gamedir = gamedir
self._launcher = Launcher(gamedir) self._launcher = Launcher(gamedir)
self._installer = Installer(gamedir, data_dir=tempdir) self._installer = Installer(gamedir, data_dir=tempdir)
self._patcher = Patcher(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): def _ask(self, question):
if self._noconfirm: if self._noconfirm:
@ -130,11 +133,11 @@ class UI:
async def download_game(self): async def download_game(self):
print("Downloading full game (This will take a long time)...") 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): async def download_game_update(self):
print("Downloading game update (This will take a long time)...") 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): async def download_voiceover(self, languages: str):
res_info = await self._launcher.get_resource_info() res_info = await self._launcher.get_resource_info()
@ -143,7 +146,7 @@ class UI:
if not self._installer.voiceover_lang_translate(lng) == vo.language: if not self._installer.voiceover_lang_translate(lng) == vo.language:
continue continue
print("Downloading voiceover pack for {} (This will take a long time)...".format(lng)) 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): async def download_voiceover_update(self, languages: str):
res_info = await self._launcher.get_resource_info() res_info = await self._launcher.get_resource_info()
@ -152,17 +155,20 @@ class UI:
if not self._installer.voiceover_lang_translate(lng) == vo.language: if not self._installer.voiceover_lang_translate(lng) == vo.language:
continue continue
print("Downloading voiceover update pack for {} (This will take a long time)...".format(lng)) 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): async def install_game(self, forced: bool = False):
res_info = await self._launcher.get_resource_info() 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?"): if not self._ask("Do you want to install the game?"):
print("Aborting game installation process.") print("Aborting game installation process.")
return return
await self.download_game() await self.download_game()
print("Game archive:", res_info.game.latest.get_name()) print("Game archive:", game.latest.get_name())
await self._install_from_archive(self._installer.temp_path.joinpath(res_info.game.latest.get_name()), forced) await self._install_from_archive(self._installer.temp_path.joinpath(game.latest.get_name()), forced)
async def install_voiceover(self, languages: str): async def install_voiceover(self, languages: str):
res_info = await self._launcher.get_resource_info() res_info = await self._launcher.get_resource_info()
@ -172,13 +178,12 @@ class UI:
continue continue
if not self._ask("Do you want to install this voiceover pack? ({})".format(lng)): if not self._ask("Do you want to install this voiceover pack? ({})".format(lng)):
print("Aborting voiceover installation process.") print("Aborting voiceover installation process.")
return break
print("Downloading voiceover pack (This will take a long time)...") 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( await self._apply_voiceover_from_archive(
self._installer.temp_path.joinpath(vo.get_name()) self._installer.temp_path.joinpath(vo.get_name())
) )
break
async def update_game(self): async def update_game(self):
game_ver = await self._installer.get_game_version() game_ver = await self._installer.get_game_version()
@ -188,15 +193,18 @@ class UI:
print("Current game installation detected: {}".format(game_ver)) print("Current game installation detected: {}".format(game_ver))
diff_archive = await self._installer.get_game_diff_archive() diff_archive = await self._installer.get_game_diff_archive()
res_info = await self._launcher.get_resource_info() 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: if not diff_archive:
print("No game updates available.") print("No game updates available.")
return 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?"): if not self._ask("Do you want to update the game?"):
print("Aborting game update process.") print("Aborting game update process.")
return return
print("Downloading game update (This will take a long time)...") 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...") print("Installing game update...")
await self.install_from_file(self._installer.temp_path.joinpath(diff_archive.get_name())) 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: if self._installer.voiceover_lang_translate(lng, "locale") not in installed_voiceovers:
await self.install_voiceover(lng) await self.install_voiceover(lng)
continue 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: if not diff_archive:
print("No voiceover updates available for {}.".format(lng)) print("No voiceover updates available for {}.".format(lng))
continue continue
@ -221,7 +229,7 @@ class UI:
print("Aborting this voiceover language update process.") print("Aborting this voiceover language update process.")
continue continue
print("Downloading voiceover update (This may takes some time)...") 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)) print("Installing voiceover update for {}...".format(lng))
await self._apply_voiceover_from_archive(self._installer.temp_path.joinpath(diff_archive.get_name())) 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)") help="Update the voiceover pack only (or install if not found)")
parser.add_argument("-Syu", "--update-all", action="store_true", parser.add_argument("-Syu", "--update-all", action="store_true",
help="Update the game and all installed voiceover packs (or install if not found)") 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("-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("-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("-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("-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("--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("--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("--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-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 " parser.add_argument("--from-vo-ver", action="store", help="Override the detected game version for voiceover "
"detection", type=str, default=None) "detection", type=str, default=None)
@ -310,7 +319,7 @@ async def main():
if args.temporary_dir: if args.temporary_dir:
args.temporary_dir.mkdir(parents=True, exist_ok=True) 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: if args.install and args.update:
raise ValueError("Cannot specify both --install and --update arguments.") raise ValueError("Cannot specify both --install and --update arguments.")

View File

@ -389,19 +389,15 @@ class Installer:
self._config.set_game_version(version) self._config.set_game_version(version)
self._config.save() self._config.save()
async def download_full_game(self): async def download_full_game(self, pre_download=False):
resource = await self._launcher.get_resource_info() game = await self._get_game(pre_download)
if resource is None: archive_name = game.latest.path.split("/")[-1]
raise RuntimeError("Failed to fetch game resource info.") await self._download_file(game.latest.path, archive_name, game.latest.size)
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_voiceover(self, language: str): async def download_full_voiceover(self, language: str, pre_download=False):
archive = await self._launcher.get_resource_info() game = await self._get_game(pre_download)
if archive is None:
raise RuntimeError("Failed to fetch game resource info.")
translated_lang = self.voiceover_lang_translate(language) 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: if vo.language == translated_lang:
await self._download_file(vo.path, vo.get_name(), vo.size) await self._download_file(vo.path, vo.get_name(), vo.size)
@ -452,52 +448,57 @@ class Installer:
raise ValueError("Could not fetch game resource") raise ValueError("Could not fetch game resource")
return 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: if not from_version:
from_version = await self._get_game_version() from_version = await self._get_game_version()
version_info = await self._get_game_resource() game = await self._get_game(pre_download=pre_download)
if self._version == version_info.game.latest.version: if self._version == game.latest.version:
raise ValueError("Game is already up to date.") 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: if diff_archive is None:
raise ValueError("Game diff archive is not available for this version, please reinstall.") 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) 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: if not from_version:
from_version = await self._get_game_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: if diff_archive is None:
raise ValueError("Voiceover diff archive is not available for this version, please reinstall.") 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) 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 """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 from_version is not specified, it will be taken from the game version.
""" """
if not from_version: if not from_version:
from_version = await self._get_game_version() from_version = await self._get_game_version()
game_resource = await self._get_game_resource() game = await self._get_game(pre_download=pre_download)
if not game_resource:
raise ValueError("Could not fetch game resource")
translated_lang = self.voiceover_lang_translate(lang) translated_lang = self.voiceover_lang_translate(lang)
for v in game_resource.game.diffs: for v in game.diffs:
if v.version != from_version: if v.version != from_version:
continue continue
for vo in v.voice_packs: for vo in v.voice_packs:
if vo.language == translated_lang: if vo.language == translated_lang:
return vo 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 """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 from_version is not specified, it will be taken from the game version.
""" """
if not from_version: if not from_version:
from_version = await self._get_game_version() from_version = await self._get_game_version()
game_resource = await self._get_game_resource() game = await self._get_game(pre_download=pre_download)
for v in game_resource.game.diffs: for v in game.diffs:
if v.version == from_version: if v.version == from_version:
return v return v