Compare commits
2 Commits
891cf71aa7
...
5b916025f3
Author | SHA1 | Date | |
---|---|---|---|
5b916025f3 | |||
69a53da8ae |
@ -3,26 +3,33 @@ 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, game_path: str = None, patch_type=None, noconfirm: bool = False
|
self,
|
||||||
|
game_path: str = None,
|
||||||
|
patch_type=None,
|
||||||
|
noconfirm: bool = False,
|
||||||
|
silent: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Vollerei CLI
|
Vollerei CLI
|
||||||
"""
|
"""
|
||||||
print(f"Vollerei v{__version__}")
|
utils.silent_message = silent
|
||||||
|
msg(f"Vollerei v{__version__}")
|
||||||
if noconfirm:
|
if noconfirm:
|
||||||
print("User requested to automatically answer yes to all questions.")
|
msg("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:
|
||||||
patch_type = PatchType.Jadeite
|
hsr_patch_type = PatchType.Jadeite
|
||||||
elif isinstance(patch_type, str):
|
elif isinstance(patch_type, str):
|
||||||
patch_type = PatchType[patch_type]
|
hsr_patch_type = PatchType[patch_type]
|
||||||
elif isinstance(patch_type, int):
|
elif isinstance(patch_type, int):
|
||||||
patch_type = PatchType(patch_type)
|
hsr_patch_type = PatchType(patch_type)
|
||||||
self.hsr = HSR(game_path=game_path, patch_type=patch_type)
|
self.hsr = HSR(game_path=game_path, patch_type=hsr_patch_type)
|
||||||
|
@ -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
|
from vollerei.cli.utils import ask, msg
|
||||||
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)
|
||||||
print("Game directory:", self._game.path)
|
msg("Game directory:", self._game.path)
|
||||||
print("Game version:", self._game.get_version_str())
|
msg("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,3 +115,6 @@ 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())
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
no_confirm = False
|
no_confirm = False
|
||||||
|
silent_message = False
|
||||||
|
|
||||||
|
|
||||||
def ask(question: str):
|
def ask(question: str):
|
||||||
@ -12,3 +13,12 @@ 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)
|
||||||
|
@ -4,11 +4,35 @@ 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:
|
||||||
"""
|
"""
|
||||||
@ -17,7 +41,12 @@ 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:
|
||||||
TODO
|
language (str): Language of the voice pack.
|
||||||
|
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
|
||||||
@ -29,6 +58,33 @@ 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:
|
||||||
"""
|
"""
|
||||||
@ -49,6 +105,39 @@ 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:
|
||||||
"""
|
"""
|
||||||
@ -58,13 +147,35 @@ 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:
|
||||||
TODO
|
name (str): Game archive name.
|
||||||
|
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
|
path: str | None
|
||||||
# str -> int
|
# str -> int
|
||||||
size: int
|
size: int
|
||||||
md5: str
|
md5: str
|
||||||
@ -110,14 +221,14 @@ class Latest:
|
|||||||
return Latest(
|
return Latest(
|
||||||
data["name"],
|
data["name"],
|
||||||
data["version"],
|
data["version"],
|
||||||
data["path"],
|
data["path"] if data["path"] != "" else None,
|
||||||
data["size"],
|
int(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"],
|
data["decompressed_path"] if data["decompressed_path"] != "" else None,
|
||||||
[Segment.from_dict(i) for i in data["segments"]],
|
[Segment.from_dict(i) for i in data["segments"]],
|
||||||
data["package_size"],
|
int(data["package_size"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -149,27 +260,96 @@ 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
|
||||||
@ -198,14 +378,6 @@ 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
|
||||||
|
@ -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. (os, cn)
|
channel: Game channel to get the resource information from.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Resource: Game resource information.
|
Resource: Game resource information.
|
||||||
|
Loading…
Reference in New Issue
Block a user