mirror of
https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer.git
synced 2025-06-04 14:43:02 +00:00
Update source->V4.8.6.17218
This commit is contained in:
parent
c88b83e11d
commit
fb9ca25c3f
114
source/GUI.py
114
source/GUI.py
@ -8,15 +8,47 @@
|
|||||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
from PySide6.QtCore import (
|
||||||
QMetaObject, QObject, QPoint, QRect, QSize, QTime,
|
QCoreApplication,
|
||||||
QUrl, Qt)
|
QDate,
|
||||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
|
QDateTime,
|
||||||
QFontDatabase, QGradient, QIcon, QImage,
|
QLocale,
|
||||||
QKeySequence, QLinearGradient, QPainter, QPalette,
|
QMetaObject,
|
||||||
QPixmap, QRadialGradient, QTransform)
|
QObject,
|
||||||
from PySide6.QtWidgets import (QApplication, QLabel, QPushButton, QSizePolicy,
|
QPoint,
|
||||||
QSplitter, QVBoxLayout, QWidget)
|
QRect,
|
||||||
|
QSize,
|
||||||
|
QTime,
|
||||||
|
QUrl,
|
||||||
|
Qt,
|
||||||
|
)
|
||||||
|
from PySide6.QtGui import (
|
||||||
|
QBrush,
|
||||||
|
QColor,
|
||||||
|
QConicalGradient,
|
||||||
|
QCursor,
|
||||||
|
QFont,
|
||||||
|
QFontDatabase,
|
||||||
|
QGradient,
|
||||||
|
QIcon,
|
||||||
|
QImage,
|
||||||
|
QKeySequence,
|
||||||
|
QLinearGradient,
|
||||||
|
QPainter,
|
||||||
|
QPalette,
|
||||||
|
QPixmap,
|
||||||
|
QRadialGradient,
|
||||||
|
QTransform,
|
||||||
|
)
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QApplication,
|
||||||
|
QLabel,
|
||||||
|
QPushButton,
|
||||||
|
QSizePolicy,
|
||||||
|
QSplitter,
|
||||||
|
QVBoxLayout,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
from pic_data import img_data
|
from pic_data import img_data
|
||||||
from PySide6.QtCore import QByteArray
|
from PySide6.QtCore import QByteArray
|
||||||
import base64
|
import base64
|
||||||
@ -26,7 +58,7 @@ class Ui_mainwin(object):
|
|||||||
|
|
||||||
def setupUi(self, mainwin):
|
def setupUi(self, mainwin):
|
||||||
if not mainwin.objectName():
|
if not mainwin.objectName():
|
||||||
mainwin.setObjectName(u"mainwin")
|
mainwin.setObjectName("mainwin")
|
||||||
mainwin.setWindowModality(Qt.WindowModality.NonModal)
|
mainwin.setWindowModality(Qt.WindowModality.NonModal)
|
||||||
mainwin.resize(1280, 720)
|
mainwin.resize(1280, 720)
|
||||||
mainwin.setMinimumSize(QSize(1280, 720))
|
mainwin.setMinimumSize(QSize(1280, 720))
|
||||||
@ -35,99 +67,95 @@ class Ui_mainwin(object):
|
|||||||
pixmap = QPixmap()
|
pixmap = QPixmap()
|
||||||
|
|
||||||
icon = QIcon()
|
icon = QIcon()
|
||||||
pixmap.loadFromData(QByteArray(base64.b64decode(img_data['icon'])))
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["icon"])))
|
||||||
icon.addPixmap(pixmap)
|
icon.addPixmap(pixmap)
|
||||||
|
|
||||||
mainwin.setWindowIcon(icon)
|
mainwin.setWindowIcon(icon)
|
||||||
|
|
||||||
self.mainbg = QLabel(mainwin)
|
self.mainbg = QLabel(mainwin)
|
||||||
self.mainbg.setObjectName(u"mainbg")
|
self.mainbg.setObjectName("mainbg")
|
||||||
self.mainbg.setEnabled(True)
|
self.mainbg.setEnabled(True)
|
||||||
self.mainbg.setGeometry(QRect(0, 0, 1280, 720))
|
self.mainbg.setGeometry(QRect(0, 0, 1280, 720))
|
||||||
self.mainbg.setMinimumSize(QSize(1280, 720))
|
self.mainbg.setMinimumSize(QSize(1280, 720))
|
||||||
self.mainbg.setMaximumSize(QSize(1280, 720))
|
self.mainbg.setMaximumSize(QSize(1280, 720))
|
||||||
self.mainbg.setTextFormat(Qt.TextFormat.AutoText)
|
self.mainbg.setTextFormat(Qt.TextFormat.AutoText)
|
||||||
|
|
||||||
pixmap.loadFromData(QByteArray(base64.b64decode(img_data['bg'])))
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["bg"])))
|
||||||
|
|
||||||
self.mainbg.setPixmap(pixmap)
|
self.mainbg.setPixmap(pixmap)
|
||||||
self.mainbg.setScaledContents(False)
|
self.mainbg.setScaledContents(False)
|
||||||
self.mainbg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.mainbg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.cover = QSplitter(mainwin)
|
self.cover = QSplitter(mainwin)
|
||||||
self.cover.setObjectName(u"cover")
|
self.cover.setObjectName("cover")
|
||||||
self.cover.setGeometry(QRect(0, 320, 213, 400))
|
self.cover.setGeometry(QRect(0, 320, 213, 400))
|
||||||
self.cover.setOrientation(Qt.Orientation.Vertical)
|
self.cover.setOrientation(Qt.Orientation.Vertical)
|
||||||
self.Cover_1 = QLabel(self.cover)
|
self.Cover_1 = QLabel(self.cover)
|
||||||
self.Cover_1.setObjectName(u"Cover_1")
|
self.Cover_1.setObjectName("Cover_1")
|
||||||
self.Cover_1.setMinimumSize(QSize(213, 100))
|
self.Cover_1.setMinimumSize(QSize(213, 100))
|
||||||
self.Cover_1.setMaximumSize(QSize(213, 100))
|
self.Cover_1.setMaximumSize(QSize(213, 100))
|
||||||
self.Cover_1.setTextFormat(Qt.TextFormat.AutoText)
|
self.Cover_1.setTextFormat(Qt.TextFormat.AutoText)
|
||||||
|
|
||||||
pixmap.loadFromData(
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_1_cover"])))
|
||||||
QByteArray(base64.b64decode(img_data['vol_1_cover'])))
|
|
||||||
self.Cover_1.setPixmap(pixmap)
|
self.Cover_1.setPixmap(pixmap)
|
||||||
|
|
||||||
self.Cover_1.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.Cover_1.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.cover.addWidget(self.Cover_1)
|
self.cover.addWidget(self.Cover_1)
|
||||||
self.Cover_2 = QLabel(self.cover)
|
self.Cover_2 = QLabel(self.cover)
|
||||||
self.Cover_2.setObjectName(u"Cover_2")
|
self.Cover_2.setObjectName("Cover_2")
|
||||||
self.Cover_2.setMinimumSize(QSize(213, 100))
|
self.Cover_2.setMinimumSize(QSize(213, 100))
|
||||||
self.Cover_2.setMaximumSize(QSize(213, 100))
|
self.Cover_2.setMaximumSize(QSize(213, 100))
|
||||||
self.Cover_2.setTextFormat(Qt.TextFormat.AutoText)
|
self.Cover_2.setTextFormat(Qt.TextFormat.AutoText)
|
||||||
|
|
||||||
pixmap.loadFromData(
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_2_cover"])))
|
||||||
QByteArray(base64.b64decode(img_data['vol_2_cover'])))
|
|
||||||
self.Cover_2.setPixmap(pixmap)
|
self.Cover_2.setPixmap(pixmap)
|
||||||
|
|
||||||
self.Cover_2.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.Cover_2.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.cover.addWidget(self.Cover_2)
|
self.cover.addWidget(self.Cover_2)
|
||||||
self.Cover_3 = QLabel(self.cover)
|
self.Cover_3 = QLabel(self.cover)
|
||||||
self.Cover_3.setObjectName(u"Cover_3")
|
self.Cover_3.setObjectName("Cover_3")
|
||||||
self.Cover_3.setMinimumSize(QSize(213, 100))
|
self.Cover_3.setMinimumSize(QSize(213, 100))
|
||||||
self.Cover_3.setMaximumSize(QSize(213, 100))
|
self.Cover_3.setMaximumSize(QSize(213, 100))
|
||||||
self.Cover_3.setTextFormat(Qt.TextFormat.AutoText)
|
self.Cover_3.setTextFormat(Qt.TextFormat.AutoText)
|
||||||
|
|
||||||
pixmap.loadFromData(
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_3_cover"])))
|
||||||
QByteArray(base64.b64decode(img_data['vol_3_cover'])))
|
|
||||||
self.Cover_3.setPixmap(pixmap)
|
self.Cover_3.setPixmap(pixmap)
|
||||||
|
|
||||||
self.Cover_3.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.Cover_3.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.cover.addWidget(self.Cover_3)
|
self.cover.addWidget(self.Cover_3)
|
||||||
self.Cover_4 = QLabel(self.cover)
|
self.Cover_4 = QLabel(self.cover)
|
||||||
self.Cover_4.setObjectName(u"Cover_4")
|
self.Cover_4.setObjectName("Cover_4")
|
||||||
self.Cover_4.setMinimumSize(QSize(213, 100))
|
self.Cover_4.setMinimumSize(QSize(213, 100))
|
||||||
self.Cover_4.setMaximumSize(QSize(213, 100))
|
self.Cover_4.setMaximumSize(QSize(213, 100))
|
||||||
self.Cover_4.setTextFormat(Qt.TextFormat.AutoText)
|
self.Cover_4.setTextFormat(Qt.TextFormat.AutoText)
|
||||||
|
|
||||||
pixmap.loadFromData(
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_4_cover"])))
|
||||||
QByteArray(base64.b64decode(img_data['vol_4_cover'])))
|
|
||||||
self.Cover_4.setPixmap(pixmap)
|
self.Cover_4.setPixmap(pixmap)
|
||||||
|
|
||||||
self.Cover_4.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.Cover_4.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.cover.addWidget(self.Cover_4)
|
self.cover.addWidget(self.Cover_4)
|
||||||
self.menubg = QLabel(mainwin)
|
self.menubg = QLabel(mainwin)
|
||||||
self.menubg.setObjectName(u"menubg")
|
self.menubg.setObjectName("menubg")
|
||||||
self.menubg.setGeometry(QRect(800, 0, 480, 720))
|
self.menubg.setGeometry(QRect(800, 0, 480, 720))
|
||||||
self.menubg.setMinimumSize(QSize(480, 720))
|
self.menubg.setMinimumSize(QSize(480, 720))
|
||||||
self.menubg.setMaximumSize(QSize(480, 720))
|
self.menubg.setMaximumSize(QSize(480, 720))
|
||||||
|
|
||||||
pixmap.loadFromData(QByteArray(base64.b64decode(img_data['menubg'])))
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["menubg"])))
|
||||||
self.menubg.setPixmap(pixmap)
|
self.menubg.setPixmap(pixmap)
|
||||||
|
|
||||||
self.menubg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.menubg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.layoutWidget = QWidget(mainwin)
|
self.layoutWidget = QWidget(mainwin)
|
||||||
self.layoutWidget.setObjectName(u"layoutWidget")
|
self.layoutWidget.setObjectName("layoutWidget")
|
||||||
self.layoutWidget.setGeometry(QRect(940, 340, 212, 204))
|
self.layoutWidget.setGeometry(QRect(940, 340, 212, 204))
|
||||||
self.button = QVBoxLayout(self.layoutWidget)
|
self.button = QVBoxLayout(self.layoutWidget)
|
||||||
self.button.setObjectName(u"button")
|
self.button.setObjectName("button")
|
||||||
self.button.setContentsMargins(0, 0, 0, 0)
|
self.button.setContentsMargins(0, 0, 0, 0)
|
||||||
self.startbtn = QPushButton(self.layoutWidget)
|
self.startbtn = QPushButton(self.layoutWidget)
|
||||||
self.startbtn.setObjectName(u"startbtn")
|
self.startbtn.setObjectName("startbtn")
|
||||||
self.startbtn.setMinimumSize(QSize(210, 98))
|
self.startbtn.setMinimumSize(QSize(210, 98))
|
||||||
self.startbtn.setMaximumSize(QSize(210, 98))
|
self.startbtn.setMaximumSize(QSize(210, 98))
|
||||||
|
|
||||||
icon1 = QIcon()
|
icon1 = QIcon()
|
||||||
pixmap.loadFromData(QByteArray(base64.b64decode(img_data['btn01bg'])))
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["btn01bg"])))
|
||||||
icon1.addPixmap(pixmap)
|
icon1.addPixmap(pixmap)
|
||||||
|
|
||||||
self.startbtn.setIcon(icon1)
|
self.startbtn.setIcon(icon1)
|
||||||
@ -137,12 +165,12 @@ class Ui_mainwin(object):
|
|||||||
self.button.addWidget(self.startbtn)
|
self.button.addWidget(self.startbtn)
|
||||||
|
|
||||||
self.exitbtn = QPushButton(self.layoutWidget)
|
self.exitbtn = QPushButton(self.layoutWidget)
|
||||||
self.exitbtn.setObjectName(u"exitbtn")
|
self.exitbtn.setObjectName("exitbtn")
|
||||||
self.exitbtn.setMinimumSize(QSize(210, 98))
|
self.exitbtn.setMinimumSize(QSize(210, 98))
|
||||||
self.exitbtn.setMaximumSize(QSize(210, 98))
|
self.exitbtn.setMaximumSize(QSize(210, 98))
|
||||||
|
|
||||||
icon2 = QIcon()
|
icon2 = QIcon()
|
||||||
pixmap.loadFromData(QByteArray(base64.b64decode(img_data['btn02bg'])))
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["btn02bg"])))
|
||||||
icon2.addPixmap(pixmap)
|
icon2.addPixmap(pixmap)
|
||||||
|
|
||||||
self.exitbtn.setIcon(icon2)
|
self.exitbtn.setIcon(icon2)
|
||||||
@ -152,22 +180,21 @@ class Ui_mainwin(object):
|
|||||||
self.button.addWidget(self.exitbtn)
|
self.button.addWidget(self.exitbtn)
|
||||||
|
|
||||||
self.sup_info = QLabel(mainwin)
|
self.sup_info = QLabel(mainwin)
|
||||||
self.sup_info.setObjectName(u"sup_info")
|
self.sup_info.setObjectName("sup_info")
|
||||||
self.sup_info.setGeometry(QRect(0, 230, 213, 100))
|
self.sup_info.setGeometry(QRect(0, 230, 213, 100))
|
||||||
self.sup_info.setMinimumSize(QSize(213, 100))
|
self.sup_info.setMinimumSize(QSize(213, 100))
|
||||||
self.sup_info.setMaximumSize(QSize(213, 100))
|
self.sup_info.setMaximumSize(QSize(213, 100))
|
||||||
|
|
||||||
pixmap.loadFromData(
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["support_info"])))
|
||||||
QByteArray(base64.b64decode(img_data['support_info'])))
|
|
||||||
self.sup_info.setPixmap(pixmap)
|
self.sup_info.setPixmap(pixmap)
|
||||||
|
|
||||||
self.future_info = QLabel(mainwin)
|
self.future_info = QLabel(mainwin)
|
||||||
self.future_info.setObjectName(u"future_info")
|
self.future_info.setObjectName("future_info")
|
||||||
self.future_info.setGeometry(QRect(860, 700, 370, 16))
|
self.future_info.setGeometry(QRect(860, 700, 370, 16))
|
||||||
self.future_info.setMinimumSize(QSize(370, 16))
|
self.future_info.setMinimumSize(QSize(370, 16))
|
||||||
self.future_info.setMaximumSize(QSize(370, 16))
|
self.future_info.setMaximumSize(QSize(370, 16))
|
||||||
font = QFont()
|
font = QFont()
|
||||||
font.setFamilies([u"Consolas"])
|
font.setFamilies(["Consolas"])
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
font.setItalic(True)
|
font.setItalic(True)
|
||||||
self.future_info.setFont(font)
|
self.future_info.setFont(font)
|
||||||
@ -188,7 +215,9 @@ class Ui_mainwin(object):
|
|||||||
def retranslateUi(self, mainwin):
|
def retranslateUi(self, mainwin):
|
||||||
mainwin.setWindowTitle(
|
mainwin.setWindowTitle(
|
||||||
QCoreApplication.translate(
|
QCoreApplication.translate(
|
||||||
"mainwin", u"FRAISEMOE Addons Installer V4.5.0.30988", None))
|
"mainwin", "FRAISEMOE Addons Installer V4.8.6.17218", None
|
||||||
|
)
|
||||||
|
)
|
||||||
self.mainbg.setText("")
|
self.mainbg.setText("")
|
||||||
self.Cover_1.setText("")
|
self.Cover_1.setText("")
|
||||||
self.Cover_2.setText("")
|
self.Cover_2.setText("")
|
||||||
@ -200,7 +229,8 @@ class Ui_mainwin(object):
|
|||||||
self.sup_info.setText("")
|
self.sup_info.setText("")
|
||||||
self.future_info.setText(
|
self.future_info.setText(
|
||||||
QCoreApplication.translate(
|
QCoreApplication.translate(
|
||||||
"mainwin",
|
"mainwin", "Nekopara After La Vraie Famille COMMING SOON IN 2025", None
|
||||||
"Nekopara After La Vraie Famille COMMING SOON IN 2025", None))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# retranslateUi
|
# retranslateUi
|
||||||
|
695
source/Main.py
695
source/Main.py
@ -5,309 +5,467 @@ import shutil
|
|||||||
import hashlib
|
import hashlib
|
||||||
import sys
|
import sys
|
||||||
import base64
|
import base64
|
||||||
|
import psutil
|
||||||
|
import ctypes
|
||||||
|
|
||||||
from PySide6 import QtWidgets, QtCore
|
from PySide6 import QtWidgets, QtCore
|
||||||
from PySide6.QtCore import Qt, QByteArray
|
from PySide6.QtCore import Qt, QByteArray, Signal
|
||||||
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QProgressBar, QVBoxLayout, QLabel
|
from PySide6.QtWidgets import (
|
||||||
|
QApplication,
|
||||||
|
QWidget,
|
||||||
|
QMessageBox,
|
||||||
|
QProgressBar,
|
||||||
|
QVBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
)
|
||||||
from PySide6.QtGui import QIcon, QPixmap
|
from PySide6.QtGui import QIcon, QPixmap
|
||||||
|
from collections import deque
|
||||||
from pic_data import img_data
|
from pic_data import img_data
|
||||||
from GUI import Ui_mainwin
|
from GUI import Ui_mainwin
|
||||||
|
|
||||||
Msgboxtitle = "@FRAISEMOE Addons Installer V4.5.0.30988"
|
APP_VERSION = "@FRAISEMOE Addons Installer V4.8.6.17218"
|
||||||
|
TEMP = os.getenv("TEMP")
|
||||||
Packfolder = "./addons"
|
CACHE = os.path.join(TEMP, "FRAISEMOE")
|
||||||
|
PLUGIN = os.path.join(CACHE, "PLUGIN")
|
||||||
|
CONFIG_URL = "https://archive.ovofish.com/api/widget/nekopara/download_url.json"
|
||||||
|
UA = "Mozilla/5.0 (Linux debian12 Python-Accept) Gecko/20100101 Firefox/114.0"
|
||||||
|
SRC_HASHES = {
|
||||||
|
"NEKOPARA Vol.1": "04b48b231a7f34431431e5027fcc7b27affaa951b8169c541709156acf754f3e",
|
||||||
|
"NEKOPARA Vol.2": "b9c00a2b113a1e768bf78400e4f9075ceb7b35349cdeca09be62eb014f0d4b42",
|
||||||
|
"NEKOPARA Vol.3": "2ce7b223c84592e1ebc3b72079dee1e5e8d064ade15723328a64dee58833b9d5",
|
||||||
|
"NEKOPARA Vol.4": "4a4a9ae5a75a18aacbe3ab0774d7f93f99c046afe3a777ee0363e8932b90f36a",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def calc_hash(dstfilepath):
|
def admin_status():
|
||||||
msg_box = QMessageBox()
|
try:
|
||||||
msg_box.setWindowTitle(f'通知 {Msgboxtitle}')
|
return ctypes.windll.shell32.IsUserAnAdmin()
|
||||||
pixmap = QPixmap()
|
except:
|
||||||
|
return False
|
||||||
icon = QIcon()
|
|
||||||
pixmap.loadFromData(QByteArray(base64.b64decode(img_data['icon'])))
|
|
||||||
icon.addPixmap(pixmap)
|
def run_as_admin():
|
||||||
|
script = os.path.abspath(sys.argv[0])
|
||||||
msg_box.setWindowIcon(icon)
|
params = " ".join([script] + sys.argv[1:])
|
||||||
msg_box.setText('\n正在检验文件完整性...\n')
|
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1)
|
||||||
msg_box.setStandardButtons(QMessageBox.StandardButton.NoButton)
|
|
||||||
msg_box.show()
|
|
||||||
|
class DownloadThread(QtCore.QThread):
|
||||||
# Force the message box to be painted immediately
|
progress = Signal(int)
|
||||||
QApplication.processEvents()
|
finished = Signal(bool, str)
|
||||||
|
|
||||||
sha256_hash = hashlib.sha256()
|
def __init__(self, url, _7z_path, parent=None):
|
||||||
with open(dstfilepath, "rb") as f:
|
super().__init__(parent)
|
||||||
for byte_block in iter(lambda: f.read(4096), b""):
|
self.url = url
|
||||||
sha256_hash.update(byte_block)
|
self._7z_path = _7z_path
|
||||||
# Close the message box after the operation is complete
|
|
||||||
msg_box.close()
|
def run(self):
|
||||||
|
try:
|
||||||
return sha256_hash.hexdigest()
|
headers = {"User-Agent": UA}
|
||||||
|
r = requests.get(self.url, headers=headers, stream=True, timeout=10)
|
||||||
|
r.raise_for_status()
|
||||||
|
block_size = 64 * 1024
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
def game_process_status(process_name):
|
||||||
|
for proc in psutil.process_iter(["pid", "name"]):
|
||||||
|
try:
|
||||||
|
if process_name.lower() in proc.info["name"].lower():
|
||||||
|
return proc.info["pid"]
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def kill_process(pid):
|
||||||
|
try:
|
||||||
|
process = psutil.Process(pid)
|
||||||
|
process.terminate()
|
||||||
|
process.wait(timeout=5)
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# UI
|
|
||||||
class MyWindow(QWidget, Ui_mainwin):
|
class MyWindow(QWidget, Ui_mainwin):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.tgtfolder = ''
|
self.selected_folder = ""
|
||||||
self.download_stat = {
|
self.installed_status = {
|
||||||
'NEKOPARA Vol.1': False,
|
"NEKOPARA Vol.1": False,
|
||||||
'NEKOPARA Vol.2': False,
|
"NEKOPARA Vol.2": False,
|
||||||
'NEKOPARA Vol.3': False,
|
"NEKOPARA Vol.3": False,
|
||||||
'NEKOPARA Vol.4': False
|
"NEKOPARA Vol.4": False,
|
||||||
}
|
}
|
||||||
# Create a folder to store the downloaded files
|
self.download_queue = deque()
|
||||||
if not os.path.exists(Packfolder):
|
self.current_download_thread = None
|
||||||
os.makedirs(Packfolder)
|
|
||||||
if not os.path.exists(Packfolder):
|
game_process_info = {
|
||||||
QMessageBox.critical(self, f'错误 {Msgboxtitle}',
|
"nekopara_vol1.exe": "NEKOPARA Vol.1",
|
||||||
'\n无法创建安装包存放位置,请检查文件读写权限后再试\n')
|
"nekopara_vol2.exe": "NEKOPARA Vol.2",
|
||||||
|
"NEKOPARAvol3.exe": "NEKOPARA Vol.3",
|
||||||
|
"nekopara_vol4.exe": "NEKOPARA Vol.4",
|
||||||
|
}
|
||||||
|
|
||||||
|
for process_name, game_version in game_process_info.items():
|
||||||
|
pid = game_process_status(process_name)
|
||||||
|
if pid:
|
||||||
|
msg_box = QMessageBox()
|
||||||
|
msg_box.setWindowTitle(f"进程检测 {APP_VERSION}")
|
||||||
|
pixmap = QPixmap()
|
||||||
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["icon"])))
|
||||||
|
icon = QIcon(pixmap)
|
||||||
|
msg_box.setWindowIcon(icon)
|
||||||
|
msg_box.setText(f"\n检测到 {game_version} 正在运行,是否关闭?\n")
|
||||||
|
yes_button = msg_box.addButton(
|
||||||
|
"确定", QMessageBox.ButtonRole.AcceptRole
|
||||||
|
)
|
||||||
|
no_button = msg_box.addButton("取消", QMessageBox.ButtonRole.RejectRole)
|
||||||
|
msg_box.setDefaultButton(no_button)
|
||||||
|
msg_box.exec()
|
||||||
|
|
||||||
|
if msg_box.clickedButton() == yes_button:
|
||||||
|
kill_process(pid)
|
||||||
|
else:
|
||||||
|
QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
f"警告 {APP_VERSION}",
|
||||||
|
f"\n请关闭 {game_version} 后再运行本程序。\n",
|
||||||
|
)
|
||||||
|
self.close()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
if not os.path.exists(PLUGIN):
|
||||||
|
os.makedirs(PLUGIN)
|
||||||
|
if not os.path.exists(PLUGIN):
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
"\n无法创建缓存位置\n\n使用管理员身份运行或检查文件读写权限\n",
|
||||||
|
)
|
||||||
self.close()
|
self.close()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
# Buttons
|
self.startbtn.clicked.connect(self.file_dialog)
|
||||||
self.startbtn.clicked.connect(self.ChooseFileDialog)
|
self.exitbtn.clicked.connect(self.shutdown_app)
|
||||||
self.exitbtn.clicked.connect(self.closeEvent)
|
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def get_install_paths(self):
|
||||||
self.confirm_quit = True
|
return {
|
||||||
if self.confirm_quit:
|
"NEKOPARA Vol.1": os.path.join(
|
||||||
reply = QMessageBox.question(
|
self.selected_folder, "NEKOPARA Vol. 1", "adultsonly.xp3"
|
||||||
self, '退出程序', '\n是否确定退出?\n',
|
),
|
||||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
"NEKOPARA Vol.2": os.path.join(
|
||||||
QMessageBox.StandardButton.No)
|
self.selected_folder, "NEKOPARA Vol. 2", "adultsonly.xp3"
|
||||||
if reply == QMessageBox.StandardButton.Yes:
|
),
|
||||||
# event.accept()
|
"NEKOPARA Vol.3": os.path.join(
|
||||||
shutil.rmtree(Packfolder)
|
self.selected_folder, "NEKOPARA Vol. 3", "update00.int"
|
||||||
sys.exit()
|
),
|
||||||
else:
|
"NEKOPARA Vol.4": os.path.join(
|
||||||
pass
|
self.selected_folder, "NEKOPARA Vol. 4", "vol4adult.xp3"
|
||||||
# event.ignore()
|
),
|
||||||
else:
|
|
||||||
# event.accept()
|
|
||||||
shutil.rmtree(Packfolder)
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def download_config(self) -> dict:
|
|
||||||
config_url = 'https://cn-sy1.rains3.com/cloudfile/cloudfile/uploads/2025/01/25/xyXGNdvF_download_config.json'
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.get(config_url, timeout=10)
|
|
||||||
response = response.json()
|
|
||||||
|
|
||||||
url_v1 = response['vol.1.data']['url']
|
|
||||||
url_v2 = response['vol.2.data']['url']
|
|
||||||
url_v3 = response['vol.3.data']['url']
|
|
||||||
url_v4 = response['vol.4.data']['url']
|
|
||||||
except Exception:
|
|
||||||
QMessageBox.critical(self, f"错误 {Msgboxtitle}",f"\n获取下载配置失败\n检查网络状态,或服务器故障\n")
|
|
||||||
return {}
|
|
||||||
return {'vol1':url_v1,'vol2':url_v2,'vol3':url_v3,'vol4':url_v4}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def CheckFileStat(self, basedic):
|
|
||||||
pass
|
|
||||||
self.download_stat = {
|
|
||||||
'NEKOPARA Vol.1': False,
|
|
||||||
'NEKOPARA Vol.2': False,
|
|
||||||
'NEKOPARA Vol.3': False,
|
|
||||||
'NEKOPARA Vol.4': False
|
|
||||||
}
|
}
|
||||||
if os.path.exists(f'{basedic}/NEKOPARA Vol. 1/adultsonly.xp3'):
|
|
||||||
if calc_hash(
|
|
||||||
f'{basedic}/NEKOPARA Vol. 1/adultsonly.xp3'
|
|
||||||
) == '04b48b231a7f34431431e5027fcc7b27affaa951b8169c541709156acf754f3e':
|
|
||||||
self.download_stat['NEKOPARA Vol.1'] = True
|
|
||||||
else:
|
|
||||||
QMessageBox.warning(self, f"警告 {Msgboxtitle}",
|
|
||||||
'\n补丁文件文件校验失败\n即将重新安装当前版本补丁\n')
|
|
||||||
os.remove(f'{basedic}/NEKOPARA Vol. 1/adultsonly.xp3')
|
|
||||||
|
|
||||||
if os.path.exists(f'{basedic}/NEKOPARA Vol. 2/adultsonly.xp3'):
|
def file_dialog(self):
|
||||||
if calc_hash(
|
self.selected_folder = QtWidgets.QFileDialog.getExistingDirectory(
|
||||||
f'{basedic}/NEKOPARA Vol. 2/adultsonly.xp3'
|
self, f"选择游戏所在【上级目录】 {APP_VERSION}"
|
||||||
) == 'b9c00a2b113a1e768bf78400e4f9075ceb7b35349cdeca09be62eb014f0d4b42':
|
)
|
||||||
self.download_stat['NEKOPARA Vol.2'] = True
|
if not self.selected_folder:
|
||||||
else:
|
QMessageBox.warning(
|
||||||
QMessageBox.warning(self, f"警告 {Msgboxtitle}",
|
self, f"通知 {APP_VERSION}", "\n未选择任何目录,请重新选择\n"
|
||||||
'\n补丁文件文件校验失败\n即将重新安装当前版本补丁\n')
|
)
|
||||||
os.remove(f'{basedic}/NEKOPARA Vol. 2/adultsonly.xp3')
|
|
||||||
|
|
||||||
if os.path.exists(f'{basedic}/NEKOPARA Vol. 3/update00.int'):
|
|
||||||
if calc_hash(
|
|
||||||
f'{basedic}/NEKOPARA Vol. 3/update00.int'
|
|
||||||
) == '2ce7b223c84592e1ebc3b72079dee1e5e8d064ade15723328a64dee58833b9d5':
|
|
||||||
self.download_stat['NEKOPARA Vol.3'] = True
|
|
||||||
else:
|
|
||||||
QMessageBox.warning(self, f"警告 {Msgboxtitle}",
|
|
||||||
'\n补丁文件文件校验失败\n即将重新安装当前版本补丁\n')
|
|
||||||
os.remove(f'{basedic}/NEKOPARA Vol. 3/update00.int')
|
|
||||||
|
|
||||||
if os.path.exists(f'{basedic}/NEKOPARA Vol. 4/vol4adult.xp3'):
|
|
||||||
if calc_hash(
|
|
||||||
f'{basedic}/NEKOPARA Vol. 4/vol4adult.xp3'
|
|
||||||
) == '4a4a9ae5a75a18aacbe3ab0774d7f93f99c046afe3a777ee0363e8932b90f36a':
|
|
||||||
self.download_stat['NEKOPARA Vol.4'] = True
|
|
||||||
else:
|
|
||||||
QMessageBox.warning(self, f"警告 {Msgboxtitle}",
|
|
||||||
'\n补丁文件文件校验失败\n即将重新安装当前版本补丁\n')
|
|
||||||
os.remove(f'{basedic}/NEKOPARA Vol. 4/vol4adult.xp3')
|
|
||||||
|
|
||||||
def ChooseFileDialog(self):
|
|
||||||
self.tgtfolder = QtWidgets.QFileDialog.getExistingDirectory(
|
|
||||||
self, f"选择游戏所在上级目录 {Msgboxtitle}")
|
|
||||||
if not self.tgtfolder:
|
|
||||||
QMessageBox.warning(self, f"通知 {Msgboxtitle}",
|
|
||||||
"\n尚未选择任何目录,请重新选择目录\n")
|
|
||||||
return
|
return
|
||||||
self.PackParameter()
|
self.download_action()
|
||||||
|
|
||||||
def DownloadParameter(self, url, dstfilepath, GV, file_7z_route,
|
def hash_calculate(self, file_path):
|
||||||
srcfileroute):
|
sha256_hash = hashlib.sha256()
|
||||||
"""
|
with open(file_path, "rb") as f:
|
||||||
install with params
|
for byte_block in iter(lambda: f.read(4096), b""):
|
||||||
:param url: install url
|
sha256_hash.update(byte_block)
|
||||||
:param dstfilepath: dest folder route
|
return sha256_hash.hexdigest()
|
||||||
:param GV: GameVer
|
|
||||||
:param file_7z_route: temp addonfile route,eg: './addons/vol.1.7z'
|
def hash_pop_window(self):
|
||||||
:param srcfileroute: src route,eg: './addons/vol.1/adultsonly.xp3'
|
msg_box = QMessageBox()
|
||||||
:return: None
|
msg_box.setWindowTitle(f"通知 {APP_VERSION}")
|
||||||
"""
|
pixmap = QPixmap()
|
||||||
# TODO: check whether 7z file in assets and unzip, install
|
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["icon"])))
|
||||||
if not os.path.exists(dstfilepath):
|
icon = QIcon(pixmap)
|
||||||
self.download_stat[GV] = False
|
msg_box.setWindowIcon(icon)
|
||||||
elif self.download_stat[GV]:
|
msg_box.setText("\n正在检验文件状态...\n")
|
||||||
QMessageBox.information(self, f"通知 {Msgboxtitle}",
|
msg_box.setStandardButtons(QMessageBox.StandardButton.NoButton)
|
||||||
f"\n{GV} 补丁包已安装\n")
|
msg_box.show()
|
||||||
else:
|
QApplication.processEvents()
|
||||||
progress_window = ProgressWindow(self)
|
return msg_box
|
||||||
progress_window.show()
|
|
||||||
try:
|
def pre_hash_compare(self, install_path, game_version, SRC_HASHES):
|
||||||
r = requests.get(url, stream=True, timeout=10)
|
if not os.path.exists(install_path):
|
||||||
r.raise_for_status()
|
self.installed_status[game_version] = False
|
||||||
block_size = 64 * 1024
|
return
|
||||||
progress = 0
|
|
||||||
progress_window.setmaxvalue(
|
msg_box = self.hash_pop_window()
|
||||||
int(r.headers.get('content-length', 0)))
|
file_hash = self.hash_calculate(install_path)
|
||||||
with open(file_7z_route, 'wb') as f:
|
msg_box.close()
|
||||||
for chunk in r.iter_content(chunk_size=block_size):
|
|
||||||
f.write(chunk)
|
if file_hash != SRC_HASHES[game_version]:
|
||||||
progress += len(chunk)
|
msg_box = QMessageBox(self)
|
||||||
progress_window.setprogressbarval(progress)
|
msg_box.setWindowTitle(f"文件校验 {APP_VERSION}")
|
||||||
QApplication.processEvents()
|
msg_box.setText(
|
||||||
except Exception as e:
|
f"\n【 当前版本已安装旧版本补丁 -> {game_version} 】\n\n是否重新安装?\n----->取消安装前应确认补丁是否可用<-----\n"
|
||||||
QMessageBox.critical(self, f"错误 {Msgboxtitle}",
|
)
|
||||||
f"\n网络超时,重启程序再试\n错误信息:{e}\n")
|
yes_button = msg_box.addButton("确定", QMessageBox.ButtonRole.AcceptRole)
|
||||||
self.download_stat[GV] = False
|
no_button = msg_box.addButton("取消", QMessageBox.ButtonRole.RejectRole)
|
||||||
progress_window.close()
|
msg_box.setDefaultButton(no_button)
|
||||||
|
msg_box.exec()
|
||||||
|
if msg_box.clickedButton() == yes_button:
|
||||||
|
self.installed_status[game_version] = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# Extract the compressed file to the specified directory
|
|
||||||
archive = py7zr.SevenZipFile(file_7z_route, mode='r')
|
|
||||||
# Decompression directory of compressed files
|
|
||||||
archive.extractall(path=r'./addons')
|
|
||||||
archive.close()
|
|
||||||
shutil.copy(srcfileroute, dstfilepath)
|
|
||||||
self.download_stat[GV] = True
|
|
||||||
QMessageBox.information(self, f"通知 {Msgboxtitle}",
|
|
||||||
f"\n{GV} 补丁包安装完成\n")
|
|
||||||
|
|
||||||
def PackParameter(self):
|
|
||||||
# Get the installing stat
|
|
||||||
self.CheckFileStat(self.tgtfolder)
|
|
||||||
config = self.download_config()
|
|
||||||
if not config:
|
|
||||||
QMessageBox.critical(self, f'错误 {Msgboxtitle}',
|
|
||||||
'\n网络连接错误,重启应用再试\n')
|
|
||||||
return
|
|
||||||
|
|
||||||
# Download
|
|
||||||
self.DownloadParameter(
|
|
||||||
config['vol1'],
|
|
||||||
f'{self.tgtfolder}/NEKOPARA Vol. 1', 'NEKOPARA Vol.1',
|
|
||||||
'./addons/vol.1.7z', './addons/vol.1/adultsonly.xp3')
|
|
||||||
self.DownloadParameter(
|
|
||||||
config['vol2'],
|
|
||||||
f'{self.tgtfolder}/NEKOPARA Vol. 2', 'NEKOPARA Vol.2',
|
|
||||||
'./addons/vol.2.7z', './addons/vol.2/adultsonly.xp3')
|
|
||||||
self.DownloadParameter(
|
|
||||||
config['vol3'],
|
|
||||||
f'{self.tgtfolder}/NEKOPARA Vol. 3', 'NEKOPARA Vol.3',
|
|
||||||
'./addons/vol.3.7z', './addons/vol.3/update00.int')
|
|
||||||
self.DownloadParameter(
|
|
||||||
config['vol4'],
|
|
||||||
f'{self.tgtfolder}/NEKOPARA Vol. 4', 'NEKOPARA Vol.4',
|
|
||||||
'./addons/vol.4.7z', './addons/vol.4/vol4adult.xp3')
|
|
||||||
if not self.CompareHash():
|
|
||||||
QMessageBox.critical(self, f"错误 {Msgboxtitle}",
|
|
||||||
"\n文件损坏,无法通过文件校验\n请重新启动程序\n")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Count the installation results
|
|
||||||
installver = ''
|
|
||||||
installnum = 0
|
|
||||||
failver = ''
|
|
||||||
failnum = 0
|
|
||||||
for i in list(self.download_stat.keys()):
|
|
||||||
if self.download_stat[i]:
|
|
||||||
installver += f"{i}\n"
|
|
||||||
installnum += 1
|
|
||||||
else:
|
else:
|
||||||
failver += f"{i}\n"
|
self.installed_status[game_version] = True
|
||||||
failnum += 1
|
return
|
||||||
|
else:
|
||||||
|
self.installed_status[game_version] = True
|
||||||
|
return
|
||||||
|
|
||||||
QMessageBox.information(
|
def late_hash_compare(self, SRC_HASHES):
|
||||||
self, f"完成 {Msgboxtitle}", f"\n安装结果:\n"
|
install_paths = self.get_install_paths()
|
||||||
f"安装成功数:{installnum} 安装失败数:{failnum}\n\n"
|
|
||||||
f"安装成功的版本:\n"
|
|
||||||
f"{installver}\n"
|
|
||||||
f"尚未持有的版本:\n"
|
|
||||||
f"{failver}\n")
|
|
||||||
|
|
||||||
def CompareHash(self):
|
|
||||||
# Src files hash
|
|
||||||
src_hash_v1 = '04b48b231a7f34431431e5027fcc7b27affaa951b8169c541709156acf754f3e'
|
|
||||||
src_hash_v2 = 'b9c00a2b113a1e768bf78400e4f9075ceb7b35349cdeca09be62eb014f0d4b42'
|
|
||||||
src_hash_v3 = '2ce7b223c84592e1ebc3b72079dee1e5e8d064ade15723328a64dee58833b9d5'
|
|
||||||
src_hash_v4 = '4a4a9ae5a75a18aacbe3ab0774d7f93f99c046afe3a777ee0363e8932b90f36a'
|
|
||||||
# Check hash value
|
|
||||||
passed = True
|
passed = True
|
||||||
passlist = []
|
for game, hash_value in SRC_HASHES.items():
|
||||||
for i in list(self.download_stat.keys()):
|
if self.installed_status.get(game):
|
||||||
if self.download_stat[i]:
|
msg_box = self.hash_pop_window()
|
||||||
passlist.append(i)
|
file_hash = self.hash_calculate(install_paths[game])
|
||||||
for i in passlist:
|
msg_box.close()
|
||||||
if i == 'NEKOPARA Vol.1':
|
if file_hash != hash_value:
|
||||||
if calc_hash(f'{self.tgtfolder}/NEKOPARA Vol. 1/adultsonly.xp3'
|
|
||||||
) != src_hash_v1:
|
|
||||||
passed = False
|
|
||||||
elif i == 'NEKOPARA Vol.2':
|
|
||||||
if calc_hash(f'{self.tgtfolder}/NEKOPARA Vol. 2/adultsonly.xp3'
|
|
||||||
) != src_hash_v2:
|
|
||||||
passed = False
|
|
||||||
elif i == 'NEKOPARA Vol.3':
|
|
||||||
if calc_hash(f'{self.tgtfolder}/NEKOPARA Vol. 3/update00.int'
|
|
||||||
) != src_hash_v3:
|
|
||||||
passed = False
|
|
||||||
elif i == 'NEKOPARA Vol.4':
|
|
||||||
if calc_hash(f'{self.tgtfolder}/NEKOPARA Vol. 4/vol4adult.xp3'
|
|
||||||
) != src_hash_v4:
|
|
||||||
passed = False
|
passed = False
|
||||||
|
break
|
||||||
return passed
|
return passed
|
||||||
|
|
||||||
|
def download_config(self) -> dict:
|
||||||
|
try:
|
||||||
|
headers = {"User-Agent": UA}
|
||||||
|
response = requests.get(CONFIG_URL, headers=headers, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
response = response.json()
|
||||||
|
return {f"vol{i+1}": response[f"vol.{i+1}.data"]["url"] for i in range(4)}
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n下载配置获取失败\n\n网络状态异常或服务器故障\n\n【错误信息】:{e}\n",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n下载配置获取失败\n\n未知错误\n\n【错误信息】:{e}\n",
|
||||||
|
)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path):
|
||||||
|
game_exe = {
|
||||||
|
"NEKOPARA Vol.1": os.path.join(
|
||||||
|
self.selected_folder, "NEKOPARA Vol. 1", "nekopara_vol1.exe"
|
||||||
|
),
|
||||||
|
"NEKOPARA Vol.2": os.path.join(
|
||||||
|
self.selected_folder, "NEKOPARA Vol. 2", "nekopara_vol2.exe"
|
||||||
|
),
|
||||||
|
"NEKOPARA Vol.3": os.path.join(
|
||||||
|
self.selected_folder, "NEKOPARA Vol. 3", "NEKOPARAvol3.exe"
|
||||||
|
),
|
||||||
|
"NEKOPARA Vol.4": os.path.join(
|
||||||
|
self.selected_folder, "NEKOPARA Vol. 4", "nekopara_vol4.exe"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
game_version not in game_exe
|
||||||
|
or not os.path.exists(game_exe[game_version])
|
||||||
|
or self.installed_status[game_version]
|
||||||
|
):
|
||||||
|
self.next_download_task()
|
||||||
|
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_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_VERSION}", f"\n{game_version} 补丁已安装\n"
|
||||||
|
)
|
||||||
|
except py7zr.Bad7zFile as e:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n文件损坏\n\n【错误信息】:{e}\n",
|
||||||
|
)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n文件不存在\n\n【错误信息】:{e}\n",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n文件操作失败,请重试\n\n【错误信息】:{e}\n",
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
msg_box.close()
|
||||||
|
else:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n文件获取失败\n网络状态异常或服务器故障\n\n【错误信息】:{error}\n",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.next_download_task()
|
||||||
|
|
||||||
|
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, SRC_HASHES)
|
||||||
|
if self.late_hash_compare(SRC_HASHES):
|
||||||
|
config = self.download_config()
|
||||||
|
if not config:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self, f"错误 {APP_VERSION}", "\n网络状态异常或服务器故障,请重试\n"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
for i in range(1, 5):
|
||||||
|
game_version = f"NEKOPARA Vol.{i}"
|
||||||
|
if self.installed_status[game_version] == False:
|
||||||
|
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,
|
||||||
|
f"vol.{i}",
|
||||||
|
[
|
||||||
|
"adultsonly.xp3",
|
||||||
|
"adultsonly.xp3",
|
||||||
|
"update00.int",
|
||||||
|
"vol4adult.xp3",
|
||||||
|
][i - 1],
|
||||||
|
)
|
||||||
|
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.show_result()
|
||||||
|
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 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_VERSION}",
|
||||||
|
f"\n安装结果:\n"
|
||||||
|
f"安装成功数:{len(installed_version.splitlines())} 安装失败数:{len(failed_ver.splitlines())}\n\n"
|
||||||
|
f"安装成功的版本:\n"
|
||||||
|
f"{installed_version}\n"
|
||||||
|
f"尚未持有的版本:\n"
|
||||||
|
f"{failed_ver}\n",
|
||||||
|
)
|
||||||
|
|
||||||
|
def shutdown_app(self):
|
||||||
|
msg_box = QMessageBox(self)
|
||||||
|
msg_box.setWindowTitle("退出程序")
|
||||||
|
msg_box.setText("\n是否确定退出?\n")
|
||||||
|
yes_button = msg_box.addButton("确定", QMessageBox.ButtonRole.AcceptRole)
|
||||||
|
no_button = msg_box.addButton("取消", QMessageBox.ButtonRole.RejectRole)
|
||||||
|
msg_box.setDefaultButton(no_button)
|
||||||
|
msg_box.exec()
|
||||||
|
|
||||||
|
if msg_box.clickedButton() == yes_button:
|
||||||
|
if (
|
||||||
|
self.current_download_thread
|
||||||
|
and self.current_download_thread.isRunning()
|
||||||
|
):
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
"\n当前有下载任务正在进行,完成后再试。\n",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if os.path.exists(PLUGIN):
|
||||||
|
try:
|
||||||
|
shutil.rmtree(PLUGIN)
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
f"错误 {APP_VERSION}",
|
||||||
|
f"\n清理缓存失败\n\n【错误信息】:{e}\n",
|
||||||
|
)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
# Progress
|
|
||||||
class ProgressWindow(QtWidgets.QDialog):
|
class ProgressWindow(QtWidgets.QDialog):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(ProgressWindow, self).__init__(parent)
|
super(ProgressWindow, self).__init__(parent)
|
||||||
self.setWindowTitle(f"下载进度 {Msgboxtitle}")
|
self.setWindowTitle(f"下载进度 {APP_VERSION}")
|
||||||
self.resize(400, 100)
|
self.resize(400, 100)
|
||||||
self.progress_bar_max = 100
|
self.progress_bar_max = 100
|
||||||
# Disable close button and system menu
|
|
||||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
|
self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
|
||||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowSystemMenuHint)
|
self.setWindowFlags(self.windowFlags() & ~Qt.WindowSystemMenuHint)
|
||||||
|
|
||||||
@ -329,7 +487,10 @@ class ProgressWindow(QtWidgets.QDialog):
|
|||||||
QtCore.QTimer.singleShot(2000, self.close)
|
QtCore.QTimer.singleShot(2000, self.close)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
|
if not admin_status():
|
||||||
|
run_as_admin()
|
||||||
|
sys.exit()
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
window = MyWindow()
|
window = MyWindow()
|
||||||
window.show()
|
window.show()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user