diff --git a/README.md b/README.md index b0ad5f9..112f939 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ since I want to support other anime games and the launcher code is very messy, t - [x] Repair the game (Smarter than the official launcher!) - [x] Update the game - [x] Update voicepacks -- [ ] Uninstall the game (Just remove the game files) -- [ ] Voicepacks installation +- [ ] Uninstall the game (Just remove the game directory lol) +- [x] Voicepacks installation #### Advanced features - [x] Apply the update archives - [x] Download the update archives diff --git a/vollerei/cli/hsr.py b/vollerei/cli/hsr.py index ba852a5..d4609da 100644 --- a/vollerei/cli/hsr.py +++ b/vollerei/cli/hsr.py @@ -4,7 +4,7 @@ from cleo.helpers import option, argument from copy import deepcopy from pathlib import PurePath from platform import system -from vollerei.common.enums import GameChannel +from vollerei.common.enums import GameChannel, VoicePackLanguage from vollerei.cli import utils from vollerei.exceptions.game import GameError from vollerei.hsr import Game, Patcher @@ -129,9 +129,97 @@ class VoicepackList(Command): self.line(f"Available voicepacks: {', '.join(available_voicepacks_str)}") -class VoicepackUpdateAll(Command): - name = "hsr voicepack update-all" - description = "Updates all installed voicepacks" +class VoicepackInstall(Command): + name = "hsr voicepack install" + description = ( + "Installs the specified installed voicepacks" + ) + options = default_options + [ + option("pre-download", description="Pre-download the game if available"), + ] + arguments = [ + argument( + "language", description="Languages to install", multiple=True, optional=True + ) + ] + + def handle(self): + callback(command=self) + pre_download = self.option("pre-download") + # Typing manually because pylance detect it as Any + languages: list[str] = self.argument("language") + # Get installed voicepacks + language_objects = [] + for language in languages: + language = language.lower() + try: + language_objects.append(VoicePackLanguage[language.capitalize()]) + except KeyError: + try: + language_objects.append(VoicePackLanguage.from_remote_str(language)) + except ValueError: + self.line_error(f"Invalid language: {language}") + if len(language_objects) == 0: + self.line_error( + "No valid languages specified, you must specify a language to install" + ) + return + progress = utils.ProgressIndicator(self) + progress.start("Fetching install package information... ") + try: + game_info = State.game.get_remote_game(pre_download=pre_download) + except Exception as e: + progress.finish( + f"Fetching failed with following error: {e} \n{traceback.format_exc()}" + ) + return + progress.finish( + "Installation information fetched successfully." + ) + if not self.confirm("Do you want to install the specified voicepacks?"): + self.line("Installation aborted.") + return + # Voicepack update + for remote_voicepack in game_info.major.audio_pkgs: + if remote_voicepack.language not in language_objects: + continue + self.line( + f"Downloading install package for language: {remote_voicepack.language.name}... " + ) + archive_file = State.game.cache.joinpath(PurePath(remote_voicepack.url).name) + try: + download_result = utils.download( + remote_voicepack.url, archive_file, file_len=remote_voicepack.size + ) + except Exception as e: + self.line_error(f"Couldn't download package: {e}") + return + if not download_result: + self.line_error("Download failed.") + return + self.line("Download completed.") + progress = utils.ProgressIndicator(self) + progress.start("Installing package...") + try: + State.game.install_archive(archive_file) + except Exception as e: + progress.finish( + f"Couldn't apply package: {e} \n{traceback.format_exc()}" + ) + return + progress.finish( + f"Package applied for language {remote_voicepack.language.name}." + ) + self.line( + f"The voicepacks have been installed to version: {State.game.get_version_str()}" + ) + + +class VoicepackUpdate(Command): + name = "hsr voicepack update" + description = ( + "Updates the specified installed voicepacks, if not specified, updates all" + ) options = default_options + [ option( "auto-repair", "R", description="Automatically repair the game if needed" @@ -141,23 +229,44 @@ class VoicepackUpdateAll(Command): "from-version", description="Update from a specific version", flag=False ), ] + arguments = [ + argument( + "language", description="Languages to update", multiple=True, optional=True + ) + ] def handle(self): callback(command=self) auto_repair = self.option("auto-repair") pre_download = self.option("pre-download") from_version = self.option("from-version") + # Typing manually because pylance detect it as Any + languages: list[str] = self.argument("language") if auto_repair: self.line("Auto-repair is enabled.") if from_version: self.line(f"Updating from version: {from_version}") State.game.version_override = from_version # Get installed voicepacks + if len(languages) == 0: + self.line( + "No languages specified, updating all installed voicepacks..." + ) installed_voicepacks = State.game.get_installed_voicepacks() + if len(languages) > 0: + languages = [x.lower() for x in languages] + # Support both English and en-us and en + installed_voicepacks = [ + x + for x in installed_voicepacks + if x.name.lower() in languages + or x.value.lower() in languages + or x.name.lower()[:2] in languages + ] installed_voicepacks_str = [ f"{str(x.name)}" for x in installed_voicepacks ] - self.line(f"Installed voicepacks: {', '.join(installed_voicepacks_str)}") + self.line(f"Updating voicepacks: {', '.join(installed_voicepacks_str)}") progress = utils.ProgressIndicator(self) progress.start("Checking for updates... ") try: @@ -684,7 +793,7 @@ class InstallDownloadCommand(Command): self.line_error("Download failed.") return self.line("Download completed.") - + class UpdateDownloadCommand(Command): name = "hsr update download" @@ -838,7 +947,8 @@ commands = [ UpdatePatchCommand, UpdateCommand, UpdateDownloadCommand, + VoicepackInstall, VoicepackList, VoicepackListInstalled, - VoicepackUpdateAll, + VoicepackUpdate, ]