Support downloading game & voicepacks and install, bump to 1.1.0
In CLI too, also optimized code & added test for voiceover functions.
This commit is contained in:
@ -1,13 +1,14 @@
|
||||
import re
|
||||
import shutil
|
||||
|
||||
import aiohttp
|
||||
import appdirs
|
||||
import zipfile
|
||||
import warnings
|
||||
import json
|
||||
from pathlib import Path
|
||||
from configparser import ConfigParser
|
||||
|
||||
from aiopath import AsyncPath
|
||||
from worthless import constants
|
||||
from worthless.launcher import Launcher
|
||||
|
||||
@ -75,11 +76,12 @@ class Installer:
|
||||
self._gamedir = gamedir
|
||||
if not data_dir:
|
||||
self._appdirs = appdirs.AppDirs(constants.APP_NAME, constants.APP_AUTHOR)
|
||||
self._temp_path = Path(self._appdirs.user_cache_dir).joinpath("Installer")
|
||||
self.temp_path = Path(self._appdirs.user_cache_dir).joinpath("Installer")
|
||||
else:
|
||||
if not isinstance(data_dir, Path):
|
||||
data_dir = Path(data_dir)
|
||||
self._temp_path = data_dir.joinpath("Temp/Installer/")
|
||||
self.temp_path = data_dir.joinpath("Temp/Installer/")
|
||||
self.temp_path.mkdir(parents=True, exist_ok=True)
|
||||
config_file = self._gamedir.joinpath("config.ini")
|
||||
self._config_file = config_file.resolve()
|
||||
self._version = None
|
||||
@ -87,6 +89,33 @@ class Installer:
|
||||
self._launcher = Launcher(self._gamedir, overseas=self._overseas)
|
||||
self._version = self.get_game_version()
|
||||
|
||||
async def _download_file(self, file_url: str, file_name: str, file_len: int = None):
|
||||
"""
|
||||
Download file name to temporary directory,
|
||||
:param file_url:
|
||||
:param file_name:
|
||||
:return:
|
||||
"""
|
||||
params = {}
|
||||
file_path = AsyncPath(self.temp_path).joinpath(file_name)
|
||||
if file_path.exists():
|
||||
async with file_path.open("rb") as f:
|
||||
cur_len = len(await f.read())
|
||||
params |= {
|
||||
"Range": f"bytes={cur_len}-{file_len if file_len else ''}"
|
||||
}
|
||||
else:
|
||||
await file_path.touch()
|
||||
async with aiohttp.ClientSession() as session:
|
||||
rsp = await session.get(file_url, params=params, timeout=None)
|
||||
rsp.raise_for_status()
|
||||
while True:
|
||||
chunk = await rsp.content.read(8192)
|
||||
if not chunk:
|
||||
break
|
||||
async with file_path.open("ab") as f:
|
||||
await f.write(chunk)
|
||||
|
||||
def get_game_archive_version(self, game_archive: str | Path):
|
||||
if not game_archive.exists():
|
||||
raise FileNotFoundError(f"Game archive {game_archive} not found")
|
||||
@ -110,6 +139,7 @@ class Installer:
|
||||
return "zh-cn"
|
||||
case "Korean":
|
||||
return "ko-kr"
|
||||
return lang
|
||||
|
||||
@staticmethod
|
||||
def get_voiceover_archive_language(voiceover_archive: str | Path):
|
||||
@ -174,20 +204,53 @@ class Installer:
|
||||
|
||||
archive.extractall(self._gamedir, members=files)
|
||||
archive.close()
|
||||
# Update game version on local variable.
|
||||
self._version = self.get_game_version()
|
||||
|
||||
async def download_game_update(self):
|
||||
if self._version is None:
|
||||
raise ValueError("Game version not found, use install_game to install the game.")
|
||||
async def download_full_game(self):
|
||||
archive = await self._launcher.get_resource_info()
|
||||
if archive is None:
|
||||
raise RuntimeError("Failed to fetch game resource info.")
|
||||
if self._version == archive.game.latest.version:
|
||||
raise ValueError("Game is already up to date.")
|
||||
await self._download_file(archive.game.latest.path, archive.game.latest.name, archive.game.latest.size)
|
||||
|
||||
async def download_full_voiceover(self, language: str):
|
||||
archive = await self._launcher.get_resource_info()
|
||||
if archive is None:
|
||||
raise RuntimeError("Failed to fetch game resource info.")
|
||||
translated_lang = self.voiceover_lang_translate(language)
|
||||
for vo in archive.game.latest.voice_packs:
|
||||
if vo.language == translated_lang:
|
||||
await self._download_file(vo.path, vo.name, vo.size)
|
||||
|
||||
async def download_game_update(self, from_version: str = None):
|
||||
if not from_version:
|
||||
self._version = from_version
|
||||
if not from_version:
|
||||
raise ValueError("Game version not found")
|
||||
version_info = await self._launcher.get_resource_info()
|
||||
if version_info is None:
|
||||
raise RuntimeError("Failed to fetch game resource info.")
|
||||
if self._version == version_info.game.latest.version:
|
||||
raise ValueError("Game is already up to date.")
|
||||
diff_archive = self.get_game_diff_archive()
|
||||
diff_archive = await self.get_game_diff_archive(from_version)
|
||||
if diff_archive is None:
|
||||
raise ValueError("Game diff archive is not available for this version, please reinstall.")
|
||||
# TODO: Download the diff archive
|
||||
raise NotImplementedError("Downloading game diff archive is not implemented yet.")
|
||||
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):
|
||||
if not from_version:
|
||||
self._version = from_version
|
||||
if not from_version:
|
||||
raise ValueError("Game version not found, use install_game to install the game.")
|
||||
version_info = await self._launcher.get_resource_info()
|
||||
if version_info is None:
|
||||
raise RuntimeError("Failed to fetch game resource info.")
|
||||
diff_archive = await self.get_voiceover_diff_archive(language, from_version)
|
||||
if diff_archive is None:
|
||||
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)
|
||||
|
||||
def uninstall_game(self):
|
||||
shutil.rmtree(self._gamedir)
|
||||
@ -211,6 +274,30 @@ class Installer:
|
||||
archive.extractall(self._gamedir)
|
||||
archive.close()
|
||||
|
||||
async def get_voiceover_diff_archive(self, lang: str, from_version: str = None):
|
||||
"""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 not from_version:
|
||||
if self._version:
|
||||
from_version = self._version
|
||||
else:
|
||||
from_version = self._version = self.get_game_version()
|
||||
if not from_version:
|
||||
raise ValueError("No game version found")
|
||||
game_resource = await self._launcher.get_resource_info()
|
||||
if not game_resource:
|
||||
raise ValueError("Could not fetch game resource")
|
||||
translated_lang = self.voiceover_lang_translate(lang)
|
||||
for v in game_resource.game.diffs:
|
||||
if v.version != from_version:
|
||||
continue
|
||||
for vo in v.voice_packs:
|
||||
if vo.language != translated_lang:
|
||||
continue
|
||||
return vo
|
||||
|
||||
async def get_game_diff_archive(self, from_version: str = None):
|
||||
"""Gets a diff archive from `from_version` to the latest one
|
||||
|
||||
|
||||
Reference in New Issue
Block a user