diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7dbdfee --- /dev/null +++ b/.gitignore @@ -0,0 +1,171 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# PyPI configuration file +.pypirc \ No newline at end of file diff --git a/__pycache__/Ui_install.cpython-310.pyc b/__pycache__/Ui_install.cpython-310.pyc deleted file mode 100644 index c92eb95..0000000 Binary files a/__pycache__/Ui_install.cpython-310.pyc and /dev/null differ diff --git a/__pycache__/animations.cpython-310.pyc b/__pycache__/animations.cpython-310.pyc deleted file mode 100644 index 7987610..0000000 Binary files a/__pycache__/animations.cpython-310.pyc and /dev/null differ diff --git a/__pycache__/core.cpython-310.pyc b/__pycache__/core.cpython-310.pyc deleted file mode 100644 index 11d6b45..0000000 Binary files a/__pycache__/core.cpython-310.pyc and /dev/null differ diff --git a/__pycache__/main_window.cpython-310.pyc b/__pycache__/main_window.cpython-310.pyc deleted file mode 100644 index eb99d0b..0000000 Binary files a/__pycache__/main_window.cpython-310.pyc and /dev/null differ diff --git a/core.py b/core.py deleted file mode 100644 index 96d8881..0000000 --- a/core.py +++ /dev/null @@ -1,254 +0,0 @@ -import os -import shutil -import requests -import py7zr -import hashlib -import psutil -import ctypes -import base64 -from PySide6.QtWidgets import (QMessageBox, QFileDialog, QProgressDialog) -from PySide6.QtCore import QThread, Signal - -# 配置信息(完全保持不变) -app_data = { - "APP_VERSION": "4.10.0.17496", - "APP_NAME": "@FRAISEMOE Addons Installer", - "TEMP": "TEMP", - "CACHE": "FRAISEMOE", - "PLUGIN": "PLUGIN", - "CONFIG_URL": "aHR0cHM6Ly9hcmNoaXZlLm92b2Zpc2guY29tL2FwaS93aWRnZXQvbmVrb3BhcmEvZG93bmxvYWRfdXJsLmpzb24=", - "UA": "TW96aWxsYS81LjAgKExpbnV4IGRlYmlhbjEyIEZyYWlzZU1vZS1BY2NlcHQpIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvMTE0LjA=", - "game_info": { - "NEKOPARA Vol.1": { - "exe": "nekopara_vol1.exe", - "hash": "04b48b231a7f34431431e5027fcc7b27affaa951b8169c541709156acf754f3e", - "install_path": "NEKOPARA Vol. 1/adultsonly.xp3", - "plugin_path": "vol.1/adultsonly.xp3", - }, - "NEKOPARA Vol.2": { - "exe": "nekopara_vol2.exe", - "hash": "b9c00a2b113a1e768bf78400e4f9075ceb7b35349cdeca09be62eb014f0d4b42", - "install_path": "NEKOPARA Vol. 2/adultsonly.xp3", - "plugin_path": "vol.2/adultsonly.xp3", - }, - "NEKOPARA Vol.3": { - "exe": "NEKOPARAvol3.exe", - "hash": "2ce7b223c84592e1ebc3b72079dee1e5e8d064ade15723328a64dee58833b9d5", - "install_path": "NEKOPARA Vol. 3/update00.int", - "plugin_path": "vol.3/update00.int", - }, - "NEKOPARA Vol.4": { - "exe": "nekopara_vol4.exe", - "hash": "4a4a9ae5a75a18aacbe3ab0774d7f93f99c046afe3a777ee0363e8932b90f36a", - "install_path": "NEKOPARA Vol. 4/vol4adult.xp3", - "plugin_path": "vol.4/vol4adult.xp3", - }, - }, -} - -def decode_base64(encoded_str): - """Base64解码函数""" - return base64.b64decode(encoded_str).decode("utf-8") - -class DownloadThread(QThread): - progress_updated = Signal(int) - download_finished = Signal(bool, str) - - def __init__(self, url, save_path, headers): - super().__init__() - self.url = url - self.save_path = save_path - self.headers = headers - - def run(self): - try: - response = requests.get(self.url, headers=self.headers, stream=True) - response.raise_for_status() - - total_size = int(response.headers.get('content-length', 0)) - downloaded = 0 - - with open(self.save_path, 'wb') as f: - for chunk in response.iter_content(chunk_size=8192): - if chunk: - f.write(chunk) - downloaded += len(chunk) - progress = int((downloaded / total_size) * 100) - self.progress_updated.emit(progress) - - self.download_finished.emit(True, "") - except Exception as e: - self.download_finished.emit(False, str(e)) - -class AppCore: - def __init__(self, ui): - self.ui = ui - self.install_dir = "" - self.temp_dir = os.path.join(os.getenv(app_data["TEMP"]), app_data["CACHE"]) - self.plugin_dir = os.path.join(self.temp_dir, app_data["PLUGIN"]) - self._create_dirs() # 确保这个方法被正确定义 - - # 解码配置 - self.CONFIG_URL = decode_base64(app_data["CONFIG_URL"]) - self.UA = decode_base64(app_data["UA"]) + f" FraiseMoe/{app_data['APP_VERSION']}" - self.GAME_INFO = app_data["game_info"] - self.current_download_thread = None - - def _create_dirs(self): - """创建必要的目录""" - os.makedirs(self.plugin_dir, exist_ok=True) - - def start_installation(self): - """开始安装流程""" - if not self._select_install_dir(): - return - - if not self._check_processes(): - return - - self._download_and_install() - - def _select_install_dir(self): - """选择安装目录""" - dir_path = QFileDialog.getExistingDirectory( - self.ui, "选择游戏安装目录" - ) - if not dir_path: - QMessageBox.warning(self.ui, "警告", "必须选择安装目录") - return False - self.install_dir = dir_path - return True - - def _check_processes(self): - """检查游戏进程""" - for proc in psutil.process_iter(['name']): - if proc.info['name'] in [info['exe'] for info in self.GAME_INFO.values()]: - reply = QMessageBox.question( - self.ui, "警告", - "检测到游戏正在运行,需要关闭才能继续安装", - QMessageBox.Yes | QMessageBox.No - ) - if reply == QMessageBox.Yes: - try: - proc.kill() - except: - QMessageBox.critical( - self.ui, "错误", - "无法关闭游戏进程,请手动关闭" - ) - return False - else: - return False - return True - - def _get_download_config(self): - """获取下载配置""" - try: - headers = {"User-Agent": self.UA} - response = requests.get(self.CONFIG_URL, headers=headers, timeout=10) - response.raise_for_status() - return response.json() - except Exception as e: - QMessageBox.critical( - self.ui, "错误", - f"获取下载配置失败: {str(e)}" - ) - return None - - def _download_and_install(self): - """下载并安装文件""" - config = self._get_download_config() - if not config: - return - - # 创建进度对话框 - progress = QProgressDialog("下载补丁文件中...", "取消", 0, 100, self.ui) - progress.setWindowTitle("安装进度") - progress.setAutoClose(True) - - # 为每个游戏版本下载补丁 - for game_name, game_info in self.GAME_INFO.items(): - vol_key = f"vol.{game_name.split()[-1]}.data" - if vol_key not in config: - continue - - download_url = config[vol_key]["url"] - temp_file = os.path.join(self.plugin_dir, f"{vol_key}.7z") - - # 启动下载线程 - self.current_download_thread = DownloadThread( - download_url, - temp_file, - {"User-Agent": self.UA} - ) - self.current_download_thread.progress_updated.connect(progress.setValue) - self.current_download_thread.download_finished.connect( - lambda success, err, f=temp_file, g=game_name, i=game_info: - self._handle_download_result(success, err, f, g, i) - ) - self.current_download_thread.start() - - progress.exec_() - - def _handle_download_result(self, success, error, temp_file, game_name, game_info): - """处理下载结果""" - if not success: - QMessageBox.critical(self.ui, "下载失败", f"错误: {error}") - return - - try: - # 解压文件 - with py7zr.SevenZipFile(temp_file, mode='r') as archive: - archive.extractall(path=self.plugin_dir) - - # 复制到游戏目录 - plugin_path = os.path.join(self.plugin_dir, game_info["plugin_path"]) - install_path = os.path.join(self.install_dir, game_info["install_path"]) - - os.makedirs(os.path.dirname(install_path), exist_ok=True) - shutil.copy(plugin_path, install_path) - - # 验证哈希 - if self._verify_hash(install_path, game_info["hash"]): - QMessageBox.information( - self.ui, "安装完成", - f"{game_name} 补丁已成功安装!" - ) - else: - QMessageBox.warning( - self.ui, "警告", - f"{game_name} 文件校验失败,安装可能不完整" - ) - except Exception as e: - QMessageBox.critical( - self.ui, "安装失败", - f"安装过程中出错: {str(e)}" - ) - finally: - # 清理临时文件 - if os.path.exists(temp_file): - os.remove(temp_file) - - def _verify_hash(self, file_path, expected_hash): - """验证文件哈希""" - if not os.path.exists(file_path): - return False - - sha256_hash = hashlib.sha256() - with open(file_path, "rb") as f: - for byte_block in iter(lambda: f.read(8192), b""): - sha256_hash.update(byte_block) - return sha256_hash.hexdigest() == expected_hash - - def shutdown_app(self): - """关闭应用程序""" - reply = QMessageBox.question( - self.ui, "退出", - "确定要退出安装程序吗?", - QMessageBox.Yes | QMessageBox.No - ) - if reply == QMessageBox.Yes: - # 清理临时目录 - if os.path.exists(self.temp_dir): - shutil.rmtree(self.temp_dir) - self.ui.close() \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index 747785e..0000000 --- a/main.py +++ /dev/null @@ -1,12 +0,0 @@ -import sys -from PySide6.QtWidgets import QApplication -from main_window import MainWindow - -def main(): - app = QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec()) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/main_window.py b/main_window.py deleted file mode 100644 index 53825cc..0000000 --- a/main_window.py +++ /dev/null @@ -1,28 +0,0 @@ -from PySide6.QtWidgets import QMainWindow -from PySide6.QtCore import QTimer -from Ui_install import Ui_MainWindows -from animations import MultiStageAnimations - -class MainWindow(QMainWindow): - def __init__(self): - super().__init__() - - # 先初始化UI - self.ui = Ui_MainWindows() - self.ui.setupUi(self) - - # 然后初始化动画系统 - self.animator = MultiStageAnimations(self.ui) - - # 在窗口显示前设置初始状态 - self.animator.initialize() - - # 窗口显示后延迟100ms启动动画 - QTimer.singleShot(100, self.start_animations) - - def start_animations(self): - self.animator.start_animations() - - def closeEvent(self, event): - self.animator.clear_animations() - super().closeEvent(event) \ No newline at end of file diff --git a/IMG/After/voaf_ga01.jpg b/source/IMG/After/voaf_ga01.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/After/voaf_ga01.jpg rename to source/IMG/After/voaf_ga01.jpg diff --git a/IMG/After/voaf_ga02.jpg b/source/IMG/After/voaf_ga02.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/After/voaf_ga02.jpg rename to source/IMG/After/voaf_ga02.jpg diff --git a/IMG/BG/bg1.jpg b/source/IMG/BG/bg1.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/BG/bg1.jpg rename to source/IMG/BG/bg1.jpg diff --git a/IMG/BG/bg2.jpg b/source/IMG/BG/bg2.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/BG/bg2.jpg rename to source/IMG/BG/bg2.jpg diff --git a/IMG/BG/bg3.jpg b/source/IMG/BG/bg3.jpg similarity index 100% rename from IMG/BG/bg3.jpg rename to source/IMG/BG/bg3.jpg diff --git a/IMG/BG/bg4.jpg b/source/IMG/BG/bg4.jpg similarity index 100% rename from IMG/BG/bg4.jpg rename to source/IMG/BG/bg4.jpg diff --git a/IMG/BG/menubg.jpg b/source/IMG/BG/menubg.jpg similarity index 100% rename from IMG/BG/menubg.jpg rename to source/IMG/BG/menubg.jpg diff --git a/IMG/BTN/exit.bmp b/source/IMG/BTN/exit.bmp similarity index 100% rename from IMG/BTN/exit.bmp rename to source/IMG/BTN/exit.bmp diff --git a/IMG/BTN/start_install.bmp b/source/IMG/BTN/start_install.bmp similarity index 100% rename from IMG/BTN/start_install.bmp rename to source/IMG/BTN/start_install.bmp diff --git a/source/IMG/ICO/icon.ico b/source/IMG/ICO/icon.ico new file mode 100644 index 0000000..4bf517d Binary files /dev/null and b/source/IMG/ICO/icon.ico differ diff --git a/source/IMG/ICO/icon.png b/source/IMG/ICO/icon.png new file mode 100644 index 0000000..d5ca97d Binary files /dev/null and b/source/IMG/ICO/icon.png differ diff --git a/IMG/LOGO/gl_head_logo_jp.png b/source/IMG/LOGO/gl_head_logo_jp.png similarity index 100% rename from IMG/LOGO/gl_head_logo_jp.png rename to source/IMG/LOGO/gl_head_logo_jp.png diff --git a/IMG/LOGO/vo01_logo.png b/source/IMG/LOGO/vo01_logo.png old mode 100755 new mode 100644 similarity index 100% rename from IMG/LOGO/vo01_logo.png rename to source/IMG/LOGO/vo01_logo.png diff --git a/IMG/LOGO/vo02_logo.png b/source/IMG/LOGO/vo02_logo.png old mode 100755 new mode 100644 similarity index 100% rename from IMG/LOGO/vo02_logo.png rename to source/IMG/LOGO/vo02_logo.png diff --git a/IMG/LOGO/vo03_logo.png b/source/IMG/LOGO/vo03_logo.png old mode 100755 new mode 100644 similarity index 100% rename from IMG/LOGO/vo03_logo.png rename to source/IMG/LOGO/vo03_logo.png diff --git a/IMG/LOGO/vo04_logo.png b/source/IMG/LOGO/vo04_logo.png old mode 100755 new mode 100644 similarity index 100% rename from IMG/LOGO/vo04_logo.png rename to source/IMG/LOGO/vo04_logo.png diff --git a/IMG/LOGO/voaf_logo.png b/source/IMG/LOGO/voaf_logo.png old mode 100755 new mode 100644 similarity index 100% rename from IMG/LOGO/voaf_logo.png rename to source/IMG/LOGO/voaf_logo.png diff --git a/IMG/vol4/vo04_ga01.jpg b/source/IMG/vol4/vo04_ga01.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/vol4/vo04_ga01.jpg rename to source/IMG/vol4/vo04_ga01.jpg diff --git a/IMG/vol4/vo04_ga05.jpg b/source/IMG/vol4/vo04_ga05.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/vol4/vo04_ga05.jpg rename to source/IMG/vol4/vo04_ga05.jpg diff --git a/IMG/vol4/vo04_ga06.jpg b/source/IMG/vol4/vo04_ga06.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/vol4/vo04_ga06.jpg rename to source/IMG/vol4/vo04_ga06.jpg diff --git a/IMG/vol4/vo04_ga07.jpg b/source/IMG/vol4/vo04_ga07.jpg old mode 100755 new mode 100644 similarity index 100% rename from IMG/vol4/vo04_ga07.jpg rename to source/IMG/vol4/vo04_ga07.jpg diff --git a/Ui_install.py b/source/Ui_install.py similarity index 100% rename from Ui_install.py rename to source/Ui_install.py diff --git a/animations.py b/source/animations.py similarity index 97% rename from animations.py rename to source/animations.py index 81d7add..268d75a 100644 --- a/animations.py +++ b/source/animations.py @@ -12,10 +12,10 @@ class MultiStageAnimations: # 动画时序配置 self.animation_config = { "logo": { - "delay_after": 800 # Logo动画完成后等待300ms + "delay_after": 1800 # Logo动画完成后等待300ms }, "mainbg": { - "delay_after": 200 # 主背景淡入完成后等待200ms + "delay_after": 500 # 主背景淡入完成后等待200ms } } diff --git a/install.ui b/source/install.ui similarity index 100% rename from install.ui rename to source/install.ui diff --git a/source/main.py b/source/main.py new file mode 100644 index 0000000..1014c11 --- /dev/null +++ b/source/main.py @@ -0,0 +1,605 @@ +import os +import py7zr +import requests +import shutil +import hashlib +import sys +import base64 +import psutil +import ctypes +import concurrent.futures +from PySide6.QtGui import QIcon +from collections import deque + +from PySide6.QtCore import ( Qt, + Signal, QThread, QTimer) +from PySide6.QtGui import (QIcon, QPixmap, ) +from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QMessageBox, + QProgressBar, QVBoxLayout, QFileDialog, QDialog) + +from Ui_install import Ui_MainWindows +from animations import MultiStageAnimations +import sys +import os + + + +# 配置信息 +app_data = { + "APP_VERSION": "4.10.0.17496", + "APP_NAME": "@FRAISEMOE Addons Installer", + "TEMP": "TEMP", + "CACHE": "FRAISEMOE", + "PLUGIN": "PLUGIN", + "CONFIG_URL": "aHR0cHM6Ly9hcmNoaXZlLm92b2Zpc2guY29tL2FwaS93aWRnZXQvbmVrb3BhcmEvZG93bmxvYWRfdXJsLmpzb24=", + "UA": "TW96aWxsYS81LjAgKExpbnV4IGRlYmlhbjEyIEZyYWlzZU1vZS1BY2NlcHQpIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvMTE0LjA=", + "game_info": { + "NEKOPARA Vol.1": { + "exe": "nekopara_vol1.exe", + "hash": "04b48b231a7f34431431e5027fcc7b27affaa951b8169c541709156acf754f3e", + "install_path": "NEKOPARA Vol. 1/adultsonly.xp3", + "plugin_path": "vol.1/adultsonly.xp3", + }, + "NEKOPARA Vol.2": { + "exe": "nekopara_vol2.exe", + "hash": "b9c00a2b113a1e768bf78400e4f9075ceb7b35349cdeca09be62eb014f0d4b42", + "install_path": "NEKOPARA Vol. 2/adultsonly.xp3", + "plugin_path": "vol.2/adultsonly.xp3", + }, + "NEKOPARA Vol.3": { + "exe": "NEKOPARAvol3.exe", + "hash": "2ce7b223c84592e1ebc3b72079dee1e5e8d064ade15723328a64dee58833b9d5", + "install_path": "NEKOPARA Vol. 3/update00.int", + "plugin_path": "vol.3/update00.int", + }, + "NEKOPARA Vol.4": { + "exe": "nekopara_vol4.exe", + "hash": "4a4a9ae5a75a18aacbe3ab0774d7f93f99c046afe3a777ee0363e8932b90f36a", + "install_path": "NEKOPARA Vol. 4/vol4adult.xp3", + "plugin_path": "vol.4/vol4adult.xp3", + }, + }, +} + +# Base64解码 +def decode_base64(encoded_str): + return base64.b64decode(encoded_str).decode("utf-8") + +# 全局变量 +APP_VERSION = app_data["APP_VERSION"] +APP_NAME = app_data["APP_NAME"] +TEMP = os.getenv(app_data["TEMP"]) +CACHE = os.path.join(TEMP, app_data["CACHE"]) +PLUGIN = os.path.join(CACHE, app_data["PLUGIN"]) +CONFIG_URL = decode_base64(app_data["CONFIG_URL"]) +UA = decode_base64(app_data["UA"]) + f" FraiseMoe/{APP_VERSION}" +GAME_INFO = app_data["game_info"] +BLOCK_SIZE = 67108864 +HASH_SIZE = 134217728 +PLUGIN_HASH = {game: info["hash"] for game, info in GAME_INFO.items()} +PROCESS_INFO = {info["exe"]: game for game, info in GAME_INFO.items()} + +def msgbox_frame(title, text, buttons=QMessageBox.StandardButton.NoButton): + msg_box = QMessageBox() + msg_box.setWindowTitle(title) + + # 设置弹窗图标 + icon_path = "IMG/ICO/icon.png" + if os.path.exists(icon_path): + msg_box.setWindowIcon(QIcon(icon_path)) + pixmap = QPixmap(icon_path) + if not pixmap.isNull(): + msg_box.setIconPixmap(pixmap.scaled(64, 64, Qt.KeepAspectRatio)) + else: + msg_box.setIcon(QMessageBox.Information) + + msg_box.setText(text) + msg_box.setStandardButtons(buttons) + return msg_box + +# 哈希值计算类 +class HashManager: + def __init__(self, HASH_SIZE): + self.HASH_SIZE = HASH_SIZE + + def hash_calculate(self, file_path): + sha256_hash = hashlib.sha256() + with open(file_path, "rb") as f: + for byte_block in iter(lambda: f.read(self.HASH_SIZE), b""): + sha256_hash.update(byte_block) + return sha256_hash.hexdigest() + + def calculate_hashes_in_parallel(self, file_paths): + with concurrent.futures.ThreadPoolExecutor() as executor: + future_to_file = { + executor.submit(self.hash_calculate, path): path for path in file_paths + } + results = {} + for future in concurrent.futures.as_completed(future_to_file): + file_path = future_to_file[future] + try: + results[file_path] = future.result() + except Exception as e: + results[file_path] = None + msg_box = msgbox_frame( + f"错误 {APP_NAME}", + f"\n文件哈希值计算失败\n\n【错误信息】:{e}\n", + QMessageBox.StandardButton.Ok, + ) + msg_box.exec() + return results + + def hash_pop_window(self): + msg_box = msgbox_frame(f"通知 {APP_NAME}", "\n正在检验文件状态...\n") + msg_box.show() + QApplication.processEvents() + return msg_box + + def cfg_pre_hash_compare(self, install_path, game_version, plugin_hash, installed_status): + if not os.path.exists(install_path): + installed_status[game_version] = False + return + file_hash = self.hash_calculate(install_path) + if file_hash == plugin_hash[game_version]: + installed_status[game_version] = True + else: + reply = msgbox_frame( + f"文件校验 {APP_NAME}", + f"\n检测到 {game_version} 的文件哈希值不匹配,是否重新安装?\n", + QMessageBox.Yes | QMessageBox.No, + ).exec() + if reply == QMessageBox.Yes: + installed_status[game_version] = False + else: + installed_status[game_version] = True + + def cfg_after_hash_compare(self, install_paths, plugin_hash, installed_status): + passed = True + file_paths = [ + install_paths[game] for game in plugin_hash if installed_status.get(game) + ] + hash_results = self.calculate_hashes_in_parallel(file_paths) + + for game, hash_value in plugin_hash.items(): + if installed_status.get(game): + file_hash = hash_results.get(install_paths[game]) + if file_hash != hash_value: + msg_box = msgbox_frame( + f"文件校验 {APP_NAME}", + f"\n检测到 {game} 的文件哈希值不匹配\n", + QMessageBox.StandardButton.Ok, + ) + msg_box.exec() + installed_status[game] = False + passed = False + break + return passed + +# 管理员权限检查类 +class AdminPrivileges: + def __init__(self): + self.required_exes = [ + "nekopara_vol1.exe", + "nekopara_vol2.exe", + "NEKOPARAvol3.exe", + "nekopara_vol4.exe", + ] + + def is_admin(self): + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + return False + + def request_admin_privileges(self): + if not self.is_admin(): + msg_box = msgbox_frame( + f"权限检测 {APP_NAME}", + "\n需要管理员权限运行此程序\n", + QMessageBox.Yes | QMessageBox.No, + ) + reply = msg_box.exec() + if reply == QMessageBox.Yes: + try: + ctypes.windll.shell32.ShellExecuteW( + None, "runas", sys.executable, " ".join(sys.argv), None, 1 + ) + except Exception as e: + msg_box = msgbox_frame( + f"错误 {APP_NAME}", + f"\n请求管理员权限失败\n\n【错误信息】:{e}\n", + QMessageBox.StandardButton.Ok, + ) + msg_box.exec() + sys.exit(1) + else: + msg_box = msgbox_frame( + f"权限检测 {APP_NAME}", + "\n无法获取管理员权限,程序将退出\n", + QMessageBox.StandardButton.Ok, + ) + msg_box.exec() + sys.exit(1) + + def check_and_terminate_processes(self): + for proc in psutil.process_iter(["pid", "name"]): + if proc.info["name"] in self.required_exes: + msg_box = msgbox_frame( + f"进程检测 {APP_NAME}", + f"\n检测到游戏正在运行: {proc.info['name']} \n\n是否终止?\n", + QMessageBox.Yes | QMessageBox.No, + ) + reply = msg_box.exec() + if reply == QMessageBox.Yes: + try: + proc.terminate() + proc.wait(timeout=3) + except psutil.AccessDenied: + msg_box = msgbox_frame( + f"错误 {APP_NAME}", + f"\n无法关闭游戏: {proc.info['name']} \n\n请手动关闭后重启应用\n", + QMessageBox.StandardButton.Ok, + ) + msg_box.exec() + sys.exit(1) + else: + msg_box = msgbox_frame( + f"进程检测 {APP_NAME}", + f"\n未关闭的游戏: {proc.info['name']} \n\n请手动关闭后重启应用\n", + QMessageBox.StandardButton.Ok, + ) + msg_box.exec() + sys.exit(1) + +# 下载线程类 +class DownloadThread(QThread): + progress = Signal(int) + finished = Signal(bool, str) + + def __init__(self, url, _7z_path, parent=None): + super().__init__(parent) + self.url = url + self._7z_path = _7z_path + + def run(self): + try: + headers = {"User-Agent": UA} + r = requests.get(self.url, headers=headers, stream=True, timeout=10) + r.raise_for_status() + total_size = int(r.headers.get("content-length", 0)) + with open(self._7z_path, "wb") as f: + for chunk in r.iter_content(chunk_size=BLOCK_SIZE): + f.write(chunk) + self.progress.emit(f.tell() * 100 // total_size) + self.finished.emit(True, "") + except requests.exceptions.RequestException as e: + self.finished.emit(False, f"\n网络请求错误\n\n【错误信息】: {e}\n") + except Exception as e: + self.finished.emit(False, f"\n未知错误\n\n【错误信息】: {e}\n") + +# 下载进度窗口类 +class ProgressWindow(QDialog): + def __init__(self, parent=None): + super(ProgressWindow, self).__init__(parent) + self.setWindowTitle(f"下载进度 {APP_NAME}") + self.resize(400, 100) + self.progress_bar_max = 100 + self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) + self.setWindowFlags(self.windowFlags() & ~Qt.WindowSystemMenuHint) + + layout = QVBoxLayout() + self.progress_bar = QProgressBar() + self.progress_bar.setValue(0) + self.label = QLabel("\n正在下载...\n") + layout.addWidget(self.label) + layout.addWidget(self.progress_bar) + self.setLayout(layout) + + def setmaxvalue(self, value): + self.progress_bar_max = value + self.progress_bar.setMaximum(value) + + def setprogressbarval(self, value): + self.progress_bar.setValue(value) + if value == self.progress_bar_max: + QTimer.singleShot(2000, self.close) + +# 主窗口类 +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + + # 初始化UI (从Ui_install.py导入) + self.ui = Ui_MainWindows() + self.ui.setupUi(self) + + icon_path = "IMG/ICO/icon.png" + if os.path.exists(icon_path): + self.setWindowIcon(QIcon(icon_path)) + + # 设置窗口标题为APP_NAME加版本号 + self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}") + + # 初始化动画系统 (从animations.py导入) + self.animator = MultiStageAnimations(self.ui) + + # 初始化功能变量 + self.selected_folder = "" + self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)} + self.download_queue = deque() + self.current_download_thread = None + self.hash_manager = HashManager(BLOCK_SIZE) + + # 检查管理员权限和进程 + admin_privileges = AdminPrivileges() + admin_privileges.request_admin_privileges() + admin_privileges.check_and_terminate_processes() + + # 创建缓存目录 + if not os.path.exists(PLUGIN): + try: + os.makedirs(PLUGIN) + except OSError as e: + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + f"\n无法创建缓存位置\n\n使用管理员身份运行或检查文件读写权限\n\n【错误信息】:{e}\n", + ) + sys.exit(1) + + # 连接信号 (使用Ui_install.py中的组件名称) + self.ui.start_install_btn.clicked.connect(self.file_dialog) + self.ui.exit_btn.clicked.connect(self.shutdown_app) + + # 在窗口显示前设置初始状态 + self.animator.initialize() + + # 窗口显示后延迟100ms启动动画 + QTimer.singleShot(100, self.start_animations) + + def start_animations(self): + self.animator.start_animations() + + def get_install_paths(self): + return { + game: os.path.join(self.selected_folder, info["install_path"]) + for game, info in GAME_INFO.items() + } + + def file_dialog(self): + self.selected_folder = QFileDialog.getExistingDirectory( + self, f"选择游戏所在【上级目录】 {APP_NAME}" + ) + if not self.selected_folder: + QMessageBox.warning( + self, f"通知 {APP_NAME}", "\n未选择任何目录,请重新选择\n" + ) + return + self.download_action() + + def get_download_url(self) -> dict: + try: + headers = {"User-Agent": UA} + response = requests.get(CONFIG_URL, headers=headers, timeout=10) + response.raise_for_status() + config_data = response.json() + if not all(f"vol.{i+1}.data" in config_data for i in range(4)): + raise ValueError("配置文件数据异常") + return { + f"vol{i+1}": config_data[f"vol.{i+1}.data"]["url"] for i in range(4) + } + except requests.exceptions.RequestException as e: + status_code = e.response.status_code if e.response is not None else "未知" + try: + error_response = e.response.json() if e.response else {} + json_title = error_response.get("title", "无错误类型") + json_message = error_response.get("message", "无附加错误信息") + except (ValueError, AttributeError): + json_title = "配置文件异常,无法解析错误类型" + json_message = "配置文件异常,无法解析错误信息" + + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + f"\n下载配置获取失败\n\n【HTTP状态】:{status_code}\n【错误类型】:{json_title}\n【错误信息】:{json_message}\n", + ) + return {} + except ValueError as e: + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + f"\n配置文件格式异常\n\n【错误信息】:{e}\n", + ) + return {} + + def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path): + game_exe = { + game: os.path.join( + self.selected_folder, info["install_path"].split("/")[0], info["exe"] + ) + for game, info in GAME_INFO.items() + } + + if ( + game_version not in game_exe + or not os.path.exists(game_exe[game_version]) + or self.installed_status[game_version] + ): + self.installed_status[game_version] = False + self.show_result() + return + + progress_window = ProgressWindow(self) + progress_window.show() + + self.current_download_thread = DownloadThread(url, _7z_path, self) + self.current_download_thread.progress.connect(progress_window.setprogressbarval) + self.current_download_thread.finished.connect( + lambda success, error: self.install_setting( + success, + error, + progress_window, + game_folder, + game_version, + _7z_path, + plugin_path, + ) + ) + self.current_download_thread.start() + + def install_setting( + self, + success, + error, + progress_window, + game_folder, + game_version, + _7z_path, + plugin_path, + ): + progress_window.close() + if success: + try: + msg_box = self.hash_manager.hash_pop_window() + QApplication.processEvents() + with py7zr.SevenZipFile(_7z_path, mode="r") as archive: + archive.extractall(path=PLUGIN) + shutil.copy(plugin_path, game_folder) + self.installed_status[game_version] = True + QMessageBox.information( + self, f"通知 {APP_NAME}", f"\n{game_version} 补丁已安装\n" + ) + except (py7zr.Bad7zFile, FileNotFoundError, Exception) as e: + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + f"\n文件操作失败,请重试\n\n【错误信息】:{e}\n", + ) + finally: + msg_box.close() + else: + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + f"\n文件获取失败\n网络状态异常或服务器故障\n\n【错误信息】:{error}\n", + ) + self.next_download_task() + + def pre_hash_compare(self, install_path, game_version, plugin_hash): + msg_box = self.hash_manager.hash_pop_window() + self.hash_manager.cfg_pre_hash_compare( + install_path, game_version, plugin_hash, self.installed_status + ) + msg_box.close() + + def download_action(self): + install_paths = self.get_install_paths() + for game_version, install_path in install_paths.items(): + self.pre_hash_compare(install_path, game_version, PLUGIN_HASH) + + config = self.get_download_url() + if not config: + QMessageBox.critical( + self, f"错误 {APP_NAME}", "\n网络状态异常或服务器故障,请重试\n" + ) + return + + for i in range(1, 5): + game_version = f"NEKOPARA Vol.{i}" + if not self.installed_status[game_version]: + url = config[f"vol{i}"] + game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}") + _7z_path = os.path.join(PLUGIN, f"vol.{i}.7z") + plugin_path = os.path.join( + PLUGIN, GAME_INFO[game_version]["plugin_path"] + ) + self.download_queue.append( + (url, game_folder, game_version, _7z_path, plugin_path) + ) + + self.next_download_task() + + def next_download_task(self): + if not self.download_queue: + self.after_hash_compare(PLUGIN_HASH) + return + url, game_folder, game_version, _7z_path, plugin_path = self.download_queue.popleft() + self.download_setting(url, game_folder, game_version, _7z_path, plugin_path) + + def after_hash_compare(self, plugin_hash): + msg_box = self.hash_manager.hash_pop_window() + result = self.hash_manager.cfg_after_hash_compare( + self.get_install_paths(), plugin_hash, self.installed_status + ) + msg_box.close() + self.show_result() + return result + + def show_result(self): + installed_version = "\n".join( + [i for i in self.installed_status if self.installed_status[i]] + ) + failed_ver = "\n".join( + [i for i in self.installed_status if not self.installed_status[i]] + ) + QMessageBox.information( + self, + f"完成 {APP_NAME}", + f"\n安装结果:\n安装成功数:{len(installed_version.splitlines())} 安装失败数:{len(failed_ver.splitlines())}\n" + f"安装成功的版本:\n{installed_version}\n尚未持有或未使用本工具安装补丁的版本:\n{failed_ver}\n", + ) + + def closeEvent(self, event): + self.shutdown_app(event) + + def shutdown_app(self, event=None): + reply = QMessageBox.question( + self, + "退出程序", + "\n是否确定退出?\n", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No, + ) + + if reply == QMessageBox.Yes: + if ( + self.current_download_thread + and self.current_download_thread.isRunning() + ): + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + "\n当前有下载任务正在进行,完成后再试\n", + ) + if event: + event.ignore() + return + + if os.path.exists(PLUGIN): + for attempt in range(3): + try: + shutil.rmtree(PLUGIN) + break + except Exception as e: + if attempt == 2: + QMessageBox.critical( + self, + f"错误 {APP_NAME}", + f"\n清理缓存失败\n\n【错误信息】:{e}\n", + ) + if event: + event.accept() + sys.exit(1) + if event: + event.accept() + else: + sys.exit(0) + else: + if event: + event.ignore() + +if __name__ == "__main__": + app = QApplication([]) + window = MainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/source/nuitka-crash-report.xml b/source/nuitka-crash-report.xml new file mode 100644 index 0000000..9815aa3 --- /dev/null +++ b/source/nuitka-crash-report.xml @@ -0,0 +1,8419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/pic_data.py b/source/pic_data.py new file mode 100644 index 0000000..1ef5386 --- /dev/null +++ b/source/pic_data.py @@ -0,0 +1,14 @@ + +img_data = { + "firstbg": "", + "vol1": "", + "vol2": "", + "vol3": "", + "vol4": "", + "after": "", + "defaultbg": "", + "menubg": "", + "start_install_btn": "", + "exit_btn": "", + "icon": "" +} \ No newline at end of file diff --git a/popup.ui b/source/popup.ui similarity index 100% rename from popup.ui rename to source/popup.ui diff --git a/source/requirements.txt b/source/requirements.txt new file mode 100644 index 0000000..156d493 --- /dev/null +++ b/source/requirements.txt @@ -0,0 +1,77 @@ +altgraph==0.17.4 +annotated-types==0.7.0 +anyio==4.9.0 +async-timeout==5.0.1 +auto-py-to-exe==2.46.0 +bottle==0.13.4 +bottle-websocket==0.2.9 +Brotli==1.1.0 +certifi==2025.6.15 +cffi==1.17.1 +charset-normalizer==3.4.2 +click==8.2.1 +colorama==0.4.6 +colorthief==0.2.1 +darkdetect==0.8.0 +Eel==0.18.2 +exceptiongroup==1.3.0 +fastapi==0.115.14 +future==1.0.0 +gevent==25.5.1 +gevent-websocket==0.10.1 +greenlet==3.2.3 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +importlib_resources==6.5.2 +inflate64==1.0.3 +multivolumefile==0.2.3 +Nuitka==2.7.11 +numpy==2.2.6 +ordered-set==4.1.0 +packaging==25.0 +pefile==2023.2.7 +pillow==11.3.0 +playwright==1.53.0 +psutil==7.0.0 +py7zr==1.0.0 +pybcj==1.0.6 +pycparser==2.22 +pycryptodomex==3.23.0 +pydantic==2.11.7 +pydantic_core==2.33.2 +pyee==13.0.0 +pyinstaller==6.14.1 +pyinstaller-hooks-contrib==2025.5 +pyparsing==3.2.3 +pyppmd==1.2.0 +PyQt-SiliconUI==1.0.1 +PyQt5==5.15.11 +PyQt5-Qt5==5.15.2 +PyQt5_sip==12.17.0 +PySide6==6.9.1 +PySide6-Fluent-Widgets==1.8.3 +PySide6_Addons==6.9.1 +PySide6_Essentials==6.9.1 +PySideSix-Frameless-Window==0.7.3 +python-dateutil==2.9.0.post0 +python-multipart==0.0.20 +pywin32==310 +pywin32-ctypes==0.2.3 +pyzstd==0.17.0 +redis==6.2.0 +requests==2.32.4 +scipy==1.15.3 +shiboken6==6.9.1 +six==1.17.0 +sniffio==1.3.1 +starlette==0.46.2 +texttable==1.7.0 +typing-inspection==0.4.1 +typing_extensions==4.14.0 +urllib3==2.5.0 +uvicorn==0.35.0 +zope.event==5.1 +zope.interface==7.2 +zstandard==0.23.0