Compare commits

..

No commits in common. "5b916025f37f8351ea5f3956b64fc1b48bfabcc7" and "891cf71aa793d7378bd3d15cab547e6ef4aa5714" have entirely different histories.

5 changed files with 26 additions and 218 deletions

View File

@ -3,33 +3,26 @@ from vollerei import __version__
from vollerei.cli.hsr import HSR from vollerei.cli.hsr import HSR
from vollerei.hsr import PatchType from vollerei.hsr import PatchType
from vollerei.cli import utils from vollerei.cli import utils
from vollerei.cli.utils import msg
class CLI: class CLI:
def __init__( def __init__(
self, self, game_path: str = None, patch_type=None, noconfirm: bool = False
game_path: str = None,
patch_type=None,
noconfirm: bool = False,
silent: bool = False,
) -> None: ) -> None:
""" """
Vollerei CLI Vollerei CLI
""" """
utils.silent_message = silent print(f"Vollerei v{__version__}")
msg(f"Vollerei v{__version__}")
if noconfirm: if noconfirm:
msg("User requested to automatically answer yes to all questions.") print("User requested to automatically answer yes to all questions.")
utils.no_confirm = noconfirm utils.no_confirm = noconfirm
if not game_path: if not game_path:
game_path = Path.cwd() game_path = Path.cwd()
game_path = Path(game_path) game_path = Path(game_path)
hsr_patch_type = patch_type
if patch_type is None: if patch_type is None:
hsr_patch_type = PatchType.Jadeite patch_type = PatchType.Jadeite
elif isinstance(patch_type, str): elif isinstance(patch_type, str):
hsr_patch_type = PatchType[patch_type] patch_type = PatchType[patch_type]
elif isinstance(patch_type, int): elif isinstance(patch_type, int):
hsr_patch_type = PatchType(patch_type) patch_type = PatchType(patch_type)
self.hsr = HSR(game_path=game_path, patch_type=hsr_patch_type) self.hsr = HSR(game_path=game_path, patch_type=patch_type)

View File

@ -1,6 +1,6 @@
from traceback import print_exc from traceback import print_exc
from platform import system from platform import system
from vollerei.cli.utils import ask, msg from vollerei.cli.utils import ask
from vollerei.hsr import Game, Patcher from vollerei.hsr import Game, Patcher
from vollerei.exceptions.patcher import PatcherError, PatchUpdateError from vollerei.exceptions.patcher import PatcherError, PatchUpdateError
from vollerei.hsr.patcher import PatchType from vollerei.hsr.patcher import PatchType
@ -11,8 +11,8 @@ class HSR:
self, game_path=None, patch_type: PatchType = PatchType.Jadeite self, game_path=None, patch_type: PatchType = PatchType.Jadeite
) -> None: ) -> None:
self._game = Game(game_path) self._game = Game(game_path)
msg("Game directory:", self._game.path) print("Game directory:", self._game.path)
msg("Game version:", self._game.get_version_str()) print("Game version:", self._game.get_version_str())
self._patcher = Patcher() self._patcher = Patcher()
self._patcher.patch_type = patch_type self._patcher.patch_type = patch_type
@ -115,6 +115,3 @@ class HSR:
self.__patch_jadeite() self.__patch_jadeite()
case PatchType.Astra: case PatchType.Astra:
self.__patch_astra() self.__patch_astra()
def get_version(self):
print(self._game.get_version_str())

View File

@ -1,5 +1,4 @@
no_confirm = False no_confirm = False
silent_message = False
def ask(question: str): def ask(question: str):
@ -13,12 +12,3 @@ def ask(question: str):
# Pacman way, treat all other answers as no # Pacman way, treat all other answers as no
else: else:
return False return False
def msg(*args, **kwargs):
"""
Print but silentable
"""
if silent_message:
return
print(*args, **kwargs)

View File

@ -4,35 +4,11 @@ Class wrapper for API endpoint /resource
class Segment: class Segment:
"""
A segment of the game archive.
Attributes:
path (str): Segment download path.
md5 (str): Segment md5 checksum.
package_size (int | None): Segment package size.
"""
path: str path: str
md5: str md5: str
# str -> int and checked if int is 0 then None # str -> int and checked if int is 0 then None
package_size: int | None package_size: int | None
def __init__(self, path: str, md5: str, package_size: int | None) -> None:
self.path = path
self.md5 = md5
self.package_size = package_size
@staticmethod
def from_dict(data: dict) -> "Segment":
return Segment(
data["path"],
data["md5"],
int(data["package_size"])
if data["package_size"] and data["package_size"] != "0"
else None,
)
class VoicePack: class VoicePack:
""" """
@ -41,12 +17,7 @@ class VoicePack:
`name` maybe converted from `path` if the server returns empty string. `name` maybe converted from `path` if the server returns empty string.
Attributes: Attributes:
language (str): Language of the voice pack. TODO
name (str): Voice pack archive name.
path (str): Voice pack download path.
size (int): Voice pack size.
md5 (str): Voice pack md5 checksum.
package_size (int): Voice pack package size.
""" """
language: str language: str
@ -58,33 +29,6 @@ class VoicePack:
# str -> int # str -> int
package_size: int package_size: int
def __init__(
self,
language: str,
name: str,
path: str,
size: int,
md5: str,
package_size: int,
) -> None:
self.language = language
self.name = name
self.path = path
self.size = size
self.md5 = md5
self.package_size = package_size
@staticmethod
def from_dict(data: dict) -> "VoicePack":
return VoicePack(
data["language"],
data["name"],
data["path"],
int(data["size"]),
data["md5"],
int(data["package_size"]),
)
class Diff: class Diff:
""" """
@ -105,39 +49,6 @@ class Diff:
# str -> int # str -> int
package_size: int package_size: int
def __init__(
self,
name: str,
version: str,
path: str,
size: int,
md5: str,
is_recommended_update: bool,
voice_packs: list[VoicePack],
package_size: int,
) -> None:
self.name = name
self.version = version
self.path = path
self.size = size
self.md5 = md5
self.is_recommended_update = is_recommended_update
self.voice_packs = voice_packs
self.package_size = package_size
@staticmethod
def from_dict(data: dict) -> "Diff":
return Diff(
data["name"],
data["version"],
data["path"],
int(data["size"]),
data["md5"],
data["is_recommended_update"],
[VoicePack.from_dict(i) for i in data["voice_packs"]],
int(data["package_size"]),
)
class Latest: class Latest:
""" """
@ -147,35 +58,13 @@ class Latest:
and if `path` is empty too then it'll convert the name from the first and if `path` is empty too then it'll convert the name from the first
segment of `segments` list. segment of `segments` list.
`path` maybe None if the server returns empty string, in that case
you'll have to download the game using `segments` list and merge them.
`voice_packs` will be empty for Star Rail, they force you to download
in-game instead.
`decompressed_path` is useful for repairing game files by only having
to re-download the corrupted files.
`segments` is a list of game archive segments, you'll have to download
them and merge them together to get the full game archive. Not available
on Star Rail.
Attributes: Attributes:
name (str): Game archive name. TODO
version (str): Game version in the archive.
path (str | None): Game archive download path.
size (int): Game archive size in bytes.
md5 (str): Game archive MD5 checksum.
entry (str): Game entry file (e.g. GenshinImpact.exe).
voice_packs (list[VoicePack]): Game voice packs.
decompressed_path (str | None): Game archive decompressed path.
segments (list[Segment]): Game archive segments.
package_size (int): Game archive package size in bytes.
""" """
name: str name: str
version: str version: str
path: str | None path: str
# str -> int # str -> int
size: int size: int
md5: str md5: str
@ -221,14 +110,14 @@ class Latest:
return Latest( return Latest(
data["name"], data["name"],
data["version"], data["version"],
data["path"] if data["path"] != "" else None, data["path"],
int(data["size"]), data["size"],
data["md5"], data["md5"],
data["entry"], data["entry"],
[VoicePack.from_dict(i) for i in data["voice_packs"]], [VoicePack.from_dict(i) for i in data["voice_packs"]],
data["decompressed_path"] if data["decompressed_path"] != "" else None, data["decompressed_path"],
[Segment.from_dict(i) for i in data["segments"]], [Segment.from_dict(i) for i in data["segments"]],
int(data["package_size"]), data["package_size"],
) )
@ -260,96 +149,27 @@ class Plugin:
# str -> int # str -> int
package_size: int package_size: int
def __init__(
self,
name: str,
version: str | None,
path: str,
size: int,
md5: str,
entry: str | None,
package_size: int,
) -> None:
self.name = name
self.version = version
self.path = path
self.size = size
self.md5 = md5
self.entry = entry
self.package_size = package_size
@staticmethod
def from_dict(data: dict) -> "Plugin":
return Plugin(
data["name"],
data["version"] if data["version"] != "" else None,
data["path"],
int(data["size"]),
data["md5"],
data["entry"] if data["entry"] != "" else None,
int(data["package_size"]),
)
class LauncherPlugin: class LauncherPlugin:
plugins: list[Plugin] plugins: list[Plugin]
# str -> int # str -> int
version: int version: int
def __init__(self, plugins: list[Plugin], version: int) -> None:
self.plugins = plugins
self.version = version
@staticmethod
def from_dict(data: dict) -> "LauncherPlugin":
return LauncherPlugin(
[Plugin.from_dict(i) for i in data["plugins"]], int(data["version"])
)
class DeprecatedPackage: class DeprecatedPackage:
name: str name: str
md5: str md5: str
def __init__(self, name: str, md5: str) -> None:
self.name = name
self.md5 = md5
@staticmethod
def from_dict(data: dict) -> "DeprecatedPackage":
return DeprecatedPackage(data["name"], data["md5"])
class DeprecatedFile: class DeprecatedFile:
path: str path: str
# str but checked for empty string # str but checked for empty string
md5: str | None md5: str | None
def __init__(self, path: str, md5: str | None) -> None:
self.path = path
self.md5 = md5
@staticmethod
def from_dict(data: dict) -> "DeprecatedFile":
return DeprecatedFile(data["path"], data["md5"] if data["md5"] != "" else None)
class Resource: class Resource:
""" """
Data class for /resource endpoint Data class for /resource endpoint
I'm still unclear about `force_update` and `sdk` attributes, so I'll
leave them as None for now.
Attributes:
game (Game): Game resource information.
plugin (LauncherPlugin): Launcher plugin information.
web_url (str): Game official launcher web URL.
force_update (None): Not used by official launcher I guess?
pre_download_game (Game | None): Pre-download game resource information.
deprecated_packages (list[DeprecatedPackage]): Deprecated game packages.
sdk (None): Maybe for Bilibili version of Genshin?
deprecated_files (list[DeprecatedFile]): Deprecated game files.
""" """
# I'm generous enough to convert the string into int # I'm generous enough to convert the string into int
@ -378,6 +198,14 @@ class Resource:
sdk: None, sdk: None,
deprecated_files: list[DeprecatedFile], deprecated_files: list[DeprecatedFile],
) -> None: ) -> None:
# Fixups
game_latest_path = game.latest.path
if game.latest.name == "":
print("A")
if game_latest_path == "":
game.latest.name = game.latest.segments[0].path.split("/")[-1]
else:
game.latest.name = game_latest_path.split("/")[-1]
self.game = game self.game = game
self.plugin = plugin self.plugin = plugin
self.web_url = web_url self.web_url = web_url

View File

@ -10,7 +10,7 @@ def get_resource(channel: GameChannel = GameChannel.Overseas) -> Resource:
Get game resource information from the launcher API. Get game resource information from the launcher API.
Args: Args:
channel: Game channel to get the resource information from. channel: Game channel to get the resource information from. (os, cn)
Returns: Returns:
Resource: Game resource information. Resource: Game resource information.