All the library (included natives) process are test finished.

This commit is contained in:
2026-07-12 22:43:34 +08:00
parent e42a9b0a50
commit 864ce33db1
8 changed files with 1094 additions and 469 deletions
+50 -29
View File
@@ -10,7 +10,7 @@ from typing import List
import requests
from .exception import HashMismatchException
from .exception import HashMismatchException, DownloadException
DEFAULT_HASH_CHUNK_SIZE = 4096
DEFAULT_DOWNLOAD_CHUNK_SIZE = 4096
@@ -149,30 +149,35 @@ def download_file(file: FileObject, overwrite=True, progress_callback: Callable[
file.path.parent.mkdir(parents=True, exist_ok=True)
if file.exists and not overwrite:
raise Exception("File {} already exists. Use overwrite=True to overwrite.".format(file))
raise FileExistsError("File {} already exists. Use overwrite=True to overwrite.".format(file))
if file.url is None:
raise Exception("Target file {} URL cannot be None.".format(file))
raise ValueError("Target file {} URL cannot be None.".format(file))
logger.debug("Downloading file {} to {}".format(file.url, file))
with requests.get(file.url, stream=True) as r:
current_chunk = 0
total_bytes = r.headers.get("content-length", None)
logger.debug("URL: {}".format(file.url))
logger.debug("Response code: {}".format(r.status_code))
try:
with requests.get(file.url, stream=True) as r:
current_chunk = 0
total_bytes = r.headers.get("content-length", None)
logger.debug("URL: {}".format(file.url))
logger.debug("Response code: {}".format(r.status_code))
with file.path.open("wb") as f:
for chunk in r.iter_content(chunk_size=DEFAULT_DOWNLOAD_CHUNK_SIZE):
if not chunk:
break
with file.path.open("wb") as f:
for chunk in r.iter_content(chunk_size=DEFAULT_DOWNLOAD_CHUNK_SIZE):
if not chunk:
break
f.write(chunk)
current_chunk += 1
f.write(chunk)
current_chunk += 1
if callable(progress_callback) and total_bytes is not None:
current_progress = ((current_chunk * DEFAULT_DOWNLOAD_CHUNK_SIZE) / int(total_bytes) ) * 100
progress_callback(file.name, current_progress)
if callable(progress_callback) and total_bytes is not None:
current_progress = ((current_chunk * DEFAULT_DOWNLOAD_CHUNK_SIZE) / int(total_bytes)) * 100
progress_callback(file.name, current_progress)
except Exception as e:
raise DownloadException(
"An error occurred while downloading file {}. {}".format(file.url, e)
)
if file.expected_sha256 and not file.verify_sha256():
raise HashMismatchException("sha256", file.sha256, file.expected_sha256, file.posix_path)
@@ -190,17 +195,20 @@ def download_file(file: FileObject, overwrite=True, progress_callback: Callable[
def download_multiple_files(files: List[FileObject], download_callback: Callable[str]=None,
max_workers=THREADED_DOWNLOAD_MAX_WORKERS, progress_callback: Callable[str]=None) \
-> List[FileObject]:
-> tuple[list[FileObject], list[FileObject]]:
"""
Download multiple files
:param download_callback:
:param files:
:param max_workers:
:param progress_callback:
:return: List of file objects that download failed.
:return:
fails: List of file objects that download failed.
successes: List of file objects that download succeeded.
"""
fails = []
successes = []
files_count = 0
with ThreadPoolExecutor(max_workers=max_workers) as executor:
@@ -212,6 +220,7 @@ def download_multiple_files(files: List[FileObject], download_callback: Callable
try:
files_count += 1
future.result()
successes.append(file)
if callable(download_callback):
download_callback(file.posix_path)
@@ -222,21 +231,33 @@ def download_multiple_files(files: List[FileObject], download_callback: Callable
fails.append(file)
return fails
return fails, successes
def unzip(file: FileObject, destination: Path, target_extract_dir: Path | None=None, exclude: list[str] | None = None):
with zipfile.ZipFile(file.posix_path, "r") as archive:
def unzip(filepath: Path, destination: Path, exclude: list[str] | None = None, flatten: bool=False):
exclude = exclude or []
with zipfile.ZipFile(filepath.as_posix(), "r") as archive:
for member in archive.namelist():
# Ignore exclude file.
if any(member.startswith(pattern) for pattern in exclude):
continue
if flatten:
member_path = Path(member)
if member.endswith("/") or not member_path.name:
continue
if target_extract_dir is None and exclude is None:
archive.extractall(path=destination)
else:
for member in archive.namelist():
# Ignore exclude file.
if any(member.startswith(pattern) for pattern in exclude):
continue
if member.startswith(target_extract_dir.as_posix()):
archive.extract(member, destination)
target = destination / member_path.name
with archive.open(member) as source:
with target.open("wb") as output:
output.write(source.read())
else:
archive.extract(member, path=destination)
def get_platform_info():
"""
+44 -4
View File
@@ -2,14 +2,54 @@ from pathlib import Path
class LauncherException(Exception):
def __init__(self, message):
self.message = message
user_message = "Launcher error occurred."
def __str__(self):
return self.message
def __init__(self, message=None, *, user_message=None):
super().__init__(message or user_message or self.user_message)
if user_message is not None:
self.user_message = user_message
class HashMismatchException(Exception):
def __init__(self, hash_name, got_hash, expected_hash, filepath: str | Path):
if type(filepath) == Path: filepath = Path(filepath).absolute().as_posix()
self.message = "File {} hash mismatch. Expected hash {}. Got hash {} ({})".format(filepath, got_hash,
expected_hash, hash_name)
super().__init__(self.message)
class UnsupportedPlatformException(LauncherException):
user_message = "Current platform or architecture is not supported."
class VersionNotSelectedException(LauncherException):
user_message = "Select a version before continuing."
class VersionManifestFetchException(LauncherException):
user_message = "Unable to fetch version manifest."
class VersionManifestSaveException(LauncherException):
user_message = "Unable to save version manifest."
class NoVersionFoundException(LauncherException):
user_message = "No version found. (This should never happen)"
class NoSpecifiedVersionKeyException(LauncherException):
user_message = "Specified version or type not found."
class VersionDataKeyNotFoundException(LauncherException):
user_message = "Specified version data section (key) not found."
class VersionManifestException(LauncherException):
user_message = "Unable to load version manifest."
class DependencyException(LauncherException):
user_message = "Unable to find dependency."
class NativeResolveException(LauncherException):
user_message = "Unable to resolve native libraries."
class DownloadException(LauncherException):
user_message = "Some files failed to download."
class ExtractException(LauncherException):
user_message = "Unable to extract native libraries."