From 1d223fe32a1f3f770e1989e86380c5323f962ef0 Mon Sep 17 00:00:00 2001 From: 283375 Date: Sat, 16 Sep 2023 18:59:59 +0800 Subject: [PATCH] wip: arcaea-offline==0.2.0 --- ui/designer/tabs/tabDb/tabDb_Manage.ui | 73 +++++++- ui/designer/tabs/tabDb/tabDb_Manage_ui.py | 219 ++++++++++++++-------- ui/implements/tabs/tabDb/tabDb_Manage.py | 109 ++++++++++- ui/resources/lang/en_US.ts | 137 +++++--------- ui/resources/lang/zh_CN.ts | 137 +++++--------- 5 files changed, 417 insertions(+), 258 deletions(-) diff --git a/ui/designer/tabs/tabDb/tabDb_Manage.ui b/ui/designer/tabs/tabDb/tabDb_Manage.ui index 200969e..c39e792 100644 --- a/ui/designer/tabs/tabDb/tabDb_Manage.ui +++ b/ui/designer/tabs/tabDb/tabDb_Manage.ui @@ -31,41 +31,104 @@ - + importSt3Button - + importSt3.description - + Qt::Horizontal - + exportScoresButton - + exportScores.description + + + + Qt::Horizontal + + + + + + + importPacklistButton + + + + + + + importSonglistButton + + + + + + + importPacklist.description + + + + + + + importSonglist.description + + + + + + + exportArcsongJsonButton + + + + + + + exportArcsongJson.description + + + + + + + importApkButton + + + + + + + importApk.description + + + diff --git a/ui/designer/tabs/tabDb/tabDb_Manage_ui.py b/ui/designer/tabs/tabDb/tabDb_Manage_ui.py index 7c5b07b..3d192de 100644 --- a/ui/designer/tabs/tabDb/tabDb_Manage_ui.py +++ b/ui/designer/tabs/tabDb/tabDb_Manage_ui.py @@ -1,82 +1,137 @@ -# -*- coding: utf-8 -*- - -################################################################################ -## Form generated from reading UI file 'tabDb_Manage.ui' -## -## Created by: Qt User Interface Compiler version 6.5.2 -## -## WARNING! All changes made in this file will be lost when recompiling UI file! -################################################################################ - -from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, - QMetaObject, QObject, QPoint, 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, QFormLayout, QFrame, QLabel, - QPushButton, QSizePolicy, QWidget) - -class Ui_TabDb_Manage(object): - def setupUi(self, TabDb_Manage): - if not TabDb_Manage.objectName(): - TabDb_Manage.setObjectName(u"TabDb_Manage") - TabDb_Manage.resize(630, 528) - TabDb_Manage.setWindowTitle(u"TabDb_Manage") - self.formLayout = QFormLayout(TabDb_Manage) - self.formLayout.setObjectName(u"formLayout") - self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) - self.syncArcSongDbButton = QPushButton(TabDb_Manage) - self.syncArcSongDbButton.setObjectName(u"syncArcSongDbButton") - - self.formLayout.setWidget(0, QFormLayout.LabelRole, self.syncArcSongDbButton) - - self.label = QLabel(TabDb_Manage) - self.label.setObjectName(u"label") - - self.formLayout.setWidget(0, QFormLayout.FieldRole, self.label) - - self.importSt3Button = QPushButton(TabDb_Manage) - self.importSt3Button.setObjectName(u"importSt3Button") - - self.formLayout.setWidget(1, QFormLayout.LabelRole, self.importSt3Button) - - self.label_2 = QLabel(TabDb_Manage) - self.label_2.setObjectName(u"label_2") - - self.formLayout.setWidget(1, QFormLayout.FieldRole, self.label_2) - - self.line = QFrame(TabDb_Manage) - self.line.setObjectName(u"line") - self.line.setFrameShape(QFrame.HLine) - self.line.setFrameShadow(QFrame.Sunken) - - self.formLayout.setWidget(2, QFormLayout.SpanningRole, self.line) - - self.exportScoresButton = QPushButton(TabDb_Manage) - self.exportScoresButton.setObjectName(u"exportScoresButton") - - self.formLayout.setWidget(3, QFormLayout.LabelRole, self.exportScoresButton) - - self.label_3 = QLabel(TabDb_Manage) - self.label_3.setObjectName(u"label_3") - - self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_3) - - - self.retranslateUi(TabDb_Manage) - - QMetaObject.connectSlotsByName(TabDb_Manage) - # setupUi - - def retranslateUi(self, TabDb_Manage): - self.syncArcSongDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDbButton", None)) - self.label.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDb.description", None)) - self.importSt3Button.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3Button", None)) - self.label_2.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3.description", None)) - self.exportScoresButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoresButton", None)) - self.label_3.setText(QCoreApplication.translate("TabDb_Manage", u"exportScores.description", None)) - pass - # retranslateUi - +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'tabDb_Manage.ui' +## +## Created by: Qt User Interface Compiler version 6.5.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, 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, QFormLayout, QFrame, QLabel, + QPushButton, QSizePolicy, QWidget) + +class Ui_TabDb_Manage(object): + def setupUi(self, TabDb_Manage): + if not TabDb_Manage.objectName(): + TabDb_Manage.setObjectName(u"TabDb_Manage") + TabDb_Manage.resize(630, 528) + TabDb_Manage.setWindowTitle(u"TabDb_Manage") + self.formLayout = QFormLayout(TabDb_Manage) + self.formLayout.setObjectName(u"formLayout") + self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.syncArcSongDbButton = QPushButton(TabDb_Manage) + self.syncArcSongDbButton.setObjectName(u"syncArcSongDbButton") + + self.formLayout.setWidget(0, QFormLayout.LabelRole, self.syncArcSongDbButton) + + self.label = QLabel(TabDb_Manage) + self.label.setObjectName(u"label") + + self.formLayout.setWidget(0, QFormLayout.FieldRole, self.label) + + self.importSt3Button = QPushButton(TabDb_Manage) + self.importSt3Button.setObjectName(u"importSt3Button") + + self.formLayout.setWidget(5, QFormLayout.LabelRole, self.importSt3Button) + + self.label_2 = QLabel(TabDb_Manage) + self.label_2.setObjectName(u"label_2") + + self.formLayout.setWidget(5, QFormLayout.FieldRole, self.label_2) + + self.line = QFrame(TabDb_Manage) + self.line.setObjectName(u"line") + self.line.setFrameShape(QFrame.HLine) + self.line.setFrameShadow(QFrame.Sunken) + + self.formLayout.setWidget(6, QFormLayout.SpanningRole, self.line) + + self.exportScoresButton = QPushButton(TabDb_Manage) + self.exportScoresButton.setObjectName(u"exportScoresButton") + + self.formLayout.setWidget(7, QFormLayout.LabelRole, self.exportScoresButton) + + self.label_3 = QLabel(TabDb_Manage) + self.label_3.setObjectName(u"label_3") + + self.formLayout.setWidget(7, QFormLayout.FieldRole, self.label_3) + + self.line_2 = QFrame(TabDb_Manage) + self.line_2.setObjectName(u"line_2") + self.line_2.setFrameShape(QFrame.HLine) + self.line_2.setFrameShadow(QFrame.Sunken) + + self.formLayout.setWidget(1, QFormLayout.SpanningRole, self.line_2) + + self.importPacklistButton = QPushButton(TabDb_Manage) + self.importPacklistButton.setObjectName(u"importPacklistButton") + + self.formLayout.setWidget(2, QFormLayout.LabelRole, self.importPacklistButton) + + self.importSonglistButton = QPushButton(TabDb_Manage) + self.importSonglistButton.setObjectName(u"importSonglistButton") + + self.formLayout.setWidget(3, QFormLayout.LabelRole, self.importSonglistButton) + + self.label_4 = QLabel(TabDb_Manage) + self.label_4.setObjectName(u"label_4") + + self.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_4) + + self.label_5 = QLabel(TabDb_Manage) + self.label_5.setObjectName(u"label_5") + + self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_5) + + self.exportArcsongJsonButton = QPushButton(TabDb_Manage) + self.exportArcsongJsonButton.setObjectName(u"exportArcsongJsonButton") + + self.formLayout.setWidget(8, QFormLayout.LabelRole, self.exportArcsongJsonButton) + + self.label_6 = QLabel(TabDb_Manage) + self.label_6.setObjectName(u"label_6") + + self.formLayout.setWidget(8, QFormLayout.FieldRole, self.label_6) + + self.importApkButton = QPushButton(TabDb_Manage) + self.importApkButton.setObjectName(u"importApkButton") + + self.formLayout.setWidget(4, QFormLayout.LabelRole, self.importApkButton) + + self.label_7 = QLabel(TabDb_Manage) + self.label_7.setObjectName(u"label_7") + + self.formLayout.setWidget(4, QFormLayout.FieldRole, self.label_7) + + + self.retranslateUi(TabDb_Manage) + + QMetaObject.connectSlotsByName(TabDb_Manage) + # setupUi + + def retranslateUi(self, TabDb_Manage): + self.syncArcSongDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDbButton", None)) + self.label.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDb.description", None)) + self.importSt3Button.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3Button", None)) + self.label_2.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3.description", None)) + self.exportScoresButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoresButton", None)) + self.label_3.setText(QCoreApplication.translate("TabDb_Manage", u"exportScores.description", None)) + self.importPacklistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklistButton", None)) + self.importSonglistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglistButton", None)) + self.label_4.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklist.description", None)) + self.label_5.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglist.description", None)) + self.exportArcsongJsonButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJsonButton", None)) + self.label_6.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJson.description", None)) + self.importApkButton.setText(QCoreApplication.translate("TabDb_Manage", u"importApkButton", None)) + self.label_7.setText(QCoreApplication.translate("TabDb_Manage", u"importApk.description", None)) + pass + # retranslateUi + diff --git a/ui/implements/tabs/tabDb/tabDb_Manage.py b/ui/implements/tabs/tabDb/tabDb_Manage.py index 2de997c..9193f3e 100644 --- a/ui/implements/tabs/tabDb/tabDb_Manage.py +++ b/ui/implements/tabs/tabDb/tabDb_Manage.py @@ -1,9 +1,15 @@ import json import logging import traceback +import zipfile from arcaea_offline.database import Database -from arcaea_offline.external.arcaea.st3 import St3ScoreParser +from arcaea_offline.external.arcaea import ( + PacklistParser, + SonglistParser, + St3ScoreParser, +) +from arcaea_offline.external.arcaea.common import ArcaeaParser from arcaea_offline.external.arcsong import ArcsongDbParser from PySide6.QtCore import QDir, Slot from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget @@ -40,6 +46,93 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget): self, "Sync Error", "\n".join(traceback.format_exception(e)) ) + def importFromArcaeaParser(self, parser: ArcaeaParser, logName, path): + # extracted by sourcery + db = Database() + with db.sessionmaker() as session: + parser.write_database(session) + itemNum = len(parser.parse()) + logger.info(f"updated {itemNum} {logName} from {path}") + return itemNum + + def importPacklist(self, packlistPath) -> int: + packlistParser = PacklistParser(packlistPath) + return self.importFromArcaeaParser(packlistParser, "packs", packlistPath) + + def importSonglist(self, songlistPath) -> int: + songlistParser = SonglistParser(songlistPath) + return self.importFromArcaeaParser(songlistParser, "songs", songlistPath) + + @Slot() + def on_importPacklistButton_clicked(self): + try: + packlistFile, filter = QFileDialog.getOpenFileName( + self, "Select packlist file" + ) + + if not packlistFile: + return + + packNum = self.importPacklist(packlistFile) + QMessageBox.information( + self, None, f"Updated {packNum} packs from
{packlistFile}" + ) + except Exception as e: + logger.exception("import packlist error") + QMessageBox.critical( + self, "Import Error", "\n".join(traceback.format_exception(e)) + ) + + @Slot() + def on_importSonglistButton_clicked(self): + try: + songlistFile, filter = QFileDialog.getOpenFileName( + self, "Select songlist file" + ) + + if not songlistFile: + return + + songNum = self.importSonglist(songlistFile) + QMessageBox.information( + self, None, f"Updated {songNum} songs from
{songlistFile}" + ) + except Exception as e: + logger.exception("import songlist error") + QMessageBox.critical( + self, "Import Error", "\n".join(traceback.format_exception(e)) + ) + + @Slot() + def on_importApkButton_clicked(self): + apkFile, filter = QFileDialog.getOpenFileName( + self, "Select APK file", "", "APK File (*.apk);;*" + ) + + if not apkFile: + return + + try: + logger.info(f"Importing {apkFile}") + + with zipfile.ZipFile(apkFile) as zf: + packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist" + songlistPath = zipfile.Path(zf) / "assets" / "songs" / "songlist" + + packsNum = self.importPacklist(packlistPath) + songsNum = self.importSonglist(songlistPath) + + message = [ + f"{packsNum} packs and {songsNum} songs updated from", + str(apkFile), + ] + QMessageBox.information(self, None, "
".join(message)) + except Exception as e: + logging.exception("import apk error") + QMessageBox.critical( + self, "Import Error", "\n".join(traceback.format_exception(e)) + ) + @Slot() def on_importSt3Button_clicked(self): dbFile, filter = QFileDialog.getOpenFileName(self, "Select st3 file") @@ -77,3 +170,17 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget): ) with open(exportLocation, "w", encoding="utf-8") as f: f.write(content) + + @Slot() + def on_exportArcsongJsonButton_clicked(self): + scores = Database().generate_arcsong() + content = json.dumps(scores, ensure_ascii=False) + + exportLocation, _filter = QFileDialog.getSaveFileName( + self, + "Save your scores to...", + QDir.current().filePath("arcsong.json"), + "JSON (*.json);;*", + ) + with open(exportLocation, "w", encoding="utf-8") as f: + f.write(content) diff --git a/ui/resources/lang/en_US.ts b/ui/resources/lang/en_US.ts index 7893bda..39b5fab 100644 --- a/ui/resources/lang/en_US.ts +++ b/ui/resources/lang/en_US.ts @@ -351,58 +351,58 @@ validation Commit - + emptyScoreDialog.title Empty Score - + emptyScoreDialog.content Are you sure to commit an empty score? - - + + chartInvalidDialog.title Chart Invalid - + scoreMismatchDialog.title Possible Invalid Score - + scoreMismatchDialog.content The entered score may not match the selected chart. Commit this score anyway? - + validate.ok OK - + validate.chartInvalid Chart invalid - + validate.scoreMismatch Possible invalid score - + validate.scoreEmpty Empty score - + validate.scoreIncomplete Score incomplete, cannot verify - + validate.unknownState Unknown @@ -564,6 +564,46 @@ validation exportScores.description Export all your scores to a JSON file + + + importPacklistButton + Import packlist + + + + importSonglistButton + Import songlist + + + + importPacklist.description + Import packlist file + + + + importSonglist.description + Import songlist file + + + + exportArcsongJsonButton + Export arcsong.json + + + + exportArcsongJson.description + Export arcsong.json file + + + + importApkButton + Import APK + + + + importApk.description + Import packlist and songlist from .apk file + TabInputScore @@ -667,79 +707,6 @@ validation tab.infoLookup Chart Info Lookup - - - tab.andreal - - - - - TabTools_Andreal - - - andrealPathSelector - - - - - andrealExecutableSelector - - - - - imageType - - - - - imageVersion - - - - - exportJsonButton - - - - - generateButton - - - - - previewImage - - - - - imageType.recent - - - - - imageType.best - - - - - imageType.best30 - - - - - imageFormat - - - - - chart - - - - - imageQuality - - TabTools_InfoLookup diff --git a/ui/resources/lang/zh_CN.ts b/ui/resources/lang/zh_CN.ts index 88420dd..4083b95 100644 --- a/ui/resources/lang/zh_CN.ts +++ b/ui/resources/lang/zh_CN.ts @@ -350,58 +350,58 @@ 提交 - + emptyScoreDialog.title 分数为空 - + emptyScoreDialog.content 确定提交空分数吗? - - + + chartInvalidDialog.title 谱面无效 - + scoreMismatchDialog.title 分数可能有误 - + scoreMismatchDialog.content 输入的分数不在理论计算范围内。是否确认提交? - + validate.ok OK - + validate.chartInvalid 谱面无效 - + validate.scoreMismatch 分数可能有误 - + validate.scoreEmpty 分数为空 - + validate.scoreIncomplete 分数不完整,无法验证 - + validate.unknownState 未知 @@ -563,6 +563,46 @@ exportScores.description 将所有分数导出为 JSON 文件 + + + importPacklistButton + 导入 packlist + + + + importSonglistButton + 导入 songlist + + + + importPacklist.description + 导入 packlist 文件 + + + + importSonglist.description + 导入 songlist 文件 + + + + exportArcsongJsonButton + 导出 arcsong.json + + + + exportArcsongJson.description + 导出 arcsong.json 文件 + + + + importApkButton + 导入 APK + + + + importApk.description + 从 .apk 文件导入 packlist 和 songlist + TabInputScore @@ -666,79 +706,6 @@ tab.infoLookup 查询谱面信息 - - - tab.andreal - - - - - TabTools_Andreal - - - andrealPathSelector - - - - - andrealExecutableSelector - - - - - imageType - - - - - imageVersion - - - - - exportJsonButton - - - - - generateButton - - - - - previewImage - - - - - imageType.recent - - - - - imageType.best - - - - - imageType.best30 - - - - - imageFormat - - - - - chart - - - - - imageQuality - - TabTools_InfoLookup