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,
]