2 Commits

Author SHA1 Message Date
a9d7681ee7 refactor: moving ui.extends to core
* Settings and Singletons moved
2024-06-20 21:30:21 +08:00
da3ac7acb3 using ruff as formatter & linter 2024-06-20 21:26:39 +08:00
70 changed files with 972 additions and 4034 deletions

3
.gitignore vendored
View File

@ -1,9 +1,6 @@
__debug* __debug*
.vscode/ .vscode/
# QML type cache
internal/
arcaea_offline.db arcaea_offline.db
arcaea_offline.ini arcaea_offline.ini
/data /data

View File

@ -1,12 +1,12 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0 rev: v4.4.0
hooks: hooks:
- id: end-of-file-fixer - id: end-of-file-fixer
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.13.0 rev: v0.4.9
hooks: hooks:
- id: ruff - id: ruff
args: ["--fix"] args: ["--fix"]

View File

@ -1,7 +1,5 @@
# Arcaea Offline PySide UI # Arcaea Offline PySide UI
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [ArcaeaOffline/core-ocr](https://github.com/ArcaeaOffline/core-ocr). GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [ArcaeaOffline/core-ocr](https://github.com/ArcaeaOffline/core-ocr).
## Prerequisites ## Prerequisites

91
app.py
View File

@ -1,91 +0,0 @@
import sys
from pathlib import Path
import structlog
from PySide6.QtCore import QCoreApplication, QEvent, QObject, Qt, QUrl
from PySide6.QtGui import QGuiApplication, QIcon
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQuickControls2 import QQuickStyle
from ui.resources import resources_rc # noqa: F401
from ui.theme import ThemeManager
from ui.utils import url # noqa: F401
from ui.viewmodels import overview # noqa: F401
CURRENT_DIRECTORY = Path(__file__).resolve().parent
DEFAULT_FONTS = ["微软雅黑", "Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI"]
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
class ThemeChangeEventFilter(QObject):
logger: structlog.stdlib.BoundLogger = structlog.get_logger(
tag="ThemeChangeEventFilter",
)
def __init__(self, *, themeManager: ThemeManager):
super().__init__(None)
self.themeManager = themeManager
self.scheme = self.themeManager.getCurrentScheme()
def doSomething(self) -> None:
scheme = self.themeManager.getCurrentScheme()
if scheme == self.scheme:
self.logger.debug("Ignored same scheme event (%r)", scheme)
return
self.scheme = scheme
self.themeManager.updateTheme()
self.logger.debug("something done")
def eventFilter(self, obj: QObject, event: QEvent) -> bool:
if event.type() == QEvent.Type.ThemeChange:
self.doSomething()
return False
def main() -> None:
app = QGuiApplication(sys.argv)
app.setFont(DEFAULT_FONTS)
app.setApplicationName("arcaea-offline-pyside-ui")
app.setApplicationDisplayName("Arcaea Offline")
app.setWindowIcon(QIcon(":/images/icon.png"))
engine = QQmlApplicationEngine()
themeManager = ThemeManager(parent=app)
def onThemeManagerThemeChanged():
logger.debug("App palette changed")
app.setPalette(themeManager.qPalette) # pyright: ignore[reportArgumentType]
engine.rootContext().setContextProperty("appTheme", themeManager.qmlExposer)
onThemeManagerThemeChanged()
themeManager.themeChanged.connect(onThemeManagerThemeChanged)
QQuickStyle.setStyle("Fusion")
def onEngineObjectCreated(obj: QObject | None, objUrl: QUrl) -> None:
if obj is None:
logger.critical("rootObject is None! Exiting!")
QCoreApplication.exit(-1)
engine.objectCreated.connect(
onEngineObjectCreated,
Qt.ConnectionType.QueuedConnection,
)
engine.load("ui/qmls/App.qml")
rootObject = engine.rootObjects()[0]
ef = ThemeChangeEventFilter(themeManager=themeManager)
rootObject.installEventFilter(ef)
sys.exit(app.exec())
if __name__ == "__main__":
main()

View File

@ -1,10 +0,0 @@
from PySide6.QtGui import QColor
def mixColor(source: QColor, mix: QColor, ratio: float = 0.5):
r = round((mix.red() - source.red()) * ratio + source.red())
g = round((mix.green() - source.green()) * ratio + source.green())
b = round((mix.blue() - source.blue()) * ratio + source.blue())
a = round((mix.alpha() - source.alpha()) * ratio + source.alpha())
return QColor(r, g, b, a)

View File

@ -1,12 +0,0 @@
from .conn import Database
from .init_checker import DatabaseInitCheckResult, check_db_init
from .utils import create_engine, db_path_to_sqlite_url, sqlite_url_to_db_path
__all__ = [
"check_db_init",
"create_engine",
"db_path_to_sqlite_url",
"Database",
"DatabaseInitCheckResult",
"sqlite_url_to_db_path",
]

View File

@ -1,29 +0,0 @@
from pathlib import Path
from sqlalchemy import text
from sqlalchemy.orm import sessionmaker
from core.settings import SettingsKeys, settings
from .utils import create_engine, db_path_to_sqlite_url
class Database:
def __init__(self):
if settings.stringValue(SettingsKeys.General.DatabaseType) != "file":
raise ValueError("DatabaseType is not file")
db_path = settings.stringValue(SettingsKeys.General.DatabaseConn)
if not db_path:
raise ValueError("DatabaseConn is empty")
self.engine = create_engine(db_path_to_sqlite_url(Path(db_path)))
self.sessionmaker = sessionmaker(bind=self.engine)
@property
def b30(self) -> float | None:
with self.sessionmaker() as session:
result = session.execute(
text("SELECT b30 FROM calculated_potential")
).fetchone()
return result[0] if result else None

View File

@ -1,30 +0,0 @@
from enum import Flag, auto
from pathlib import Path
from arcaea_offline.database import Database
from .utils import create_engine, db_path_to_sqlite_url
class DatabaseInitCheckResult(Flag):
NONE = 0
FILE_EXISTS = auto()
INITIALIZED = auto()
OK = FILE_EXISTS | INITIALIZED
def check_db_init(file: Path) -> DatabaseInitCheckResult:
flags = DatabaseInitCheckResult.NONE
if not file.exists():
return flags
flags |= DatabaseInitCheckResult.FILE_EXISTS
db_url = db_path_to_sqlite_url(file)
db = Database(create_engine(db_url))
if db.check_init():
flags |= DatabaseInitCheckResult.INITIALIZED
return flags

View File

@ -1,28 +0,0 @@
from pathlib import Path
from PySide6.QtCore import QSysInfo, QUrl
from sqlalchemy import Engine
from sqlalchemy import create_engine as sa_create_engine
from sqlalchemy.pool import NullPool, Pool
def db_path_to_sqlite_url(file: Path) -> QUrl:
kernelType = QSysInfo.kernelType()
# the slash count varies depending on the kernel
# https://docs.sqlalchemy.org/en/20/core/engines.html#sqlite
uri = file.resolve().as_uri()
if kernelType == "winnt":
return QUrl(uri.replace("file://", "sqlite://"))
else:
return QUrl(uri.replace("file://", "sqlite:///"))
def sqlite_url_to_db_path(url: str) -> Path:
db_file_url = url.replace("sqlite://", "file://")
return Path(QUrl(db_file_url).toLocalFile()).resolve()
def create_engine(_url: str | QUrl, pool: type[Pool] = NullPool) -> Engine:
url = _url.toString() if isinstance(_url, QUrl) else _url
return sa_create_engine(url, poolclass=pool)

View File

@ -4,8 +4,7 @@ from enum import StrEnum
class _General(StrEnum): class _General(StrEnum):
Language = "Language" Language = "Language"
DatabaseType = "DatabaseType" DatabaseUrl = "DatabaseUrl"
DatabaseConn = "DatabaseConn"
class _Ocr(StrEnum): class _Ocr(StrEnum):

View File

@ -1,10 +1,4 @@
from dataclasses import dataclass from dataclasses import dataclass
from enum import StrEnum
class GeneralDatabaseType(StrEnum):
FILE = "file"
URL = "url"
@dataclass(frozen=True) @dataclass(frozen=True)

View File

@ -13,17 +13,21 @@ import ui.resources.resources_rc # noqa: F401
from core.settings import SettingsKeys, settings from core.settings import SettingsKeys, settings
from ui.extends.shared.language import changeAppLanguage from ui.extends.shared.language import changeAppLanguage
from ui.implements.mainwindow import MainWindow from ui.implements.mainwindow import MainWindow
from ui.startup.databaseChecker import DatabaseChecker, DatabaseInitCheckResult from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult
rootLogger = logging.getLogger("root") rootLogger = logging.getLogger("root")
rootLogger.setLevel(logging.DEBUG) rootLogger.setLevel(logging.DEBUG)
rootLoggerFormatter = logging.Formatter(
"[{levelname}]{asctime}|{name}: {msg}", "%m-%d %H:%M:%S", "{"
)
def handle_exception(exc_type, exc_value, exc_traceback): def handle_exception(exc_type, exc_value, exc_traceback):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
if issubclass(exc_type, KeyboardInterrupt): if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return return
rootLogger.critical( rootLogger.critical(
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback) "Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
) )
@ -42,11 +46,6 @@ if __name__ == "__main__":
ymd = now.strftime("%Y%m%d") ymd = now.strftime("%Y%m%d")
hms = now.strftime("%H%M%S") hms = now.strftime("%H%M%S")
rootLoggerFormatter = logging.Formatter(
"[%(asctime)s/%(levelname)s] %(name)s (%(tag)s): %(message)s",
"%m-%d %H:%M:%S",
defaults={"tag": "/"},
)
rootLoggerFileHandler = logging.FileHandler( rootLoggerFileHandler = logging.FileHandler(
str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"), str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"),
encoding="utf-8", encoding="utf-8",
@ -71,10 +70,10 @@ if __name__ == "__main__":
databaseCheckResult = ( databaseCheckResult = (
databaseChecker.confirmDb() databaseChecker.confirmDb()
if settings.stringValue(SettingsKeys.General.DatabaseUrl) if settings.stringValue(SettingsKeys.General.DatabaseUrl)
else DatabaseInitCheckResult.NONE else 0
) )
if not databaseCheckResult & DatabaseInitCheckResult.INITIALIZED: if not databaseCheckResult & DatabaseCheckerResult.Initted:
result = databaseChecker.exec() result = databaseChecker.exec()
if result == QDialog.DialogCode.Accepted: if result == QDialog.DialogCode.Accepted:

View File

@ -1,3 +1,7 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project] [project]
name = "arcaea-offline-pyside-ui" name = "arcaea-offline-pyside-ui"
version = "0.3.9" version = "0.3.9"
@ -8,30 +12,14 @@ requires-python = ">=3.9"
dependencies = [ dependencies = [
"arcaea-offline==0.2.2", "arcaea-offline==0.2.2",
"arcaea-offline-ocr==0.0.99", "arcaea-offline-ocr==0.0.99",
"exif==1.6.0",
"structlog~=25.4", "PySide6==6.5.2",
"colorama~=0.4.6",
"rich~=14.2",
"exif~=1.6.0",
"PySide6==6.10.0",
"Pillow~=10.1.0",
"materialyoucolor~=2.0.10",
] ]
classifiers = [ classifiers = [
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
] ]
[project.optional-dependencies]
dev = [
"ruff>=0.14.2",
"pre-commit>=4.3.0",
"imageio",
"Nuitka~=2.7.6",
]
[project.urls] [project.urls]
"Homepage" = "https://github.com/ArcaeaOffline/client-pyside6" "Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
"Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues" "Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
@ -48,6 +36,7 @@ select = [
"I", # isort "I", # isort
"PL", # pylint "PL", # pylint
"N", # pep8-naming "N", # pep8-naming
"FBT", # flake8-boolean-trap
"A", # flake8-builtins "A", # flake8-builtins
"DTZ", # flake8-datetimez "DTZ", # flake8-datetimez
"LOG", # flake8-logging "LOG", # flake8-logging
@ -63,20 +52,7 @@ ignore = [
"N806", # non-lowercase-variable-in-function "N806", # non-lowercase-variable-in-function
"N815", # mixed-case-variable-in-class-scope "N815", # mixed-case-variable-in-class-scope
"N816", # mixed-case-variable-in-global-scope "N816", # mixed-case-variable-in-global-scope
"N999", # invalid-module-name
] ]
[tool.pyright] [tool.pyright]
ignore = ["**/__debug*.*"] ignore = ["**/__debug*.*"]
[tool.pyside6-project]
files = [
"app.py",
"ui/qmls/App.qml",
"ui/qmls/AppMain.qml",
"ui/viewmodels/overview.py",
"ui/qmls/Overview.qml",
"ui/qmls/404.qml",
]

4
requirements.dev.txt Normal file
View File

@ -0,0 +1,4 @@
black == 23.7.0
isort == 5.12.0
imageio==2.31.4
Nuitka==1.8.4

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
arcaea-offline==0.2.2
arcaea-offline-ocr==0.0.99
exif==1.6.0
Pillow==10.1.0
PySide6==6.5.2

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabOverview</class>
<widget class="QWidget" name="TabOverview">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>696</width>
<height>509</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">TabOverview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true"/>
</item>
<item>
<widget class="QLabel" name="databaseDescribeLabel">
<property name="text">
<string notr="true" extracomment="This database now have {} packs, {} songs, {} difficulties, {} chart info ({} complete) and {} scores.">...</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widget_3" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="b30Label">
<property name="font">
<font>
<pointsize>30</pointsize>
</font>
</property>
<property name="text">
<string notr="true">0.00</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string notr="true">B30</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_4" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="r10Label">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>30</pointsize>
</font>
</property>
<property name="text">
<string notr="true">--</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string notr="true">R10</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabOverview.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, QHBoxLayout, QLabel, QSizePolicy,
QVBoxLayout, QWidget)
class Ui_TabOverview(object):
def setupUi(self, TabOverview):
if not TabOverview.objectName():
TabOverview.setObjectName(u"TabOverview")
TabOverview.resize(696, 509)
TabOverview.setWindowTitle(u"TabOverview")
self.verticalLayout = QVBoxLayout(TabOverview)
self.verticalLayout.setObjectName(u"verticalLayout")
self.widget = QWidget(TabOverview)
self.widget.setObjectName(u"widget")
self.verticalLayout.addWidget(self.widget)
self.databaseDescribeLabel = QLabel(TabOverview)
self.databaseDescribeLabel.setObjectName(u"databaseDescribeLabel")
self.databaseDescribeLabel.setText(u"...")
self.verticalLayout.addWidget(self.databaseDescribeLabel)
self.widget_2 = QWidget(TabOverview)
self.widget_2.setObjectName(u"widget_2")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth())
self.widget_2.setSizePolicy(sizePolicy)
self.horizontalLayout = QHBoxLayout(self.widget_2)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.widget_3 = QWidget(self.widget_2)
self.widget_3.setObjectName(u"widget_3")
self.verticalLayout_2 = QVBoxLayout(self.widget_3)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.b30Label = QLabel(self.widget_3)
self.b30Label.setObjectName(u"b30Label")
font = QFont()
font.setPointSize(30)
self.b30Label.setFont(font)
self.b30Label.setText(u"0.00")
self.b30Label.setAlignment(Qt.AlignBottom|Qt.AlignHCenter)
self.verticalLayout_2.addWidget(self.b30Label)
self.label_2 = QLabel(self.widget_3)
self.label_2.setObjectName(u"label_2")
font1 = QFont()
font1.setPointSize(20)
self.label_2.setFont(font1)
self.label_2.setText(u"B30")
self.label_2.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
self.verticalLayout_2.addWidget(self.label_2)
self.horizontalLayout.addWidget(self.widget_3)
self.widget_4 = QWidget(self.widget_2)
self.widget_4.setObjectName(u"widget_4")
self.widget_4.setEnabled(False)
self.verticalLayout_3 = QVBoxLayout(self.widget_4)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.r10Label = QLabel(self.widget_4)
self.r10Label.setObjectName(u"r10Label")
self.r10Label.setEnabled(False)
self.r10Label.setFont(font)
self.r10Label.setText(u"--")
self.r10Label.setAlignment(Qt.AlignBottom|Qt.AlignHCenter)
self.verticalLayout_3.addWidget(self.r10Label)
self.label_4 = QLabel(self.widget_4)
self.label_4.setObjectName(u"label_4")
self.label_4.setEnabled(False)
self.label_4.setFont(font1)
self.label_4.setText(u"R10")
self.label_4.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
self.verticalLayout_3.addWidget(self.label_4)
self.horizontalLayout.addWidget(self.widget_4)
self.verticalLayout.addWidget(self.widget_2)
self.retranslateUi(TabOverview)
QMetaObject.connectSlotsByName(TabOverview)
# setupUi
def retranslateUi(self, TabOverview):
pass
# retranslateUi

View File

@ -5,6 +5,7 @@ from typing import Any, Callable, Optional, overload
from arcaea_offline.calculate import calculate_score_range from arcaea_offline.calculate import calculate_score_range
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, Score from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
from arcaea_offline_ocr.device.common import DeviceOcrResult from arcaea_offline_ocr.device.common import DeviceOcrResult
from PIL import Image from PIL import Image
from PIL.ImageQt import ImageQt from PIL.ImageQt import ImageQt
@ -139,11 +140,7 @@ class OcrQueueModel(QAbstractListModel):
return True return True
else: else:
logger.warning( logger.warning(
"%r setData at row %d with role %d and value %s rejected!", f"{repr(self)} setData at row {index.row()} with role {role} and value {value} rejected."
self,
index.row(),
role,
value,
) )
return False return False
@ -153,7 +150,7 @@ class OcrQueueModel(QAbstractListModel):
@iccOption.setter @iccOption.setter
def iccOption(self, opt: IccOption): def iccOption(self, opt: IccOption):
logger.debug("ICC option changed to %s", opt) logger.debug(f"ICC option changed to {opt}")
self.__iccOption = opt self.__iccOption = opt
@overload @overload
@ -162,7 +159,8 @@ class OcrQueueModel(QAbstractListModel):
image: str, image: str,
runnable: OcrRunnable = None, runnable: OcrRunnable = None,
process_func: Callable[[Optional[str], QImage, Any], Score] = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None,
): ... ):
...
@overload @overload
def addItem( def addItem(
@ -170,7 +168,8 @@ class OcrQueueModel(QAbstractListModel):
image: QImage, image: QImage,
runnable: OcrRunnable = None, runnable: OcrRunnable = None,
process_func: Callable[[Optional[str], QImage, Any], Score] = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None,
): ... ):
...
def addItem( def addItem(
self, self,
@ -180,7 +179,7 @@ class OcrQueueModel(QAbstractListModel):
): ):
if isinstance(image, str): if isinstance(image, str):
if image in self.imagePaths or not QFileInfo(image).exists(): if image in self.imagePaths or not QFileInfo(image).exists():
logger.warning("Attempting to add an invalid file %s", image) logger.warning(f"Attempting to add an invalid file {image}")
return return
imagePath = image imagePath = image
if self.iccOption == IccOption.TryFix: if self.iccOption == IccOption.TryFix:
@ -224,7 +223,7 @@ class OcrQueueModel(QAbstractListModel):
index = self.index(row, 0) index = self.index(row, 0)
imagePath: str = index.data(self.ImagePathRole) imagePath: str = index.data(self.ImagePathRole)
qImage: QImage = index.data(self.ImageQImageRole) qImage: QImage = index.data(self.ImageQImageRole)
logger.debug("update request: %r@row%d", result, row) logger.debug(f"update request: {result}@row{row}")
processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole) processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result) chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result)
@ -295,8 +294,8 @@ class OcrQueueModel(QAbstractListModel):
self.__items.pop(row) self.__items.pop(row)
self.endRemoveRows() self.endRemoveRows()
return return
except Exception: except Exception as e:
logger.exception("Error accepting %r", item) logger.exception(f"Error accepting {repr(item)}")
return return
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False): def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
@ -345,11 +344,17 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self.__horizontalHeaders = [ self.__horizontalHeaders = [
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"), # fmt: off
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"), QCoreApplication.translate(
"OcrTableModel", "horizontalHeader.title.select"
),
QCoreApplication.translate(
"OcrTableModel", "horizontalHeader.title.imagePreview"
),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
] # fmt: skip # fmt: on
]
def sourceModel(self) -> OcrQueueModel: def sourceModel(self) -> OcrQueueModel:
return self.__sourceModel return self.__sourceModel

View File

@ -0,0 +1,39 @@
from arcaea_offline.database import Database
from arcaea_offline.models import Chart
from arcaea_offline.searcher import Searcher
from arcaea_offline.utils.rating import rating_class_to_short_text
from PySide6.QtCore import Qt
from PySide6.QtGui import QStandardItem, QStandardItemModel
class SearchCompleterModel(QStandardItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self.searcher = Searcher()
self.db = Database()
def updateSearcherSongs(self):
with self.db.sessionmaker() as session:
self.searcher.import_songs(session)
def getSearchResult(self, kw: str):
self.clear()
songIds = self.searcher.search(kw)
charts: list[Chart] = []
for songId in songIds:
_charts = self.db.get_charts_by_song_id(songId)
_charts = sorted(_charts, key=lambda c: c.rating_class, reverse=True)
charts += _charts
for chart in charts:
displayText = (
f"{chart.title} [{rating_class_to_short_text(chart.rating_class)}]"
)
item = QStandardItem(kw)
item.setData(kw)
item.setData(displayText, Qt.ItemDataRole.UserRole + 75)
item.setData(f"{chart.song_id}, {chart.set}", Qt.ItemDataRole.UserRole + 76)
item.setData(chart, Qt.ItemDataRole.UserRole + 10)
self.appendRow(item)

View File

@ -0,0 +1,16 @@
from PySide6.QtGui import QColor
def mix_color(source_color: QColor, mix_color: QColor, mix_ratio: float = 0.5):
r = round((mix_color.red() - source_color.red()) * mix_ratio + source_color.red())
g = round(
(mix_color.green() - source_color.green()) * mix_ratio + source_color.green()
)
b = round(
(mix_color.blue() - source_color.blue()) * mix_ratio + source_color.blue()
)
a = round(
(mix_color.alpha() - source_color.alpha()) * mix_ratio + source_color.alpha()
)
return QColor(r, g, b, a)

View File

@ -1,4 +1,14 @@
from PySide6.QtCore import QObject, Signal from typing import Type
from PySide6.QtCore import QObject, QUrl, Signal
from sqlalchemy import Engine
from sqlalchemy import create_engine as sa_create_engine
from sqlalchemy.pool import NullPool, Pool
def create_engine(_url: str | QUrl, pool: Type[Pool] = NullPool) -> Engine:
url = _url.toString() if isinstance(_url, QUrl) else _url
return sa_create_engine(url, poolclass=pool)
class DatabaseUpdateSignals(QObject): class DatabaseUpdateSignals(QObject):

View File

@ -16,11 +16,13 @@ class DbB30TableModel(DbTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self._horizontalHeaders = [ self._horizontalHeaders = [
# fmt: off
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
] # fmt: skip # fmt: on
]
def syncDb(self): def syncDb(self):
self.beginResetModel() self.beginResetModel()

View File

@ -24,11 +24,13 @@ class DbScoreTableModel(DbTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self._horizontalHeaders = [ self._horizontalHeaders = [
# fmt: off
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"),
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"),
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"),
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"),
] # fmt: skip # fmt: on
]
def syncDb(self): def syncDb(self):
self.beginResetModel() self.beginResetModel()
@ -152,7 +154,7 @@ class DbScoreTableModel(DbTableModel):
self.syncDb() self.syncDb()
return True return True
except Exception: except Exception:
logger.exception("Table[Score]: Cannot remove row %s", row) logger.exception(f"Table[Score]: Cannot remove row {row}")
return False return False
def removeRow(self, row: int, parent=...): def removeRow(self, row: int, parent=...):

View File

@ -6,10 +6,10 @@ from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
from arcaea_offline_ocr.b30.shared import B30OcrResultItem from arcaea_offline_ocr.b30.shared import B30OcrResultItem
from PySide6.QtGui import QImage from PySide6.QtGui import QImage
from ui.extends.components.ocrQueue import OcrRunnable
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
from ui.extends.components.ocrQueue import OcrRunnable
class ChieriV4OcrRunnable(OcrRunnable): class ChieriV4OcrRunnable(OcrRunnable):
def __init__(self, ocr: ChieriBotV4Ocr, component): def __init__(self, ocr: ChieriBotV4Ocr, component):

View File

@ -41,7 +41,7 @@ class AndrealExecuteRunnable(QRunnable):
self.signals.completed.emit(self.jsonPath, imageBytes) self.signals.completed.emit(self.jsonPath, imageBytes)
except Exception as e: except Exception as e:
imageBytes = None imageBytes = None
logger.exception("%s error", self.__class__.__name__) logger.exception(f"{self.__class__.__name__} error")
self.signals.error.emit(self.jsonPath, str(e)) self.signals.error.emit(self.jsonPath, str(e))
finally: finally:
os.unlink(self.jsonPath) os.unlink(self.jsonPath)
@ -84,10 +84,7 @@ class AndrealHelper(QObject):
def request(self, jsonPath: str, arguments: list[str]): def request(self, jsonPath: str, arguments: list[str]):
logger.debug( logger.debug(
"%s received request jsonPath=%r arguments=%r", f"{self.__class__.__name__} received request {jsonPath=} {arguments=}"
self.__class__.__name__,
jsonPath,
arguments,
) )
runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments) runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments)
runnable.signals.error.connect(self.error) runnable.signals.error.connect(self.error)

View File

@ -2,7 +2,7 @@ from PySide6.QtCore import Slot
from PySide6.QtGui import QColor from PySide6.QtGui import QColor
from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton
from core.color import mixColor from ui.extends.shared.color import mix_color
STYLESHEET = """ STYLESHEET = """
QRadioButton {{ QRadioButton {{
@ -40,7 +40,7 @@ class RatingClassRadioButton(QRadioButton):
def setColors(self, dark_color: QColor, text_color: QColor): def setColors(self, dark_color: QColor, text_color: QColor):
self._dark_color = dark_color self._dark_color = dark_color
self._text_color = text_color self._text_color = text_color
self._mid_color = mixColor(dark_color, text_color, 0.616) self._mid_color = mix_color(dark_color, text_color, 0.616)
self.updateEffects() self.updateEffects()
def isColorsSet(self) -> bool: def isColorsSet(self) -> bool:

View File

@ -126,7 +126,7 @@ class RatingClassSelector(QWidget):
elif ratingClass in range(len(self.buttons)): elif ratingClass in range(len(self.buttons)):
button = self.buttons[ratingClass] button = self.buttons[ratingClass]
else: else:
logger.debug("Cannot select ratingClass=%s, condition check failed", ratingClass) logger.debug(f"Cannot select {ratingClass=}, condition check failed")
return return
if not button.isEnabled(): if not button.isEnabled():

View File

@ -61,22 +61,30 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
VALIDATION_ITEMS_TEXT = [ VALIDATION_ITEMS_TEXT = [
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"),
# fmt: on
], ],
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"),
# fmt: on
], ],
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"),
# fmt: on
], ],
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"),
# fmt: on,
], ],
] # fmt: skip ]
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -200,16 +208,20 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
if validate & ScoreValidateResult.ChartNotSet: if validate & ScoreValidateResult.ChartNotSet:
self.__triggerMessageBox( self.__triggerMessageBox(
"critical", "critical",
# fmt: off
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"), QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"),
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"), QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"),
) # fmt: skip # fmt: on
)
return False return False
if validate & ScoreValidateResult.ScoreIncomplete: if validate & ScoreValidateResult.ScoreIncomplete:
self.__triggerMessageBox( self.__triggerMessageBox(
"critical", "critical",
# fmt: off
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"), QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"),
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"), QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"),
) # fmt: skip # fmt: on
)
return False return False
# since validate may have multiple results # since validate may have multiple results
@ -335,8 +347,10 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
) )
if validate & ScoreValidateResult.ScoreIncompleteForValidate: if validate & ScoreValidateResult.ScoreIncompleteForValidate:
texts.append( texts.append(
# fmt: off
QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate") QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate")
) # fmt: skip # fmt: on
)
if not texts: if not texts:
texts.append( texts.append(

View File

@ -1,18 +1,14 @@
import dataclasses
import logging import logging
import re import re
from enum import IntEnum from enum import IntEnum
from typing import Any
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart from arcaea_offline.models import Chart
from arcaea_offline.searcher import Searcher from PySide6.QtCore import QModelIndex, QSignalMapper, Qt, Signal, Slot
from arcaea_offline.utils.rating import rating_class_to_short_text from PySide6.QtWidgets import QCompleter, QWidget
from PySide6.QtCore import QModelIndex, QObject, QSignalMapper, Qt, Signal, Slot
from PySide6.QtGui import QStandardItem, QStandardItemModel
from PySide6.QtWidgets import QComboBox, QCompleter, QWidget
from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector
from ui.extends.components.songIdSelector import SearchCompleterModel
from ui.extends.shared.database import databaseUpdateSignals from ui.extends.shared.database import databaseUpdateSignals
from ui.extends.shared.delegates.descriptionDelegate import DescriptionDelegate from ui.extends.shared.delegates.descriptionDelegate import DescriptionDelegate
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
@ -25,168 +21,6 @@ class SongIdSelectorMode(IntEnum):
Chart = 1 Chart = 1
# region logics
@dataclasses.dataclass
class _ComboBoxItem:
text: str
userData: Any
additionalData: dict[int, Any]
def apply(self, comboBox: QComboBox):
comboBox.addItem(self.text, self.userData)
index = comboBox.findText(self.text)
if index > -1:
for role, value in self.additionalData.items():
comboBox.setItemData(index, value, role)
class SearchCompleterModel(QStandardItemModel):
ChartRole = Qt.ItemDataRole.UserRole + 10
def __init__(self, parent=None):
super().__init__(parent)
self.searcher = Searcher()
self.db = Database()
def updateSearcherSongs(self):
with self.db.sessionmaker() as session:
self.searcher.import_songs(session)
def getSearchResult(self, kw: str):
self.clear()
songIds = self.searcher.search(kw)
charts: list[Chart] = []
for songId in songIds:
_charts = self.db.get_charts_by_song_id(songId)
_charts = sorted(_charts, key=lambda c: c.rating_class, reverse=True)
charts += _charts
for chart in charts:
displayText = (
f"{chart.title} [{rating_class_to_short_text(chart.rating_class)}]"
)
item = QStandardItem(kw)
item.setData(kw)
item.setData(displayText, DescriptionDelegate.MainTextRole)
item.setData(
f"{chart.song_id}, {chart.set}", DescriptionDelegate.DescriptionTextRole
)
item.setData(chart, self.ChartRole)
self.appendRow(item)
class SongIdSelectorViewModel(QObject):
packComboBoxItemsReady = Signal()
songIdComboBoxItemsReady = Signal()
def __init__(self, parent=None, *, db: Database):
super().__init__(parent)
if not isinstance(db, Database):
raise TypeError(
"`db` should be an instance of arcaea_offline.database.Database"
)
self.__db: Database = None # type: ignore
self.__mode = SongIdSelectorMode.SongId
self.__packComboBoxItems: list[_ComboBoxItem] = []
self.__songIdComboBoxItems: list[_ComboBoxItem] = []
self.setDatabase(db)
@property
def packComboBoxItems(self):
return self.__packComboBoxItems
@property
def songIdComboBoxItems(self):
return self.__songIdComboBoxItems
@property
def mode(self):
return self.__mode
@mode.setter
def mode(self, value):
if not isinstance(value, SongIdSelectorMode):
raise TypeError("value is not SongIdSelectorMode")
self.__mode = value
def setDatabase(self, db: Database):
self.__db = db
def updatePackComboBoxItems(self):
packs = self.__db.get_packs()
self.__packComboBoxItems.clear()
for pack in packs:
if re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = self.__db.get_pack(basePackId).name # type: ignore
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
self.__packComboBoxItems.append(
_ComboBoxItem(
text=f"{packName} ({pack.id})",
userData=pack.id,
additionalData={
DescriptionDelegate.MainTextRole: packName,
DescriptionDelegate.DescriptionTextRole: pack.id,
},
)
)
self.packComboBoxItemsReady.emit()
def updateSongIdComboBoxItems(self, packId: str):
self.__songIdComboBoxItems.clear()
items = []
if self.mode == SongIdSelectorMode.SongId:
items = self.__db.get_songs_by_pack_id(packId)
elif self.mode == SongIdSelectorMode.Chart:
items = self.__db.get_charts_by_pack_id(packId)
else:
assert not "reachable"
insertedSongIds = []
for item in items:
if self.mode == SongIdSelectorMode.SongId:
itemId = item.id # type: ignore
elif self.mode == SongIdSelectorMode.Chart:
itemId = item.song_id # type: ignore
else:
continue
if itemId not in insertedSongIds:
self.__songIdComboBoxItems.append(
_ComboBoxItem(
text=f"{item.title} ({itemId})",
userData=itemId,
additionalData={
DescriptionDelegate.MainTextRole: item.title,
DescriptionDelegate.DescriptionTextRole: itemId,
},
)
)
insertedSongIds.append(itemId)
self.songIdComboBoxItemsReady.emit()
# endregion
class SongIdSelector(Ui_SongIdSelector, QWidget): class SongIdSelector(Ui_SongIdSelector, QWidget):
valueChanged = Signal() valueChanged = Signal()
quickSearchActivated = Signal(Chart) quickSearchActivated = Signal(Chart)
@ -199,10 +33,6 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.languageChangeEventFilter = LanguageChangeEventFilter(self) self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter) self.installEventFilter(self.languageChangeEventFilter)
self.vm = SongIdSelectorViewModel(self, db=self.db)
self.vm.packComboBoxItemsReady.connect(self.fillPackComboBox)
self.vm.songIdComboBoxItemsReady.connect(self.fillSongIdComboBox)
# quick switch bindings # quick switch bindings
self.quickSwitchSignalMapper = QSignalMapper(self) self.quickSwitchSignalMapper = QSignalMapper(self)
self.previousPackageButton.clicked.connect(self.quickSwitchSignalMapper.map) self.previousPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
@ -245,7 +75,6 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
databaseUpdateSignals.songAddOrDelete.connect(self.updateDatabase) databaseUpdateSignals.songAddOrDelete.connect(self.updateDatabase)
def setMode(self, mode: SongIdSelectorMode): def setMode(self, mode: SongIdSelectorMode):
self.vm.mode = mode
self.mode = mode self.mode = mode
@Slot(str) @Slot(str)
@ -287,7 +116,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
pack = self.packComboBox.currentData() pack = self.packComboBox.currentData()
songId = self.songIdComboBox.currentData() songId = self.songIdComboBox.currentData()
self.vm.updatePackComboBoxItems() self.fillPackComboBox()
if pack: if pack:
self.selectPack(pack) self.selectPack(pack)
@ -296,24 +125,58 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
def fillPackComboBox(self): def fillPackComboBox(self):
self.packComboBox.clear() self.packComboBox.clear()
packs = self.db.get_packs()
for item in self.vm.packComboBoxItems: for pack in packs:
item.apply(self.packComboBox) if isAppendPack := re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = self.db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
self.packComboBox.addItem(f"{packName} ({pack.id})", pack.id)
row = self.packComboBox.count() - 1
self.packComboBox.setItemData(
row, packName, DescriptionDelegate.MainTextRole
)
self.packComboBox.setItemData(
row, pack.id, DescriptionDelegate.DescriptionTextRole
)
self.packComboBox.setCurrentIndex(-1) self.packComboBox.setCurrentIndex(-1)
def fillSongIdComboBox(self): def fillSongIdComboBox(self):
self.songIdComboBox.clear() self.songIdComboBox.clear()
if packId := self.packComboBox.currentData():
if self.mode == SongIdSelectorMode.SongId:
items = self.db.get_songs_by_pack_id(packId)
elif self.mode == SongIdSelectorMode.Chart:
items = self.db.get_charts_by_pack_id(packId)
else:
raise ValueError("Unknown SongIdSelectorMode.")
insertedSongIds = []
for item in items:
if self.mode == SongIdSelectorMode.SongId:
itemId = item.id
elif self.mode == SongIdSelectorMode.Chart:
itemId = item.song_id
else:
continue
for item in self.vm.songIdComboBoxItems: if itemId not in insertedSongIds:
item.apply(self.songIdComboBox) self.songIdComboBox.addItem(f"{item.title} ({itemId})", itemId)
insertedSongIds.append(itemId)
row = self.songIdComboBox.count() - 1
self.songIdComboBox.setItemData(
row, item.title, DescriptionDelegate.MainTextRole
)
self.songIdComboBox.setItemData(
row, itemId, DescriptionDelegate.DescriptionTextRole
)
self.songIdComboBox.setCurrentIndex(-1) self.songIdComboBox.setCurrentIndex(-1)
@Slot() @Slot()
def on_packComboBox_currentIndexChanged(self): def on_packComboBox_currentIndexChanged(self):
if packId := self.packComboBox.currentData(): self.fillSongIdComboBox()
self.vm.updateSongIdComboBoxItems(packId)
@Slot(str) @Slot(str)
def on_searchLineEdit_textChanged(self, text: str): def on_searchLineEdit_textChanged(self, text: str):
@ -326,10 +189,10 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
packIdIndex = self.packComboBox.findData(packId) packIdIndex = self.packComboBox.findData(packId)
if packIdIndex > -1: if packIdIndex > -1:
self.packComboBox.setCurrentIndex(packIdIndex) self.packComboBox.setCurrentIndex(packIdIndex)
self.vm.updateSongIdComboBoxItems(packId) self.fillSongIdComboBox()
return True return True
else: else:
logger.warning("Attempting to select an unknown pack [%s]", packId) logger.warning(f'Attempting to select an unknown pack "{packId}"')
return False return False
def selectSongId(self, songId: str) -> bool: def selectSongId(self, songId: str) -> bool:
@ -339,8 +202,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
return True return True
else: else:
logger.warning( logger.warning(
"Attempting to select an unknown song [%s], maybe try selecting a pack first?", f'Attempting to select an unknown song "{songId}", maybe try selecting a pack first?'
songId,
) )
return False return False
@ -351,7 +213,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
@Slot(QModelIndex) @Slot(QModelIndex)
def searchCompleterSetSelection(self, index: QModelIndex): def searchCompleterSetSelection(self, index: QModelIndex):
chart: Chart = index.data(SearchCompleterModel.ChartRole) chart: Chart = index.data(Qt.ItemDataRole.UserRole + 10)
self.selectChart(chart) self.selectChart(chart)
self.quickSearchActivated.emit(chart) self.quickSearchActivated.emit(chart)

View File

@ -163,15 +163,19 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
QMessageBox.critical( QMessageBox.critical(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"), QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
) # fmt: skip # fmt: on
)
return return
if not self.constantLineEdit.hasAcceptableInput(): if not self.constantLineEdit.hasAcceptableInput():
QMessageBox.critical( QMessageBox.critical(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"), QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
) # fmt: skip # fmt: on
)
return return
constant = int(self.constantLineEdit.text()) constant = int(self.constantLineEdit.text())
@ -198,8 +202,10 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
QMessageBox.critical( QMessageBox.critical(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"), QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
) # fmt: skip # fmt: on
)
return return
chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class) chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class)
@ -207,10 +213,12 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
result = QMessageBox.warning( result = QMessageBox.warning(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"), QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
# fmt: on
QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No, QMessageBox.StandardButton.No,
) # fmt: skip )
if result == QMessageBox.StandardButton.Yes: if result == QMessageBox.StandardButton.Yes:
with self.db.sessionmaker() as session: with self.db.sessionmaker() as session:
session.delete(chartInfo) session.delete(chartInfo)

View File

@ -91,7 +91,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
session.commit() session.commit()
databaseUpdateSignals.songAddOrDelete.emit() databaseUpdateSignals.songAddOrDelete.emit()
itemNum = len([item for item in parser.parse() if isinstance(item, instance)]) itemNum = len([item for item in parser.parse() if isinstance(item, instance)])
logger.info("updated %d %s from %s", itemNum, logName, path) logger.info(f"updated {itemNum} {logName} from {path}")
return itemNum return itemNum
def importPacklist(self, packlistPath): def importPacklist(self, packlistPath):
@ -161,7 +161,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
return return
try: try:
logger.info("Importing %s", apkFile) logger.info(f"Importing {apkFile}")
with zipfile.ZipFile(apkFile) as zf: with zipfile.ZipFile(apkFile) as zf:
packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist" packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist"
@ -193,9 +193,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
db = Database() db = Database()
parser = St3ScoreParser(dbFile) parser = St3ScoreParser(dbFile)
logger.info( logger.info(
"Got %d items from %s, writing into database...", f"Got {len(parser.parse())} items from {dbFile}, writing into database..."
len(parser.parse()),
dbFile,
) )
with db.sessionmaker() as session: with db.sessionmaker() as session:
parser.write_database(session) parser.write_database(session)
@ -220,9 +218,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
db = Database() db = Database()
parser = ArcaeaOnlineParser(apiResultFile) parser = ArcaeaOnlineParser(apiResultFile)
logger.info( logger.info(
"Got %d items from %s, writing into database...", f"Got {len(parser.parse())} items from {apiResultFile}, writing into database..."
len(parser.parse()),
apiResultFile,
) )
with db.sessionmaker() as session: with db.sessionmaker() as session:
parser.write_database(session) parser.write_database(session)

View File

@ -154,17 +154,23 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate) self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
self.quickSelect_comboBox.addItem( self.quickSelect_comboBox.addItem(
# fmt: off
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"), QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
# fmt: on
QuickSelectComboBoxValues.ID_EARLIER QuickSelectComboBoxValues.ID_EARLIER
) # fmt: skip )
self.quickSelect_comboBox.addItem( self.quickSelect_comboBox.addItem(
# fmt: off
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"), QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
# fmt: on
QuickSelectComboBoxValues.DATE_EARLIER QuickSelectComboBoxValues.DATE_EARLIER
) # fmt: skip )
self.quickSelect_comboBox.addItem( self.quickSelect_comboBox.addItem(
# fmt: off
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"), QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
# fmt: on
QuickSelectComboBoxValues.COLUMNS_INTEGRAL QuickSelectComboBoxValues.COLUMNS_INTEGRAL
) # fmt: skip )
def getQueryColumns(self): def getQueryColumns(self):
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class] columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
@ -285,12 +291,12 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
confirm = QMessageBox.warning( confirm = QMessageBox.warning(
self, self,
None, None,
QCoreApplication.translate( # fmt: off
"TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}" QCoreApplication.translate("TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}").format(len(selectedScores)),
).format(len(selectedScores)), # fmt: on
QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No, QMessageBox.StandardButton.No,
) # fmt: skip )
if confirm != QMessageBox.StandardButton.Yes: if confirm != QMessageBox.StandardButton.Yes:
return return
@ -304,11 +310,12 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
@Slot() @Slot()
def on_scan_scanButton_clicked(self): def on_scan_scanButton_clicked(self):
if len(self.getQueryColumns()) <= 2: if len(self.getQueryColumns()) <= 2:
message = QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content") # fmt: skip
result = QMessageBox.warning( result = QMessageBox.warning(
self, self,
None, None,
message, # fmt: off
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content"),
# fmt: on
QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No, QMessageBox.StandardButton.No,
) )

View File

@ -0,0 +1,55 @@
import logging
from arcaea_offline.database import Database
from PySide6.QtCore import QCoreApplication
from PySide6.QtGui import QShowEvent
from PySide6.QtWidgets import QWidget
from ui.designer.tabs.tabOverview_ui import Ui_TabOverview
from ui.extends.shared.language import LanguageChangeEventFilter
logger = logging.getLogger(__name__)
class TabOverview(Ui_TabOverview, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.db = Database()
def showEvent(self, event: QShowEvent) -> None:
self.updateOverview()
return super().showEvent(event)
def updateOverview(self):
try:
b30 = self.db.get_b30() or 0.00
self.b30Label.setText(str(f"{b30:.3f}"))
except Exception:
logger.exception("Cannot get b30:")
self.b30Label.setText("ERR")
try:
self.databaseDescribeLabel.setText(
self.describeFormatString.format(
self.db.count_packs(),
self.db.count_songs(),
self.db.count_difficulties(),
self.db.count_chart_infos(),
self.db.count_complete_chart_infos(),
self.db.count_scores(),
)
)
except Exception:
logger.exception("Cannot update overview:")
self.databaseDescribeLabel.setText("ERR")
def retranslateUi(self, *args):
super().retranslateUi(self)
# fmt: off
self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}")
# fmt: on

View File

@ -131,8 +131,11 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
@Slot() @Slot()
def on_imageTypeWhatIsThisButton_clicked(self): def on_imageTypeWhatIsThisButton_clicked(self):
message = QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description") # fmt: skip QMessageBox.information(
QMessageBox.information(self, None, message) self,
None,
QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description"),
) # fmt: skip
def imageFormat(self): def imageFormat(self):
buttonId = self.imageFormatButtonGroup.checkedId() buttonId = self.imageFormatButtonGroup.checkedId()

View File

@ -90,8 +90,10 @@ class PlayRatingCalculatorDialog(QDialog):
self.acceptButton = QPushButton(self) self.acceptButton = QPushButton(self)
self.acceptButton.setText( self.acceptButton.setText(
# fmt: off
QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton") QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton")
) # fmt: skip # fmt: on
)
self.acceptButton.setEnabled(False) self.acceptButton.setEnabled(False)
self.verticalLayout.addWidget(self.acceptButton) self.verticalLayout.addWidget(self.acceptButton)

View File

@ -1,22 +0,0 @@
import QtQuick
import QtQuick.Controls
Page {
Column {
anchors.centerIn: parent
width: parent.width
Label {
width: parent.width
text: '?'
font.pointSize: 50
horizontalAlignment: Qt.AlignCenter
}
Label {
width: parent.width
text: 'Placeholder page'
horizontalAlignment: Qt.AlignCenter
}
}
}

View File

@ -1,63 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "./DatabaseChecker" as DatabaseChecker
ApplicationWindow {
visible: true
width: 800
height: 600
SystemPalette {
id: systemPaletteActive
colorGroup: SystemPalette.Active
}
SystemPalette {
id: systemPaletteDisabled
colorGroup: SystemPalette.Disabled
}
palette {
accent: systemPaletteActive.accent
alternateBase: systemPaletteActive.alternateBase
base: systemPaletteActive.base
button: systemPaletteActive.button
buttonText: systemPaletteActive.buttonText
dark: systemPaletteActive.dark
highlight: systemPaletteActive.highlight
highlightedText: systemPaletteActive.highlightedText
light: systemPaletteActive.light
mid: systemPaletteActive.mid
midlight: systemPaletteActive.midlight
placeholderText: systemPaletteActive.placeholderText
shadow: systemPaletteActive.shadow
text: systemPaletteActive.text
window: systemPaletteActive.window
windowText: systemPaletteActive.windowText
disabled {
button: systemPaletteDisabled.button
buttonText: systemPaletteDisabled.buttonText
}
inactive {
button: systemPaletteDisabled.button
buttonText: systemPaletteDisabled.buttonText
}
}
StackLayout {
id: stackLayout
anchors.fill: parent
currentIndex: 0
DatabaseChecker.Index {
onReady: parent.currentIndex = 1
}
AppMain {}
}
}

View File

@ -1,71 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RowLayout {
id: layout
spacing: 5
ListModel {
id: navListModel
ListElement {
_id: 'home'
label: 'Overview'
qmlSource: 'Overview.qml'
}
ListElement {
_id: 'database'
label: 'Database'
qmlSource: '404.qml'
}
}
ListView {
id: navListView
Layout.preferredWidth: 200
Layout.fillHeight: true
model: navListModel
focus: true
delegate: Item {
id: navListItem
required property int index
required property string label
width: parent.width
height: 30
MouseArea {
anchors.fill: parent
onClicked: () => {
navListView.currentIndex = navListItem.index;
}
}
Label {
anchors.margins: 5
anchors.fill: parent
text: parent.label
}
}
highlight: Rectangle {
width: parent.width
height: 30
color: "#FFFF88"
y: ListView.view.currentItem.y
}
}
Loader {
Layout.preferredWidth: 500
Layout.fillWidth: true
Layout.fillHeight: true
source: navListView.currentIndex > -1 ? navListModel.get(navListView.currentIndex).qmlSource : '404.qml'
}
}

View File

@ -1,25 +0,0 @@
import QtQuick
import QtQuick.Dialogs
import internal.ui.utils
SelectorBase {
id: base
FolderDialog {
id: folderDialog
selectedFolder: base.directoryUrl
onAccepted: {
base.directoryUrl = this.selectedFolder;
this.currentFolder = this.selectedFolder;
}
}
property alias directoryUrl: base.url
shouldAcceptUrl: url => UrlUtils.isDir(url)
onBrowseButtonClicked: {
folderDialog.open();
}
placeholderText: '<font color="gray">Select a directory…</font>'
}

View File

@ -1,33 +0,0 @@
import QtQuick
import QtQuick.Dialogs
import internal.ui.utils
SelectorBase {
id: base
FileDialog {
id: fileDialog
onAccepted: {
base.url = this.selectedFile;
}
}
function isFileUrlValid(url: url): bool {
return url.toString().startsWith("file://");
}
property alias fileUrl: base.url
onFileUrlChanged: {
if (isFileUrlValid(fileUrl)) {
fileDialog.selectedFile = fileUrl;
fileDialog.currentFolder = UrlUtils.parent(fileUrl);
}
}
shouldAcceptUrl: url => UrlUtils.isFile(url)
onBrowseButtonClicked: {
fileDialog.open();
}
placeholderText: '<font color="gray">Select a file…</font>'
}

View File

@ -1,13 +0,0 @@
import QtQuick.Controls
import QtQuick.Layouts
Label {
Layout.topMargin: 7
Layout.bottomMargin: 10
anchors.topMargin: 7
anchors.bottomMargin: 10
font.pointSize: 14
font.bold: true
}

View File

@ -1,64 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import internal.ui.utils
RowLayout {
id: root
required property var shouldAcceptUrl // function (url)
signal browseButtonClicked
property string placeholderText: '<font color="gray">…</font>'
required property url url
onUrlChanged: {
Qt.callLater(() => {
updateLabel();
});
}
function updateLabel(): void {
urlLabel.text = url.toString().length > 0 ? UrlFormatUtils.toLocalFile(url) : root.placeholderText;
}
spacing: 2
Label {
id: urlLabel
Layout.fillWidth: true
text: root.placeholderText
DropArea {
anchors.fill: parent
onEntered: drag => {
if (!drag.hasUrls || drag.urls.length !== 1) {
drag.accepted = false;
return false;
}
const url = drag.urls[0];
const shouldAccept = root.shouldAcceptUrl(url);
if (!shouldAccept) {
drag.accepted = false;
return false;
}
urlLabel.text = `<font color="gray">Drop "<font color="text">${UrlUtils.name(url)}</font>"?</font>`;
}
onExited: {
root.updateLabel();
}
onDropped: drop => {
root.url = drop.urls[0];
root.updateLabel();
}
}
}
Button {
text: "Browse"
onClicked: root.browseButtonClicked()
}
}

View File

@ -1,6 +0,0 @@
module Components
internal SelectorBase SelectorBase.qml
DirectorySelector 1.0 DirectorySelector.qml
FileSelector 1.0 FileSelector.qml
SectionTitle 1.0 SectionTitle.qml

View File

@ -1,56 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "../Components"
ColumnLayout {
id: root
signal confirm
required property url directoryUrl
required property string filename
GridLayout {
columns: 2
Label {
text: "Directory"
}
DirectorySelector {
Layout.fillWidth: true
directoryUrl: root.directoryUrl
onDirectoryUrlChanged: {
root.directoryUrl = this.directoryUrl;
}
}
Label {
text: "Filename"
}
TextField {
Layout.fillWidth: true
text: root.filename
placeholderText: 'Please enter…'
onEditingFinished: {
root.filename = this.text;
}
onAccepted: {
confirmButton.click();
}
}
}
Button {
id: confirmButton
Layout.alignment: Qt.AlignRight
text: 'Confirm'
onClicked: root.confirm()
}
}

View File

@ -1,31 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Dialog {
id: root
required property string databaseUrl
padding: 10
title: qsTr('Confirm Database Connection')
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
Overlay.modal: Rectangle {
color: Qt.alpha('gray', 0.2)
}
ColumnLayout {
Label {
text: 'The connection below will be saved to settings. Confirm?'
}
Pane {
Label {
id: databaseUrlLabel
text: root.databaseUrl
}
}
}
}

View File

@ -1,34 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Dialog {
id: root
required property string errorTitle
required property string errorMessage
padding: 10
title: qsTr('Error')
standardButtons: Dialog.Ok
modal: true
Overlay.modal: Rectangle {
color: Qt.alpha('darkgray', 0.5)
}
ColumnLayout {
spacing: 5
Label {
font.pointSize: 12
font.bold: true
text: root.errorTitle
}
Label {
text: root.errorMessage
}
}
}

View File

@ -1,13 +0,0 @@
import QtQuick
import QtQuick.Layouts
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
Layout.topMargin: 5
Layout.bottomMargin: 5
color: "lightgray"
opacity: 0.5
}

View File

@ -1,124 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import internal.ui.vm 1.0
import "../Components"
Page {
id: root
property bool showConfirmConnectionDialog: false
signal ready
function confirm(source: string): void {
if (!source)
throw new Error("source is required");
const shouldConfirm = (source === 'continueButton' && vm.canConfirmSilently) || source === 'dialog';
if (shouldConfirm) {
vm.confirmCurrentConnection();
root.ready();
} else {
root.showConfirmConnectionDialog = true;
}
}
DatabaseInitViewModel {
id: vm
onSelectFileUrlChanged: {
selectOrCreate.selectFileUrl = this.selectFileUrl;
}
onDirectoryUrlChanged: {
selectOrCreate.directoryUrl = this.directoryUrl;
}
onFilenameChanged: {
selectOrCreate.filename = this.filename;
}
onDatabaseUrlChanged: {
confirmConnectionDialog.databaseUrl = this.databaseUrl;
}
onDatabaseInfoChanged: {
databaseInfo.info = this.databaseInfo;
}
onCanContinueChanged: {
continueButton.enabled = this.canContinue;
}
}
Page {
padding: 10
anchors.fill: parent
Dialog_ConfirmConnection {
id: confirmConnectionDialog
anchors.centerIn: parent
visible: root.showConfirmConnectionDialog
onAccepted: root.confirm('dialog')
onClosed: root.showConfirmConnectionDialog = false
databaseUrl: vm.databaseUrl
}
ColumnLayout {
width: parent.width
spacing: 2
SectionTitle {
text: qsTr('Select or Create Database')
}
Section_SelectOrCreate {
id: selectOrCreate
selectFileUrl: vm.selectFileUrl
createDirectoryUrl: vm.directoryUrl
createFilename: vm.filename
onUiModeChanged: uiMode => vm.uiMode = uiMode
onSelectFileUrlChanged: {
vm.selectFileUrl = selectFileUrl;
}
onCreateDirectoryUrlChanged: {
vm.directoryUrl = createDirectoryUrl;
}
onCreateFilenameChanged: {
vm.filename = createFilename;
}
onConfirmCreate: {
vm.loadDatabaseInfo();
}
}
HorizontalDivider {}
SectionTitle {
text: 'Database Status'
}
Section_DatabaseInfo {
id: databaseInfo
info: vm.databaseInfo
}
}
footer: Pane {
padding: 5
RowLayout {
Button {
id: continueButton
Layout.alignment: Qt.AlignHCenter
text: qsTr('Continue')
enabled: vm.canContinue
onClicked: root.confirm('continueButton')
}
}
}
}
}

View File

@ -1,137 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ColumnLayout {
id: root
property var info: {
'url': undefined,
'error': {
'title': undefined,
'message': undefined
},
'initialized': undefined,
'version': undefined
}
property bool showErrorDialog: false
function hasError(): bool {
return info?.error?.title !== undefined || info?.error?.message !== undefined;
}
function displayText(value): string {
return value ?? '-';
}
function displayBool(value): string {
if (value === undefined)
return '-';
return value ? `<font color="${appTheme.success}">Yes</font>` : `<font color="${appTheme.error}">No</font>`;
}
component LabelLabel: Label {
Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
font.pointSize: 10
}
SystemPalette {
id: palette
}
Dialog_Error {
parent: Overlay.overlay
anchors.centerIn: parent
visible: root.hasError() && root.showErrorDialog
errorTitle: root.displayText(root.info?.error?.title)
errorMessage: root.displayText(root.info?.error?.message)
onClosed: root.showErrorDialog = false
}
Pane {
clip: true
background: Rectangle {
color: Qt.darker(Qt.alpha(palette.window, 0.9), 0.2)
}
// Layout.preferredHeight: root.hasError() ? this.implicitHeight : 0
Layout.preferredHeight: 0
Behavior on Layout.preferredHeight {
PropertyAnimation {
duration: 300
easing.type: Easing.InOutCubic
}
}
ColumnLayout {
anchors.fill: parent
Label {
font.bold: true
text: root.displayText(root.info.error?.title)
}
Label {
text: root.displayText(root.info.error?.message)
}
}
}
GridLayout {
columns: 2
columnSpacing: 10
LabelLabel {
text: 'Connection'
}
Label {
text: root.info.url
}
LabelLabel {
text: 'Initialized'
}
Label {
text: root.displayBool(root.info?.initialized)
}
LabelLabel {
text: 'Version'
}
Label {
text: root.displayText(root.info?.version)
}
Column {
Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
LabelLabel {
text: 'Error'
}
ToolButton {
Layout.preferredWidth: root.hasError() ? this.implicitWidth : 0
Behavior on Layout.preferredWidth {
PropertyAnimation {
duration: 300
easing.type: Easing.InOutCubic
}
}
text: '[?]'
onClicked: root.showErrorDialog = true
}
}
Label {
Layout.alignment: Qt.AlignBaseline
text: root.displayText(root.info?.error?.title)
}
}
}

View File

@ -1,69 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "../Components"
ColumnLayout {
id: root
property alias selectFileUrl: fileSelector.url
property alias createDirectoryUrl: fileCreator.directoryUrl
property alias createFilename: fileCreator.filename
signal uiModeChanged(string value)
signal confirmCreate
ListModel {
id: uiModeModel
ListElement {
value: 'select'
}
ListElement {
value: 'create'
}
}
TabBar {
id: tabBar
Layout.fillWidth: true
TabButton {
text: qsTr("Select Existing")
width: implicitWidth + 10
}
TabButton {
text: qsTr("Create New File")
width: implicitWidth + 10
}
onCurrentIndexChanged: {
const idx = this.currentIndex;
root.uiModeChanged(uiModeModel.get(idx).value);
}
}
StackLayout {
currentIndex: tabBar.currentIndex
Layout.fillWidth: true
Layout.preferredHeight: children[currentIndex].height
Behavior on Layout.preferredHeight {
PropertyAnimation {
duration: 300
easing.type: Easing.InOutCubic
}
}
clip: true
FileSelector {
id: fileSelector
}
DatabaseFileCreator {
id: fileCreator
onConfirm: root.confirmCreate()
}
}
}

View File

@ -1,62 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import internal.ui.vm 1.0
Page {
id: root
property alias b30: vm.b30
OverviewViewModel {
id: vm
}
component Display: RowLayout {
required property string label
required property string value
spacing: 5
// implicitHeight: valueText.implicitHeight
Label {
text: parent.label
Layout.alignment: Qt.AlignBaseline
}
Label {
id: valueText
text: parent.value
font.pointSize: 18
Layout.alignment: Qt.AlignBaseline
}
}
RowLayout {
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
Display {
label: 'B30'
value: root.b30 >= 0 ? root.b30.toFixed(3) : 'N/A'
}
Display {
label: 'R10'
value: 'Not supported'
}
}
Button {
Layout.alignment: Qt.AlignBottom
// TODO: icon
text: 'Reload'
onClicked: vm.reload()
}
}
}

View File

@ -19,8 +19,5 @@
<file>lang/zh_CN.qm</file> <file>lang/zh_CN.qm</file>
<file>lang/en_US.qm</file> <file>lang/en_US.qm</file>
<file>themes/default.json</file>
<file>themes/tempest.json</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -1,427 +0,0 @@
{
"//url": "http://material-foundation.github.io/material-theme-builder/?primary=%234E486C&custom%3ASuccess=%2300C555&colorMatch=true",
"name": "default",
"description": "TYPE: CUSTOM\nMaterial Theme Builder export",
"seed": "#4E486C",
"coreColors": {
"primary": "#4E486C"
},
"extendedColors": [
{
"name": "Success",
"color": "#00C555",
"description": "",
"harmonized": true
}
],
"schemes": {
"light": {
"primary": "#373154",
"surfaceTint": "#605A7F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#4E486C",
"onPrimaryContainer": "#C0B8E3",
"secondary": "#605C6C",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#E3DDF0",
"onSecondaryContainer": "#646071",
"tertiary": "#4F2B40",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#684157",
"onTertiaryContainer": "#E3B0CA",
"error": "#BA1A1A",
"onError": "#FFFFFF",
"errorContainer": "#FFDAD6",
"onErrorContainer": "#93000A",
"background": "#FDF8FC",
"onBackground": "#1C1B1E",
"surface": "#FDF8FC",
"onSurface": "#1C1B1E",
"surfaceVariant": "#E6E1EA",
"onSurfaceVariant": "#48464D",
"outline": "#79767E",
"outlineVariant": "#C9C5CE",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#313033",
"inverseOnSurface": "#F4EFF3",
"inversePrimary": "#C9C1EC",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#1C1738",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#484266",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#1C1A27",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#484554",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#301024",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#613B51",
"surfaceDim": "#DDD9DC",
"surfaceBright": "#FDF8FC",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F7F2F6",
"surfaceContainer": "#F1ECF0",
"surfaceContainerHigh": "#EBE7EA",
"surfaceContainerHighest": "#E6E1E5"
},
"light-medium-contrast": {
"primary": "#373154",
"surfaceTint": "#605A7F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#4E486C",
"onPrimaryContainer": "#EDE7FF",
"secondary": "#373443",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#6F6B7B",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#4E2B40",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#684157",
"onTertiaryContainer": "#FFE4EF",
"error": "#740006",
"onError": "#FFFFFF",
"errorContainer": "#CF2C27",
"onErrorContainer": "#FFFFFF",
"background": "#FDF8FC",
"onBackground": "#1C1B1E",
"surface": "#FDF8FC",
"onSurface": "#121113",
"surfaceVariant": "#E6E1EA",
"onSurfaceVariant": "#37353D",
"outline": "#545159",
"outlineVariant": "#6F6C74",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#313033",
"inverseOnSurface": "#F4EFF3",
"inversePrimary": "#C9C1EC",
"primaryFixed": "#6F688E",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#565075",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#6F6B7B",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#565363",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#8B6078",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#71495F",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#C9C5C9",
"surfaceBright": "#FDF8FC",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F7F2F6",
"surfaceContainer": "#EBE7EA",
"surfaceContainerHigh": "#E0DCDF",
"surfaceContainerHighest": "#D4D0D4"
},
"light-high-contrast": {
"primary": "#2D2749",
"surfaceTint": "#605A7F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#4A4468",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#2D2A39",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#4A4757",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#432135",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#643D53",
"onTertiaryContainer": "#FFFFFF",
"error": "#600004",
"onError": "#FFFFFF",
"errorContainer": "#98000A",
"onErrorContainer": "#FFFFFF",
"background": "#FDF8FC",
"onBackground": "#1C1B1E",
"surface": "#FDF8FC",
"onSurface": "#000000",
"surfaceVariant": "#E6E1EA",
"onSurfaceVariant": "#000000",
"outline": "#2D2B32",
"outlineVariant": "#4A4850",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#313033",
"inverseOnSurface": "#FFFFFF",
"inversePrimary": "#C9C1EC",
"primaryFixed": "#4A4468",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#342E50",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#4A4757",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#34313F",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#643D53",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#4A273C",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#BBB8BB",
"surfaceBright": "#FDF8FC",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F4EFF3",
"surfaceContainer": "#E6E1E5",
"surfaceContainerHigh": "#D7D3D7",
"surfaceContainerHighest": "#C9C5C9"
},
"dark": {
"primary": "#C9C1EC",
"surfaceTint": "#C9C1EC",
"onPrimary": "#312C4E",
"primaryContainer": "#4E486C",
"onPrimaryContainer": "#C0B8E3",
"secondary": "#C9C4D7",
"onSecondary": "#312E3D",
"secondaryContainer": "#4A4757",
"onSecondaryContainer": "#BBB6C8",
"tertiary": "#ECB8D2",
"onTertiary": "#48253A",
"tertiaryContainer": "#684157",
"onTertiaryContainer": "#E3B0CA",
"error": "#FFB4AB",
"onError": "#690005",
"errorContainer": "#93000A",
"onErrorContainer": "#FFDAD6",
"background": "#141315",
"onBackground": "#E6E1E5",
"surface": "#141315",
"onSurface": "#E6E1E5",
"surfaceVariant": "#48464D",
"onSurfaceVariant": "#C9C5CE",
"outline": "#938F98",
"outlineVariant": "#48464D",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E6E1E5",
"inverseOnSurface": "#313033",
"inversePrimary": "#605A7F",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#1C1738",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#484266",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#1C1A27",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#484554",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#301024",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#613B51",
"surfaceDim": "#141315",
"surfaceBright": "#3A393B",
"surfaceContainerLowest": "#0F0E10",
"surfaceContainerLow": "#1C1B1E",
"surfaceContainer": "#201F22",
"surfaceContainerHigh": "#2B292C",
"surfaceContainerHighest": "#363437"
},
"dark-medium-contrast": {
"primary": "#E0D7FF",
"surfaceTint": "#C9C1EC",
"onPrimary": "#262142",
"primaryContainer": "#938CB4",
"onPrimaryContainer": "#000000",
"secondary": "#DFD9ED",
"onSecondary": "#262432",
"secondaryContainer": "#938EA0",
"onSecondaryContainer": "#000000",
"tertiary": "#FFCFE7",
"onTertiary": "#3C1A2E",
"tertiaryContainer": "#B2839C",
"onTertiaryContainer": "#000000",
"error": "#FFD2CC",
"onError": "#540003",
"errorContainer": "#FF5449",
"onErrorContainer": "#000000",
"background": "#141315",
"onBackground": "#E6E1E5",
"surface": "#141315",
"onSurface": "#FFFFFF",
"surfaceVariant": "#48464D",
"onSurfaceVariant": "#DFDAE4",
"outline": "#B4B0BA",
"outlineVariant": "#928F98",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E6E1E5",
"inverseOnSurface": "#2B292C",
"inversePrimary": "#494367",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#120C2D",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#373254",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#120F1D",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#373443",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#230619",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#4E2B40",
"surfaceDim": "#141315",
"surfaceBright": "#454447",
"surfaceContainerLowest": "#080709",
"surfaceContainerLow": "#1E1D20",
"surfaceContainer": "#28272A",
"surfaceContainerHigh": "#333235",
"surfaceContainerHighest": "#3F3D40"
},
"dark-high-contrast": {
"primary": "#F3EDFF",
"surfaceTint": "#C9C1EC",
"onPrimary": "#000000",
"primaryContainer": "#C6BDE8",
"onPrimaryContainer": "#0B0627",
"secondary": "#F3EDFF",
"onSecondary": "#000000",
"secondaryContainer": "#C5C0D3",
"onSecondaryContainer": "#0C0916",
"tertiary": "#FFEBF3",
"onTertiary": "#000000",
"tertiaryContainer": "#E7B4CE",
"onTertiaryContainer": "#1C0213",
"error": "#FFECE9",
"onError": "#000000",
"errorContainer": "#FFAEA4",
"onErrorContainer": "#220001",
"background": "#141315",
"onBackground": "#E6E1E5",
"surface": "#141315",
"onSurface": "#FFFFFF",
"surfaceVariant": "#48464D",
"onSurfaceVariant": "#FFFFFF",
"outline": "#F3EEF8",
"outlineVariant": "#C5C1CA",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E6E1E5",
"inverseOnSurface": "#000000",
"inversePrimary": "#494367",
"primaryFixed": "#E6DEFF",
"onPrimaryFixed": "#000000",
"primaryFixedDim": "#C9C1EC",
"onPrimaryFixedVariant": "#120C2D",
"secondaryFixed": "#E6E0F3",
"onSecondaryFixed": "#000000",
"secondaryFixedDim": "#C9C4D7",
"onSecondaryFixedVariant": "#120F1D",
"tertiaryFixed": "#FFD8EA",
"onTertiaryFixed": "#000000",
"tertiaryFixedDim": "#ECB8D2",
"onTertiaryFixedVariant": "#230619",
"surfaceDim": "#141315",
"surfaceBright": "#514F52",
"surfaceContainerLowest": "#000000",
"surfaceContainerLow": "#201F22",
"surfaceContainer": "#313033",
"surfaceContainerHigh": "#3C3B3E",
"surfaceContainerHighest": "#484649"
}
},
"palettes": {
"primary": {
"0": "#000000",
"5": "#110B2C",
"10": "#1C1738",
"15": "#272142",
"20": "#312C4E",
"25": "#3C375A",
"30": "#484266",
"35": "#544E72",
"40": "#605A7F",
"50": "#797299",
"60": "#938CB4",
"70": "#AEA6CF",
"80": "#C9C1EC",
"90": "#E6DEFF",
"95": "#F4EEFF",
"98": "#FDF8FF",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"secondary": {
"0": "#000000",
"5": "#111018",
"10": "#1C1A23",
"15": "#26252D",
"20": "#312F38",
"25": "#3C3A43",
"30": "#48454F",
"35": "#54515B",
"40": "#605D67",
"50": "#797580",
"60": "#938F9A",
"70": "#AEA9B5",
"80": "#C9C4D0",
"90": "#E6E0EC",
"95": "#F4EEFB",
"98": "#FDF8FF",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"tertiary": {
"0": "#000000",
"5": "#1B0C13",
"10": "#27171E",
"15": "#322128",
"20": "#3E2B33",
"25": "#49363E",
"30": "#564149",
"35": "#624D55",
"40": "#6F5861",
"50": "#897179",
"60": "#A38A93",
"70": "#BFA4AD",
"80": "#DCBFC9",
"90": "#F9DBE5",
"95": "#FFECF1",
"98": "#FFF8F8",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral": {
"0": "#000000",
"5": "#111112",
"10": "#1C1B1D",
"15": "#262527",
"20": "#313032",
"25": "#3C3B3D",
"30": "#484648",
"35": "#545254",
"40": "#605E60",
"50": "#797678",
"60": "#939092",
"70": "#ADAAAC",
"80": "#C9C5C7",
"90": "#E5E1E3",
"95": "#F4EFF1",
"98": "#FDF8FA",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral-variant": {
"0": "#000000",
"5": "#111014",
"10": "#1C1B1F",
"15": "#262529",
"20": "#313034",
"25": "#3C3B3F",
"30": "#48464A",
"35": "#545256",
"40": "#605D62",
"50": "#79767B",
"60": "#939094",
"70": "#AEAAAF",
"80": "#C9C5CA",
"90": "#E6E1E6",
"95": "#F4EFF4",
"98": "#FDF8FD",
"99": "#FFFBFF",
"100": "#FFFFFF"
}
}
}

View File

@ -1,427 +0,0 @@
{
"//url": "http://material-foundation.github.io/material-theme-builder/?primary=%23186D98&custom%3ASuccess=%2300C555&colorMatch=true",
"name": "tempest",
"description": "TYPE: CUSTOM\nMaterial Theme Builder export",
"seed": "#186D98",
"coreColors": {
"primary": "#186D98"
},
"extendedColors": [
{
"name": "Success",
"color": "#00C555",
"description": "",
"harmonized": true
}
],
"schemes": {
"light": {
"primary": "#005479",
"surfaceTint": "#03658F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#186D98",
"onPrimaryContainer": "#CEE9FF",
"secondary": "#496173",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#C9E3F8",
"onSecondaryContainer": "#4D6678",
"tertiary": "#683D7A",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#825594",
"onTertiaryContainer": "#F9DCFF",
"error": "#BA1A1A",
"onError": "#FFFFFF",
"errorContainer": "#FFDAD6",
"onErrorContainer": "#93000A",
"background": "#F7F9FD",
"onBackground": "#191C1F",
"surface": "#F7F9FD",
"onSurface": "#191C1F",
"surfaceVariant": "#DCE3EB",
"onSurfaceVariant": "#40484E",
"outline": "#70787F",
"outlineVariant": "#C0C7CF",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2D3134",
"inverseOnSurface": "#EFF1F5",
"inversePrimary": "#88CEFE",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#001E2E",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#004C6D",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#021E2D",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#314A5B",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#300443",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#603572",
"surfaceDim": "#D8DADE",
"surfaceBright": "#F7F9FD",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F2F4F7",
"surfaceContainer": "#ECEEF2",
"surfaceContainerHigh": "#E6E8EC",
"surfaceContainerHighest": "#E0E2E6"
},
"light-medium-contrast": {
"primary": "#003A55",
"surfaceTint": "#03658F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#186D98",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#203949",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#577082",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#4E2460",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#825594",
"onTertiaryContainer": "#FFFFFF",
"error": "#740006",
"onError": "#FFFFFF",
"errorContainer": "#CF2C27",
"onErrorContainer": "#FFFFFF",
"background": "#F7F9FD",
"onBackground": "#191C1F",
"surface": "#F7F9FD",
"onSurface": "#0E1214",
"surfaceVariant": "#DCE3EB",
"onSurfaceVariant": "#2F373D",
"outline": "#4C535A",
"outlineVariant": "#666E75",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2D3134",
"inverseOnSurface": "#EFF1F5",
"inversePrimary": "#88CEFE",
"primaryFixed": "#23749F",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#005B82",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#577082",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#3F5869",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#895C9B",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#6F4381",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#C4C7CA",
"surfaceBright": "#F7F9FD",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F2F4F7",
"surfaceContainer": "#E6E8EC",
"surfaceContainerHigh": "#DBDDE1",
"surfaceContainerHighest": "#CFD2D5"
},
"light-high-contrast": {
"primary": "#003046",
"surfaceTint": "#03658F",
"onPrimary": "#FFFFFF",
"primaryContainer": "#004E71",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#152F3F",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#344C5D",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#421955",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#623874",
"onTertiaryContainer": "#FFFFFF",
"error": "#600004",
"onError": "#FFFFFF",
"errorContainer": "#98000A",
"onErrorContainer": "#FFFFFF",
"background": "#F7F9FD",
"onBackground": "#191C1F",
"surface": "#F7F9FD",
"onSurface": "#000000",
"surfaceVariant": "#DCE3EB",
"onSurfaceVariant": "#000000",
"outline": "#252D33",
"outlineVariant": "#424A51",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2D3134",
"inverseOnSurface": "#FFFFFF",
"inversePrimary": "#88CEFE",
"primaryFixed": "#004E71",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#003650",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#344C5D",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#1C3546",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#623874",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#4A205C",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#B6B9BD",
"surfaceBright": "#F7F9FD",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#EFF1F5",
"surfaceContainer": "#E0E2E6",
"surfaceContainerHigh": "#D2D4D8",
"surfaceContainerHighest": "#C4C7CA"
},
"dark": {
"primary": "#88CEFE",
"surfaceTint": "#88CEFE",
"onPrimary": "#00344D",
"primaryContainer": "#186D98",
"onPrimaryContainer": "#CEE9FF",
"secondary": "#B0CADF",
"onSecondary": "#1A3343",
"secondaryContainer": "#314A5B",
"onSecondaryContainer": "#9FB8CD",
"tertiary": "#E8B4FA",
"onTertiary": "#471E59",
"tertiaryContainer": "#825594",
"onTertiaryContainer": "#F9DCFF",
"error": "#FFB4AB",
"onError": "#690005",
"errorContainer": "#93000A",
"onErrorContainer": "#FFDAD6",
"background": "#101417",
"onBackground": "#E0E2E6",
"surface": "#101417",
"onSurface": "#E0E2E6",
"surfaceVariant": "#40484E",
"onSurfaceVariant": "#C0C7CF",
"outline": "#8A9299",
"outlineVariant": "#40484E",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E0E2E6",
"inverseOnSurface": "#2D3134",
"inversePrimary": "#03658F",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#001E2E",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#004C6D",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#021E2D",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#314A5B",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#300443",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#603572",
"surfaceDim": "#101417",
"surfaceBright": "#363A3D",
"surfaceContainerLowest": "#0B0F11",
"surfaceContainerLow": "#191C1F",
"surfaceContainer": "#1D2023",
"surfaceContainerHigh": "#272A2D",
"surfaceContainerHighest": "#323538"
},
"dark-medium-contrast": {
"primary": "#BBE1FF",
"surfaceTint": "#88CEFE",
"onPrimary": "#00293D",
"primaryContainer": "#5098C5",
"onPrimaryContainer": "#000000",
"secondary": "#C6E0F5",
"onSecondary": "#0D2838",
"secondaryContainer": "#7B94A7",
"onSecondaryContainer": "#000000",
"tertiary": "#F5D0FF",
"onTertiary": "#3B114E",
"tertiaryContainer": "#AF7FC1",
"onTertiaryContainer": "#000000",
"error": "#FFD2CC",
"onError": "#540003",
"errorContainer": "#FF5449",
"onErrorContainer": "#000000",
"background": "#101417",
"onBackground": "#E0E2E6",
"surface": "#101417",
"onSurface": "#FFFFFF",
"surfaceVariant": "#40484E",
"onSurfaceVariant": "#D6DDE5",
"outline": "#ABB3BB",
"outlineVariant": "#899199",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E0E2E6",
"inverseOnSurface": "#272A2D",
"inversePrimary": "#004D6F",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#00131F",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#003A55",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#00131F",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#203949",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#220032",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#4E2460",
"surfaceDim": "#101417",
"surfaceBright": "#424548",
"surfaceContainerLowest": "#05080A",
"surfaceContainerLow": "#1B1E21",
"surfaceContainer": "#25282B",
"surfaceContainerHigh": "#303336",
"surfaceContainerHighest": "#3B3E41"
},
"dark-high-contrast": {
"primary": "#E3F2FF",
"surfaceTint": "#88CEFE",
"onPrimary": "#000000",
"primaryContainer": "#84CAFA",
"onPrimaryContainer": "#000D17",
"secondary": "#E3F2FF",
"onSecondary": "#000000",
"secondaryContainer": "#ACC6DB",
"onSecondaryContainer": "#000D17",
"tertiary": "#FDEAFF",
"onTertiary": "#000000",
"tertiaryContainer": "#E4B0F6",
"onTertiaryContainer": "#190026",
"error": "#FFECE9",
"onError": "#000000",
"errorContainer": "#FFAEA4",
"onErrorContainer": "#220001",
"background": "#101417",
"onBackground": "#E0E2E6",
"surface": "#101417",
"onSurface": "#FFFFFF",
"surfaceVariant": "#40484E",
"onSurfaceVariant": "#FFFFFF",
"outline": "#E9F1F9",
"outlineVariant": "#BCC3CB",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E0E2E6",
"inverseOnSurface": "#000000",
"inversePrimary": "#004D6F",
"primaryFixed": "#C8E6FF",
"onPrimaryFixed": "#000000",
"primaryFixedDim": "#88CEFE",
"onPrimaryFixedVariant": "#00131F",
"secondaryFixed": "#CCE6FB",
"onSecondaryFixed": "#000000",
"secondaryFixedDim": "#B0CADF",
"onSecondaryFixedVariant": "#00131F",
"tertiaryFixed": "#F8D8FF",
"onTertiaryFixed": "#000000",
"tertiaryFixedDim": "#E8B4FA",
"onTertiaryFixedVariant": "#220032",
"surfaceDim": "#101417",
"surfaceBright": "#4D5054",
"surfaceContainerLowest": "#000000",
"surfaceContainerLow": "#1D2023",
"surfaceContainer": "#2D3134",
"surfaceContainerHigh": "#383C3F",
"surfaceContainerHighest": "#44474A"
}
},
"palettes": {
"primary": {
"0": "#000000",
"5": "#00131F",
"10": "#001E2E",
"15": "#00293D",
"20": "#00344D",
"25": "#00405D",
"30": "#004C6D",
"35": "#00587E",
"40": "#03658F",
"50": "#317EAA",
"60": "#5098C5",
"70": "#6CB3E1",
"80": "#88CEFE",
"90": "#C8E6FF",
"95": "#E5F2FF",
"98": "#F6FAFF",
"99": "#FBFCFF",
"100": "#FFFFFF"
},
"secondary": {
"0": "#000000",
"5": "#05121B",
"10": "#0F1D26",
"15": "#1A2731",
"20": "#25323C",
"25": "#303D47",
"30": "#3B4853",
"35": "#46545F",
"40": "#52606B",
"50": "#6B7984",
"60": "#84929E",
"70": "#9FADB9",
"80": "#BAC8D5",
"90": "#D6E4F2",
"95": "#E5F2FF",
"98": "#F6FAFF",
"99": "#FBFCFF",
"100": "#FFFFFF"
},
"tertiary": {
"0": "#000000",
"5": "#140D25",
"10": "#1E1730",
"15": "#29223B",
"20": "#342C46",
"25": "#3F3752",
"30": "#4B425E",
"35": "#564E6A",
"40": "#635A76",
"50": "#7C7290",
"60": "#968CAB",
"70": "#B1A6C6",
"80": "#CDC1E2",
"90": "#E9DDFF",
"95": "#F6EDFF",
"98": "#FEF7FF",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral": {
"0": "#000000",
"5": "#0F1113",
"10": "#1A1C1E",
"15": "#242628",
"20": "#2F3132",
"25": "#3A3B3D",
"30": "#454749",
"35": "#515254",
"40": "#5D5E60",
"50": "#767779",
"60": "#909193",
"70": "#AAABAD",
"80": "#C6C6C8",
"90": "#E2E2E4",
"95": "#F1F0F3",
"98": "#F9F9FB",
"99": "#FCFCFE",
"100": "#FFFFFF"
},
"neutral-variant": {
"0": "#000000",
"5": "#0C1215",
"10": "#171C20",
"15": "#21262B",
"20": "#2C3135",
"25": "#373C40",
"30": "#42474C",
"35": "#4E5358",
"40": "#5A5F64",
"50": "#73787D",
"60": "#8C9196",
"70": "#A7ACB1",
"80": "#C2C7CC",
"90": "#DEE3E8",
"95": "#EDF1F7",
"98": "#F6FAFF",
"99": "#FBFCFF",
"100": "#FFFFFF"
}
}
}

View File

@ -0,0 +1,151 @@
import logging
import traceback
from enum import IntEnum
from arcaea_offline.database import Database
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, QSysInfo, Qt, QUrl, Slot
from PySide6.QtWidgets import QDialog, QMessageBox
from core.settings import SettingsKeys, settings
from ui.extends.shared.database import create_engine
from .databaseChecker_ui import Ui_DatabaseChecker
logger = logging.getLogger(__name__)
class DatabaseCheckerResult(IntEnum):
FileExist = 0x001
Initted = 0x002
class DatabaseChecker(Ui_DatabaseChecker, QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.setWindowFlag(Qt.WindowType.WindowMinimizeButtonHint, False)
self.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, False)
self.setWindowFlag(Qt.WindowType.WindowCloseButtonHint, True)
self.dbDirSelector.setMode(self.dbDirSelector.getExistingDirectory)
self.confirmDbByExistingSettings = False
if dbUrlString := settings.stringValue(SettingsKeys.General.DatabaseUrl):
dbFileUrl = QUrl(dbUrlString.replace("sqlite://", "file://"))
dbFileInfo = QFileInfo(dbFileUrl.toLocalFile())
if dbFileInfo.exists():
self.dbDirSelector.selectFile(dbFileInfo.path())
self.dbFilenameLineEdit.setText(dbFileInfo.fileName())
self.confirmDbByExistingSettings = True
self.confirmDbPathButton.click()
else:
self.dbDirSelector.selectFile(QDir.currentPath())
self.dbFilenameLineEdit.setText("arcaea_offline.db")
else:
self.dbDirSelector.selectFile(QDir.currentPath())
self.dbFilenameLineEdit.setText("arcaea_offline.db")
def writeDatabaseUrlToSettings(self, databaseUrl: str):
settings.setValue(SettingsKeys.General.DatabaseUrl, databaseUrl)
def dbPath(self):
return QDir(self.dbDirSelector.selectedFiles()[0])
def dbFileInfo(self):
return QFileInfo(
QDir.cleanPath(
self.dbPath().absoluteFilePath(self.dbFilenameLineEdit.text())
)
)
def dbFileUrl(self):
return QUrl.fromLocalFile(self.dbFileInfo().filePath())
def dbSqliteUrl(self):
kernelType = QSysInfo.kernelType()
# the slash count varies depending on the kernel
# https://docs.sqlalchemy.org/en/20/core/engines.html#sqlite
if kernelType == "winnt":
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
else:
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite:///"))
def confirmDb(self) -> DatabaseCheckerResult:
flags = 0x000
dbFileInfo = self.dbFileInfo()
dbSqliteUrl = self.dbSqliteUrl()
if not dbFileInfo.exists():
return flags
flags |= DatabaseCheckerResult.FileExist
db = Database(create_engine(dbSqliteUrl))
if db.check_init():
flags |= DatabaseCheckerResult.Initted
self.writeDatabaseUrlToSettings(self.dbSqliteUrl().toString())
return flags
def updateLabels(self):
result = self.confirmDb()
try:
db = Database()
version = db.version()
initted = db.check_init()
self.dbVersionLabel.setText(str(version))
self.dbCheckConnLabel.setText(
'<font color="green">OK</font>'
if initted
else '<font color="red">Not initted</font>'
)
self.continueButton.setEnabled(initted)
except Exception as e:
self.dbVersionLabel.setText("-")
self.dbCheckConnLabel.setText(
f'<font color="red">Error: {e}</font>'
if result & DatabaseCheckerResult.FileExist
else "-"
)
self.continueButton.setEnabled(False)
@Slot()
def on_confirmDbPathButton_clicked(self):
dbSqliteUrl = self.dbSqliteUrl()
self.writeDatabaseUrlToSettings(dbSqliteUrl.toString())
result = self.confirmDb()
if result & DatabaseCheckerResult.Initted:
if not self.confirmDbByExistingSettings:
self.writeDatabaseUrlToSettings(dbSqliteUrl.toString())
elif result & DatabaseCheckerResult.FileExist:
confirm_try_init = QMessageBox.question(
self,
None,
QCoreApplication.translate("DatabaseChecker", "dialog.tryInitExistingDatabase"),
) # fmt: skip
if confirm_try_init == QMessageBox.StandardButton.Yes:
try:
Database().init(checkfirst=True)
except Exception as e:
logger.exception("Error while initializing an existing database")
QMessageBox.critical(
self, None, "\n".join(traceback.format_exception(e))
)
else:
confirm_new_database = QMessageBox.question(
self,
None,
QCoreApplication.translate("DatabaseChecker", "dialog.confirmNewDatabase"),
) # fmt: skip
if confirm_new_database == QMessageBox.StandardButton.Yes:
db = Database(create_engine(dbSqliteUrl))
db.init()
self.updateLabels()
@Slot()
def on_dbReInitButton_clicked(self):
Database().init(checkfirst=True)
QMessageBox.information(self, None, "OK")
@Slot()
def on_continueButton_clicked(self):
self.accept()

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DatabaseChecker</class>
<widget class="QWidget" name="DatabaseChecker">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>250</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">DatabaseChecker</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>dbPathLabel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="FileSelector" name="dbDirSelector" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>dbFilenameLabel</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="dbFilenameLineEdit"/>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="confirmDbPathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>confirmDbPathButton</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QLabel" name="dbVersionLabel">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item row="6" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>dbCheckConnLabel</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="dbCheckConnLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QPushButton" name="continueButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>continueButton</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>dbVersionLabel</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>dbReInitLabel</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="dbReInitButton">
<property name="text">
<string>dbReInitButton</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileSelector</class>
<extends>QWidget</extends>
<header>ui.implements.components.fileSelector</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'databaseChecker.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, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QSizePolicy,
QSpacerItem, QWidget)
from ui.implements.components.fileSelector import FileSelector
class Ui_DatabaseChecker(object):
def setupUi(self, DatabaseChecker):
if not DatabaseChecker.objectName():
DatabaseChecker.setObjectName(u"DatabaseChecker")
DatabaseChecker.resize(350, 250)
DatabaseChecker.setWindowTitle(u"DatabaseChecker")
self.formLayout = QFormLayout(DatabaseChecker)
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.label = QLabel(DatabaseChecker)
self.label.setObjectName(u"label")
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
self.dbDirSelector = FileSelector(DatabaseChecker)
self.dbDirSelector.setObjectName(u"dbDirSelector")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.dbDirSelector)
self.label_3 = QLabel(DatabaseChecker)
self.label_3.setObjectName(u"label_3")
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_3)
self.dbFilenameLineEdit = QLineEdit(DatabaseChecker)
self.dbFilenameLineEdit.setObjectName(u"dbFilenameLineEdit")
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.dbFilenameLineEdit)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
self.confirmDbPathButton = QPushButton(DatabaseChecker)
self.confirmDbPathButton.setObjectName(u"confirmDbPathButton")
sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.confirmDbPathButton.sizePolicy().hasHeightForWidth())
self.confirmDbPathButton.setSizePolicy(sizePolicy)
self.horizontalLayout.addWidget(self.confirmDbPathButton)
self.formLayout.setLayout(2, QFormLayout.FieldRole, self.horizontalLayout)
self.dbVersionLabel = QLabel(DatabaseChecker)
self.dbVersionLabel.setObjectName(u"dbVersionLabel")
self.dbVersionLabel.setText(u"-")
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.dbVersionLabel)
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.formLayout.setItem(6, QFormLayout.FieldRole, self.verticalSpacer)
self.label_5 = QLabel(DatabaseChecker)
self.label_5.setObjectName(u"label_5")
self.formLayout.setWidget(7, QFormLayout.LabelRole, self.label_5)
self.dbCheckConnLabel = QLabel(DatabaseChecker)
self.dbCheckConnLabel.setObjectName(u"dbCheckConnLabel")
self.dbCheckConnLabel.setText(u"...")
self.formLayout.setWidget(7, QFormLayout.FieldRole, self.dbCheckConnLabel)
self.continueButton = QPushButton(DatabaseChecker)
self.continueButton.setObjectName(u"continueButton")
self.continueButton.setEnabled(False)
self.formLayout.setWidget(8, QFormLayout.SpanningRole, self.continueButton)
self.label_2 = QLabel(DatabaseChecker)
self.label_2.setObjectName(u"label_2")
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_2)
self.line = QFrame(DatabaseChecker)
self.line.setObjectName(u"line")
self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken)
self.formLayout.setWidget(3, QFormLayout.SpanningRole, self.line)
self.label_4 = QLabel(DatabaseChecker)
self.label_4.setObjectName(u"label_4")
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_4)
self.dbReInitButton = QPushButton(DatabaseChecker)
self.dbReInitButton.setObjectName(u"dbReInitButton")
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.dbReInitButton)
self.retranslateUi(DatabaseChecker)
QMetaObject.connectSlotsByName(DatabaseChecker)
# setupUi
def retranslateUi(self, DatabaseChecker):
self.label.setText(QCoreApplication.translate("DatabaseChecker", u"dbPathLabel", None))
self.label_3.setText(QCoreApplication.translate("DatabaseChecker", u"dbFilenameLabel", None))
self.confirmDbPathButton.setText(QCoreApplication.translate("DatabaseChecker", u"confirmDbPathButton", None))
self.label_5.setText(QCoreApplication.translate("DatabaseChecker", u"dbCheckConnLabel", None))
self.continueButton.setText(QCoreApplication.translate("DatabaseChecker", u"continueButton", None))
self.label_2.setText(QCoreApplication.translate("DatabaseChecker", u"dbVersionLabel", None))
self.label_4.setText(QCoreApplication.translate("DatabaseChecker", u"dbReInitLabel", None))
self.dbReInitButton.setText(QCoreApplication.translate("DatabaseChecker", u"dbReInitButton", None))
pass
# retranslateUi

View File

@ -1,3 +0,0 @@
from .manager import ThemeManager
__all__ = ["ThemeManager"]

View File

@ -1,160 +0,0 @@
import dataclasses
import json
from typing import overload
import structlog
from PySide6.QtCore import Property, QObject, QResource, Qt, Signal
from PySide6.QtGui import QColor, QGuiApplication, QPalette
from .material3 import Material3DynamicThemeImpl, Material3ThemeImpl
from .qml import ThemeQmlExposer
from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme
QML_IMPORT_NAME = "internal.ui.theme"
QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0
_THEME_CACHES: dict[ThemeInfo, ThemeImpl] = {}
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
class ThemeManager(QObject):
_void = Signal()
themeChanged = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self._qPalette = QPalette()
self._customPalette: _TCustomPalette = {
"primary": QColor.fromString("#616161"),
"success": QColor.fromString("#616161"),
"error": QColor.fromString("#616161"),
}
self._lastThemeInfo = ThemeInfo(
series="material3",
name="default",
scheme=self.getCurrentScheme(),
)
self._qmlExposer = ThemeQmlExposer(themeImpl=ThemeImpl())
self._cacheMaterial3Theme(themeName="default", scheme="light")
self._cacheMaterial3Theme(themeName="default", scheme="dark")
self._cacheMaterial3Theme(themeName="tempest", scheme="light")
self._cacheMaterial3Theme(themeName="tempest", scheme="dark")
self._cacheMaterial3DynamicTheme(themeName="default", scheme="light")
self._cacheMaterial3DynamicTheme(themeName="default", scheme="dark")
self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="light")
self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="dark")
self.setTheme("material3-dynamic", "default")
def getCurrentScheme(self) -> _TScheme:
qApp: QGuiApplication = QGuiApplication.instance() # pyright: ignore[reportAssignmentType]
return (
"dark"
if qApp.styleHints().colorScheme() == Qt.ColorScheme.Dark
else "light"
)
def getMaterial3Theme(self, themeName: str, scheme: _TScheme) -> Material3ThemeImpl:
themeDataResource = QResource(f":/themes/{themeName}.json")
if not themeDataResource.isValid():
raise ValueError(f"Material3 theme {themeName!r} not found")
themeData = json.loads(
themeDataResource.uncompressedData().data().decode("utf-8")
)
return Material3ThemeImpl(themeData=themeData, scheme=scheme)
def getMaterial3DynamicTheme(
self, themeName: str, scheme: _TScheme
) -> Material3DynamicThemeImpl:
themeDataResource = QResource(f":/themes/{themeName}.json")
if not themeDataResource.isValid():
raise ValueError(f"Material3 theme {themeName!r} not found")
themeData = json.loads(
themeDataResource.uncompressedData().data().decode("utf-8")
)
return Material3DynamicThemeImpl(
sourceColorHex=themeData["seed"],
scheme=scheme,
name=themeName,
)
def _cacheTheme(self, *, themeImpl: ThemeImpl):
_THEME_CACHES[themeImpl.info] = themeImpl
logger.debug("Theme %r cached", themeImpl.info)
def _getCachedTheme(self, *, themeInfo: ThemeInfo):
cachedTheme = _THEME_CACHES.get(themeInfo)
if cachedTheme is None:
raise KeyError(f"Theme {themeInfo!r} not cached")
return cachedTheme
def _cacheMaterial3Theme(self, *, themeName: str, scheme: _TScheme):
self._cacheTheme(
themeImpl=self.getMaterial3Theme(themeName=themeName, scheme=scheme),
)
def _cacheMaterial3DynamicTheme(self, *, themeName: str, scheme: _TScheme):
self._cacheTheme(
themeImpl=self.getMaterial3DynamicTheme(themeName=themeName, scheme=scheme)
)
@overload
def setTheme(self, *, themeInfo: ThemeInfo): ...
@overload
def setTheme(
self, themeSeries: str, themeName: str, scheme: _TScheme | None = None, /
): ...
def setTheme(self, *args, **kwargs):
if "themeInfo" in kwargs:
themeInfo = kwargs["themeInfo"]
elif 2 <= len(args) <= 3:
themeSeries = args[0]
themeName = args[1]
schemeArg = args[2] if len(args) > 2 else None
scheme = schemeArg or self.getCurrentScheme()
themeInfo = ThemeInfo(series=themeSeries, name=themeName, scheme=scheme)
else:
raise TypeError("Invalid setTheme() call")
logger.debug("Preparing to set theme %r", themeInfo)
cachedTheme = self._getCachedTheme(themeInfo=themeInfo)
self._qPalette = cachedTheme.qPalette
self._customPalette = cachedTheme.customPalette
self._lastThemeInfo = themeInfo
self._qmlExposer.themeImpl = cachedTheme
self.themeChanged.emit()
def updateTheme(self, scheme: _TScheme | None = None):
themeInfo = dataclasses.replace(self._lastThemeInfo) # make a copy
scheme = scheme or self.getCurrentScheme()
themeInfo.scheme = scheme
self.setTheme(themeInfo=themeInfo)
@Property(QPalette, notify=themeChanged)
def qPalette(self) -> QPalette:
return self._qPalette
@Property(dict, notify=themeChanged)
def customPalette(self) -> _TCustomPalette:
return self._customPalette
@Property(ThemeQmlExposer, notify=themeChanged)
def qmlExposer(self) -> ThemeQmlExposer:
return self._qmlExposer

View File

@ -1,245 +0,0 @@
from typing import TypedDict
from materialyoucolor.blend import Blend
from materialyoucolor.dynamiccolor.contrast_curve import ContrastCurve
from materialyoucolor.dynamiccolor.dynamic_color import DynamicColor, FromPaletteOptions
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
from materialyoucolor.hct import Hct
from materialyoucolor.palettes.tonal_palette import TonalPalette
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
from PySide6.QtGui import QColor, QPalette
from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme
class _M3ThemeDataExtendedColorItem(TypedDict):
name: str
color: str
description: str
harmonized: bool
_M3ThemeDataSchemes = TypedDict(
"_M3ThemeDataSchemes",
{
"light": dict[str, str],
"light-medium-contrast": dict[str, str],
"light-high-contrast": dict[str, str],
"dark": dict[str, str],
"dark-medium-contrast": dict[str, str],
"dark-high-contrast": dict[str, str],
},
)
_M3ThemeDataPalettes = TypedDict(
"_M3ThemeDataPalettes",
{
"primary": dict[str, str],
"secondary": dict[str, str],
"tertiary": dict[str, str],
"neutral": dict[str, str],
"neutral-variant": dict[str, str],
},
)
class _M3ThemeData(TypedDict):
name: str
description: str
seed: str
coreColors: dict[str, str]
extendedColors: list[_M3ThemeDataExtendedColorItem]
schemes: _M3ThemeDataSchemes
palettes: _M3ThemeDataPalettes
def _hexToHct(hexColor: str) -> Hct:
pureHexPart = hexColor[1:] if hexColor.startswith("#") else hexColor
return Hct.from_int(int(f"0xff{pureHexPart}", 16))
def _hctToQColor(hct: Hct) -> QColor:
return QColor.fromRgba(hct.to_int())
class Material3ThemeImpl(ThemeImpl):
COLOR_ROLE_MAPPING: dict[QPalette.ColorRole, str] = {
QPalette.ColorRole.Window: "surface",
QPalette.ColorRole.WindowText: "onSurface",
QPalette.ColorRole.Base: "surfaceContainer",
QPalette.ColorRole.AlternateBase: "surfaceContainerHighest",
QPalette.ColorRole.ToolTipBase: "secondaryContainer",
QPalette.ColorRole.ToolTipText: "onSecondaryContainer",
QPalette.ColorRole.PlaceholderText: "inverseSurface",
QPalette.ColorRole.Text: "onSurface",
QPalette.ColorRole.Button: "primaryContainer",
QPalette.ColorRole.ButtonText: "onPrimaryContainer",
QPalette.ColorRole.BrightText: "onSecondary",
QPalette.ColorRole.Light: "surfaceContainerLowest",
QPalette.ColorRole.Midlight: "surfaceContainerLow",
QPalette.ColorRole.Dark: "inverseSurface",
QPalette.ColorRole.Mid: "surfaceContainer",
QPalette.ColorRole.Shadow: "shadow",
QPalette.ColorRole.Highlight: "primary",
QPalette.ColorRole.Accent: "primary",
QPalette.ColorRole.HighlightedText: "onPrimary",
QPalette.ColorRole.Link: "tertiary",
QPalette.ColorRole.LinkVisited: "tertiaryContainer",
}
def __init__(self, *, themeData: _M3ThemeData, scheme: _TScheme):
self.themeData = themeData
self.scheme: _TScheme = scheme
if self.themeData["schemes"].get(scheme) is None:
raise ValueError(f"Invalid scheme: {scheme}")
def _findExtendedColor(
self, colorName: str
) -> _M3ThemeDataExtendedColorItem | None:
return next(
(it for it in self.themeData["extendedColors"] if it["name"] == colorName),
None,
)
@property
def info(self):
return ThemeInfo(
series="material3",
name=self.themeData["name"],
scheme=self.scheme,
)
@property
def qPalette(self) -> QPalette:
qPalette = QPalette()
for role, name in self.COLOR_ROLE_MAPPING.items():
color = QColor.fromString(self.themeData["schemes"][self.scheme][name])
qPalette.setColor(role, color)
return qPalette
@property
def customPalette(self) -> _TCustomPalette:
primaryHct = _hexToHct(self.themeData["schemes"][self.scheme]["primary"])
successColorItem = self._findExtendedColor("Success")
if successColorItem is None:
raise Exception("Success color not found")
successHct = _hexToHct(successColorItem["color"])
successHarmonizedHct = Hct.from_int(
Blend.harmonize(successHct.to_int(), primaryHct.to_int())
)
return {
"primary": _hctToQColor(primaryHct),
"success": _hctToQColor(successHarmonizedHct),
"error": QColor.fromString(self.themeData["schemes"][self.scheme]["error"]),
}
class Material3DynamicThemeImpl(ThemeImpl):
ACTIVE_COLOR_ROLE_MAPPING: dict[QPalette.ColorRole, DynamicColor] = {
QPalette.ColorRole.Window: MaterialDynamicColors.surface,
QPalette.ColorRole.WindowText: MaterialDynamicColors.onSurface,
QPalette.ColorRole.Base: MaterialDynamicColors.surfaceContainer,
QPalette.ColorRole.AlternateBase: MaterialDynamicColors.surfaceContainerHighest,
QPalette.ColorRole.ToolTipBase: MaterialDynamicColors.secondaryContainer,
QPalette.ColorRole.ToolTipText: MaterialDynamicColors.onSecondaryContainer,
QPalette.ColorRole.PlaceholderText: MaterialDynamicColors.inverseSurface,
QPalette.ColorRole.Text: MaterialDynamicColors.onSurface,
QPalette.ColorRole.Button: MaterialDynamicColors.primaryContainer,
QPalette.ColorRole.ButtonText: MaterialDynamicColors.onPrimaryContainer,
QPalette.ColorRole.BrightText: MaterialDynamicColors.onSecondary,
QPalette.ColorRole.Light: MaterialDynamicColors.surfaceContainerLowest,
QPalette.ColorRole.Midlight: MaterialDynamicColors.surfaceContainerLow,
QPalette.ColorRole.Dark: MaterialDynamicColors.inverseSurface,
QPalette.ColorRole.Mid: MaterialDynamicColors.surfaceContainer,
QPalette.ColorRole.Shadow: MaterialDynamicColors.shadow,
QPalette.ColorRole.Highlight: MaterialDynamicColors.primary,
QPalette.ColorRole.Accent: MaterialDynamicColors.primary,
QPalette.ColorRole.HighlightedText: MaterialDynamicColors.onPrimary,
QPalette.ColorRole.Link: MaterialDynamicColors.tertiary,
QPalette.ColorRole.LinkVisited: MaterialDynamicColors.tertiaryContainer,
}
EXTENDED_COLORS = {
"success": "#00c555",
}
def __init__(self, sourceColorHex: str, scheme: _TScheme, *, name: str):
self.material3Scheme = SchemeTonalSpot(
_hexToHct(sourceColorHex),
is_dark=scheme == "dark",
contrast_level=0.0,
)
self.name = name
@property
def info(self):
return ThemeInfo(
series="material3-dynamic",
name=self.name,
scheme="dark" if self.material3Scheme.is_dark else "light",
)
@property
def qPalette(self) -> QPalette:
qPalette = QPalette()
for role, dynamicColor in self.ACTIVE_COLOR_ROLE_MAPPING.items():
hct = dynamicColor.get_hct(self.material3Scheme)
qColor = QColor.fromRgba(hct.to_int())
qPalette.setColor(QPalette.ColorGroup.Active, role, qColor)
# TODO: disabled palette seems to work only after a theme reload, needs further investigation
if role in [QPalette.ColorRole.Button, QPalette.ColorRole.ButtonText]:
disabledHct = Hct.from_hct(hct.hue, 1.0, hct.tone)
disabledQColor = QColor.fromRgba(disabledHct.to_int())
qPalette.setColor(QPalette.ColorGroup.Disabled, role, disabledQColor)
return qPalette
@property
def customPalette(self) -> _TCustomPalette:
primaryHct = MaterialDynamicColors.primary.get_hct(self.material3Scheme)
errorHct = MaterialDynamicColors.error.get_hct(self.material3Scheme)
extendedPalettes: dict[str, DynamicColor] = {}
for colorName, colorHex in self.EXTENDED_COLORS.items():
colorHct = _hexToHct(colorHex)
colorHarmonized = Blend.harmonize(colorHct.to_int(), primaryHct.to_int())
colorTonalPalette = TonalPalette.from_int(colorHarmonized)
colorSurfacePaletteOptions = DynamicColor.from_palette(
FromPaletteOptions(
name=f"{colorName}_container",
palette=lambda s: colorTonalPalette,
tone=lambda s: 30 if s.is_dark else 90,
is_background=True,
background=lambda s: MaterialDynamicColors.highestSurface(s),
contrast_curve=ContrastCurve(1, 1, 3, 4.5),
)
)
extendedPalettes[colorName] = DynamicColor.from_palette(
FromPaletteOptions(
name=colorName, # pyright: ignore[reportArgumentType]
palette=lambda s: colorTonalPalette,
tone=lambda s: 80 if s.is_dark else 40, # pyright: ignore[reportArgumentType]
is_background=False, # pyright: ignore[reportArgumentType]
background=lambda s: MaterialDynamicColors.highestSurface(s),
contrast_curve=ContrastCurve(3, 4.5, 7, 7),
)
)
return {
"primary": _hctToQColor(primaryHct),
"success": _hctToQColor(
extendedPalettes["success"].get_hct(self.material3Scheme)
),
"error": _hctToQColor(errorHct),
}

View File

@ -1,37 +0,0 @@
from PySide6.QtCore import Property, QObject, Signal
from PySide6.QtGui import QColor
from .shared import ThemeImpl
QML_IMPORT_NAME = "internal.ui.theme"
QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0
class ThemeQmlExposer(QObject):
themeChanged = Signal()
def __init__(self, *, themeImpl: ThemeImpl, parent: QObject | None = None):
super().__init__(parent)
self._themeImpl = themeImpl
@property
def themeImpl(self) -> ThemeImpl:
return self._themeImpl
@themeImpl.setter
def themeImpl(self, themeImpl: ThemeImpl):
self._themeImpl = themeImpl
self.themeChanged.emit()
@Property(QColor, notify=themeChanged)
def primary(self):
return self._themeImpl.customPalette["primary"]
@Property(QColor, notify=themeChanged)
def success(self):
return self._themeImpl.customPalette["success"]
@Property(QColor, notify=themeChanged)
def error(self):
return self._themeImpl.customPalette["error"]

View File

@ -1,43 +0,0 @@
from dataclasses import dataclass
from typing import Literal, TypedDict
from PySide6.QtGui import QColor, QPalette
class _TCustomPalette(TypedDict):
primary: QColor
success: QColor
error: QColor
_TScheme = Literal["light", "dark"]
@dataclass
class ThemeInfo:
series: str
name: str
scheme: _TScheme
def __hash__(self) -> int:
return hash((self.series, self.name, self.scheme))
class ThemeImpl:
DEFAULT_CUSTOM_PALETTE = {
"primary": QColor.fromString("#616161"),
"success": QColor.fromString("#616161"),
"error": QColor.fromString("#616161"),
}
@property
def info(self) -> ThemeInfo:
return ThemeInfo(series="placeholder", name="placeholder", scheme="dark")
@property
def qPalette(self) -> QPalette:
return QPalette()
@property
def customPalette(self) -> _TCustomPalette:
return self.DEFAULT_CUSTOM_PALETTE # pyright: ignore[reportReturnType]

View File

@ -1 +0,0 @@
UTILS_QML_IMPORT_NAME = "internal.ui.utils"

View File

@ -1,48 +0,0 @@
from pathlib import Path
from PySide6.QtCore import QFileInfo, QObject, QUrl, Slot
from PySide6.QtQml import QmlElement, QmlSingleton
from .common import UTILS_QML_IMPORT_NAME
QML_IMPORT_NAME = UTILS_QML_IMPORT_NAME
QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0
@QmlElement
@QmlSingleton
class UrlUtils(QObject):
@Slot(str, result=bool)
@staticmethod
def isDir(url: str):
return QFileInfo(QUrl(url).toLocalFile()).isDir()
@Slot(str, result=bool)
@staticmethod
def isFile(url: str):
return QFileInfo(QUrl(url).toLocalFile()).isFile()
@Slot(str, result=str)
@staticmethod
def stem(url: str):
return Path(QUrl(url).toLocalFile()).stem
@Slot(str, result=str)
@staticmethod
def name(url: str):
return Path(QUrl(url).toLocalFile()).name
@Slot(str, result=QUrl)
@staticmethod
def parent(url: str):
return QUrl.fromLocalFile(Path(QUrl(url).toLocalFile()).parent)
@QmlElement
@QmlSingleton
class UrlFormatUtils(QObject):
@Slot(str, result=str)
@staticmethod
def toLocalFile(url: str):
return QUrl(url).toLocalFile()

View File

@ -1,2 +0,0 @@
from .databaseInit import DatabaseInitViewModel
from .overview import OverviewViewModel

View File

@ -1 +0,0 @@
VM_QML_IMPORT_NAME = "internal.ui.vm"

View File

@ -1,369 +0,0 @@
import dataclasses
from enum import StrEnum
from pathlib import Path
import structlog
from arcaea_offline.models import (
CalculatedPotential,
Chart,
ConfigBase,
ScoreBest,
ScoreCalculated,
ScoresBase,
ScoresViewBase,
SongsBase,
SongsViewBase,
)
from arcaea_offline.models import (
Property as AoProperty,
)
from PySide6.QtCore import Property, QObject, QUrl, Signal, Slot
from PySide6.QtQml import QmlElement
from sqlalchemy import inspect, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from core.database import create_engine, db_path_to_sqlite_url, sqlite_url_to_db_path
from core.settings import SettingsKeys, settings
from core.settings.values import GeneralDatabaseType
from .common import VM_QML_IMPORT_NAME
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
QML_IMPORT_NAME = VM_QML_IMPORT_NAME
QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0
class _FmwiwsDatabase:
"""Fuck Me Why I Wrote Singleton Database"""
def __init__(self, url: str):
self.engine = create_engine(url)
def init(self, *, checkfirst: bool = True):
# create tables & views
if checkfirst:
# > https://github.com/kvesteri/sqlalchemy-utils/issues/396
# > view.create_view() causes DuplicateTableError on
# > Base.metadata.create_all(checkfirst=True)
# so if `checkfirst` is True, drop these views before creating
SongsViewBase.metadata.drop_all(self.engine)
ScoresViewBase.metadata.drop_all(self.engine)
SongsBase.metadata.create_all(self.engine, checkfirst=checkfirst)
SongsViewBase.metadata.create_all(self.engine)
ScoresBase.metadata.create_all(self.engine, checkfirst=checkfirst)
ScoresViewBase.metadata.create_all(self.engine)
ConfigBase.metadata.create_all(self.engine, checkfirst=checkfirst)
version_property = AoProperty(key="version", value="4")
with Session(self.engine) as session:
session.merge(version_property)
session.commit()
def is_initialized(self):
expect_tables = (
list(SongsBase.metadata.tables.keys())
+ list(ScoresBase.metadata.tables.keys())
+ list(ConfigBase.metadata.tables.keys())
+ [
Chart.__tablename__,
ScoreCalculated.__tablename__,
ScoreBest.__tablename__,
CalculatedPotential.__tablename__,
]
)
return all(inspect(self.engine).has_table(t) for t in expect_tables)
def version(self):
with Session(self.engine) as session:
stmt = select(AoProperty.value).where(AoProperty.key == "version")
result = session.scalar(stmt)
return None if result is None else int(result)
class _UiMode(StrEnum):
SELECT = "select"
CREATE = "create"
@dataclasses.dataclass
class DatabaseInfo:
url: str
error: Exception | None = None
initialized: bool | None = None
version: int | None = None
@property
def error_dict(self):
e = self.error
if e is None:
return None
return {
"title": e.__class__.__name__,
"message": str(e),
}
def to_qml_dict(self):
return {
"url": self.url,
"error": self.error_dict,
"initialized": self.initialized,
"version": self.version,
}
@QmlElement
class DatabaseInitViewModel(QObject):
_void = Signal()
uiModeChanged = Signal()
selectFileUrlChanged = Signal()
directoryUrlChanged = Signal()
filenameChanged = Signal()
databaseUrlChanged = Signal()
databaseInfoChanged = Signal()
canContinueChanged = Signal()
canConfirmSilentlyChanged = Signal()
def __init__(self):
super().__init__()
self._selectFileUrl: QUrl | None = None
self._directoryUrl: QUrl = QUrl()
self._filename: str = ""
self._databaseInfo: DatabaseInfo | None = None
self._uiMode = _UiMode.SELECT
self.directoryUrlChanged.connect(lambda: self.databaseUrlChanged.emit())
self.filenameChanged.connect(lambda: self.databaseUrlChanged.emit())
self.selectFileUrlChanged.connect(self.databaseUrlChanged)
self.databaseInfoChanged.connect(self.canContinueChanged)
self.databaseUrlChanged.connect(self.onDatabaseUrlChanged)
self._loadSettings()
def onDatabaseUrlChanged(self):
self.loadDatabaseInfo()
@property
def _settingsDatabaseFile(self) -> Path | None:
# TODO: process database type when available
if (
settings.stringValue(SettingsKeys.General.DatabaseType)
!= GeneralDatabaseType.FILE
):
return None
file = settings.stringValue(SettingsKeys.General.DatabaseConn)
if not file:
logger.debug("No database file specified in settings")
return
filepath = Path(file)
if not filepath.exists():
logger.warning("Cannot find database file: %s", file)
return
return filepath
def _loadSettings(self) -> None:
fileUrl = self._settingsDatabaseFile
if fileUrl is None:
return
logger.info("Loading database from settings: %s", fileUrl)
self.setUiMode(_UiMode.SELECT)
self.setSelectFileUrl(fileUrl)
self.loadDatabaseInfo()
def _makeDatabaseInfo(self, dbUrl: str):
info = DatabaseInfo(url=dbUrl)
path = sqlite_url_to_db_path(dbUrl)
if not path.exists():
e = FileNotFoundError()
e.strerror = f"{path} does not exist"
info.error = e
return info
try:
db = _FmwiwsDatabase(dbUrl)
info.initialized = db.is_initialized()
info.version = db.version()
except SQLAlchemyError as e:
logger.exception("Error loading database info")
info.error = e
logger.debug("Database info for %r: %r", dbUrl, info)
return info
@Slot()
def loadDatabaseInfo(self):
dbUrl = self.getDatabaseUrl()
logger.info("Loading database info: %s", dbUrl)
if dbUrl is None:
logger.warning("Database URL is None")
return
self._databaseInfo = self._makeDatabaseInfo(dbUrl.toString())
self.databaseInfoChanged.emit()
@Slot(str)
def createFile(self, dbUrl: str):
file = sqlite_url_to_db_path(dbUrl)
if file.exists():
logger.warning(
"Attempted to create an existing file, check UI logic? (%s)", file
)
return
file.touch(mode=0o644)
logger.info("Created file %s", file)
@Slot()
def confirmCurrentConnection(self):
dbInfo = self._databaseInfo
if dbInfo is None:
logger.warning("Current database info is None, ignoring")
return
settings.setValue(
SettingsKeys.General.DatabaseType,
GeneralDatabaseType.FILE.value,
)
settings.setValue(
SettingsKeys.General.DatabaseConn,
str(sqlite_url_to_db_path(dbInfo.url).resolve().as_posix()),
)
@Slot(str)
def initialize(self, dbUrl: str):
try:
db = _FmwiwsDatabase(dbUrl)
db.init()
except SQLAlchemyError:
logger.exception("Error initializing database %s", dbUrl)
# region properties
def getUiMode(self):
return self._uiMode.value
def setUiMode(self, mode: str | _UiMode):
if isinstance(mode, _UiMode):
self._uiMode = mode
elif isinstance(mode, str):
try:
self._uiMode = _UiMode(mode)
except ValueError:
logger.warning("Invalid UI mode: %s", mode)
self.uiModeChanged.emit()
def getSelectFileUrl(self):
return self._selectFileUrl
def setSelectFileUrl(self, value: Path | QUrl | None):
if isinstance(value, Path):
value = QUrl.fromLocalFile(value.as_posix())
self._selectFileUrl = value
self.selectFileUrlChanged.emit()
def getDirectoryUrl(self):
return self._directoryUrl
def setDirectoryUrl(self, value: QUrl | None):
self._directoryUrl = value or QUrl()
self.directoryUrlChanged.emit()
def getFilename(self):
return self._filename
def setFilename(self, value: str | None):
self._filename = value or ""
self.filenameChanged.emit()
def getDatabaseUrl(self):
if self._uiMode == _UiMode.SELECT:
fileUrl = self.getSelectFileUrl()
if fileUrl is None:
return None
return db_path_to_sqlite_url(Path(fileUrl.toLocalFile()))
directoryUrl = self.getDirectoryUrl()
filename = self.getFilename()
databasePath = Path(directoryUrl.toLocalFile()) / filename
databaseUrl = db_path_to_sqlite_url(databasePath)
return databaseUrl
def getDatabaseInfo(self):
if self._databaseInfo is None:
return {
"url": "",
"initialized": None,
"version": None,
"error": None,
}
return self._databaseInfo.to_qml_dict()
def getCanContinue(self):
return (
self._databaseInfo is not None
and self._databaseInfo.error is None
and self._databaseInfo.version == 4 # noqa: PLR2004
and self._databaseInfo.initialized
)
def getCanConfirmSilently(self):
"""Whether the user can confirm database connection without a dialog popping up"""
if self.getUiMode() != _UiMode.SELECT:
return False
filepath = self._settingsDatabaseFile
if filepath is None:
return False
return (
self._databaseInfo is not None
and self._databaseInfo.error is None
and self.getDatabaseUrl() == db_path_to_sqlite_url(filepath)
)
uiMode = Property(str, getUiMode, setUiMode, notify=uiModeChanged)
selectFileUrl = Property(
QUrl,
getSelectFileUrl,
setSelectFileUrl,
notify=selectFileUrlChanged,
)
directoryUrl = Property(
QUrl,
getDirectoryUrl,
setDirectoryUrl,
notify=directoryUrlChanged,
)
filename = Property(str, getFilename, setFilename, notify=filenameChanged)
databaseUrl = Property(QUrl, getDatabaseUrl, None, notify=databaseUrlChanged)
databaseInfo = Property(dict, getDatabaseInfo, None, notify=databaseInfoChanged)
canContinue = Property(bool, getCanContinue, None, notify=canContinueChanged)
canConfirmSilently = Property(
bool,
getCanConfirmSilently,
None,
notify=canConfirmSilentlyChanged,
)
# endregion

View File

@ -1,31 +0,0 @@
from PySide6.QtCore import Property, QObject, Signal, Slot
from PySide6.QtQml import QmlElement
from core.database import Database
QML_IMPORT_NAME = "internal.ui.vm"
QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0
@QmlElement
class OverviewViewModel(QObject):
_void = Signal()
b30Changed = Signal()
def __init__(self):
super().__init__()
self._b30 = -1.0
self.reload()
@Slot()
def reload(self):
conn = Database()
b30 = conn.b30
self._b30 = b30 if b30 is not None else -1.0
self.b30Changed.emit()
@Property(float, fset=None)
def b30(self):
return self._b30

913
uv.lock generated
View File

@ -1,913 +0,0 @@
version = 1
revision = 3
requires-python = ">=3.9"
resolution-markers = [
"python_full_version >= '3.11' and sys_platform == 'darwin'",
"python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
"python_full_version == '3.10.*' and sys_platform == 'darwin'",
"python_full_version == '3.10.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version == '3.10.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.10.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
"python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version < '3.10' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
[[package]]
name = "arcaea-offline"
version = "0.2.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "beautifulsoup4" },
{ name = "sqlalchemy" },
{ name = "sqlalchemy-utils" },
{ name = "whoosh" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/9e/a6f86d8d852fef890f67f40bf5b867db32836f9fa267b49d1e3dd92629e7/arcaea-offline-0.2.2.tar.gz", hash = "sha256:0c747b630f5c8df5b0bdfc78d320b40a06f194dd8c0e4cc500700e8ca872617f", size = 33801, upload-time = "2024-03-31T16:20:16.014Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/21/cfd1651122abbabc2b3ac0c4cd22909b87e46e8e66860f0c182330ad8a40/arcaea_offline-0.2.2-py3-none-any.whl", hash = "sha256:59b6a55c4793419fa99fb019c39801953f83aa4a21a6105f3d4db5a823aba02d", size = 41812, upload-time = "2024-03-31T16:20:14.15Z" },
]
[[package]]
name = "arcaea-offline-ocr"
version = "0.0.99"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "attrs" },
{ name = "numpy" },
{ name = "opencv-python" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/90/7c/e5f89ee4a1e2221e5fa1d6b769d41c388d2470f3a29a628d30e7a65082da/arcaea_offline_ocr-0.0.99.tar.gz", hash = "sha256:9a3ab918de8e275e5190f27c7b4d5e6516ad355e3ebca79735ebd8ae95559126", size = 27521, upload-time = "2024-06-19T14:25:19.292Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0f/49/0f11fb00e7a75208fbfac799a5a77a4ace0ae2a8e5ffef791ca70ab6f262/arcaea_offline_ocr-0.0.99-py3-none-any.whl", hash = "sha256:f875da1a7e3f183fef3cfe3885635c40f3b4a9e3f9292dcffcdc008cad849fe3", size = 32165, upload-time = "2024-06-19T14:25:13.586Z" },
]
[[package]]
name = "arcaea-offline-pyside-ui"
version = "0.3.9"
source = { virtual = "." }
dependencies = [
{ name = "arcaea-offline" },
{ name = "arcaea-offline-ocr" },
{ name = "colorama" },
{ name = "exif" },
{ name = "materialyoucolor" },
{ name = "pillow" },
{ name = "pyside6" },
{ name = "rich" },
{ name = "structlog" },
]
[package.optional-dependencies]
dev = [
{ name = "imageio" },
{ name = "nuitka" },
{ name = "pre-commit" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "arcaea-offline", specifier = "==0.2.2" },
{ name = "arcaea-offline-ocr", specifier = "==0.0.99" },
{ name = "colorama", specifier = "~=0.4.6" },
{ name = "exif", specifier = "~=1.6.0" },
{ name = "imageio", marker = "extra == 'dev'" },
{ name = "materialyoucolor", specifier = "~=2.0.10" },
{ name = "nuitka", marker = "extra == 'dev'", specifier = "~=2.7.6" },
{ name = "pillow", specifier = "~=10.1.0" },
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.3.0" },
{ name = "pyside6", specifier = "==6.10.0" },
{ name = "rich", specifier = "~=14.2" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.2" },
{ name = "structlog", specifier = "~=25.4" },
]
provides-extras = ["dev"]
[[package]]
name = "attrs"
version = "23.1.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/97/90/81f95d5f705be17872843536b1868f351805acf6971251ff07c1b8334dbb/attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015", size = 212878, upload-time = "2023-04-16T10:48:18.214Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/eb/fcb708c7bf5056045e9e98f62b93bd7467eb718b0202e7698eb11d66416c/attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", size = 61160, upload-time = "2023-04-16T10:48:16.358Z" },
]
[[package]]
name = "beautifulsoup4"
version = "4.12.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "soupsieve" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/af/0b/44c39cf3b18a9280950ad63a579ce395dda4c32193ee9da7ff0aed547094/beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", size = 505113, upload-time = "2023-04-07T15:02:49.038Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/f4/a69c20ee4f660081a7dedb1ac57f29be9378e04edfcb90c526b923d4bebc/beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a", size = 142979, upload-time = "2023-04-07T15:02:50.77Z" },
]
[[package]]
name = "cfgv"
version = "3.4.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "distlib"
version = "0.4.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
]
[[package]]
name = "exif"
version = "1.6.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "plum-py" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/52/21/34c46fbda54648afdcc66f0eec0ac91675361017abcdcdd23bd88fa74cc5/exif-1.6.1.tar.gz", hash = "sha256:763599b89b9b67495713060a703f32d1874abf8f0628c9d77711c2c06a5f44c8", size = 24052, upload-time = "2024-12-10T04:38:35.812Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/07/a47a78be02b0a2d273358312f3e2eecc4981d4604f8a6e6b8ef27c1e9c2d/exif-1.6.1-py3-none-any.whl", hash = "sha256:2879830e2d8f0e5f1503110736bceb83a1e9c2121b32c23208c04284be5afbec", size = 30461, upload-time = "2024-12-10T04:38:33.547Z" },
]
[[package]]
name = "filelock"
version = "3.19.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
resolution-markers = [
"python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version < '3.10' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" },
]
[[package]]
name = "filelock"
version = "3.20.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
resolution-markers = [
"python_full_version >= '3.11' and sys_platform == 'darwin'",
"python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
"python_full_version == '3.10.*' and sys_platform == 'darwin'",
"python_full_version == '3.10.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version == '3.10.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.10.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
]
[[package]]
name = "greenlet"
version = "3.2.4"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/c0/93885c4106d2626bf51fdec377d6aef740dfa5c4877461889a7cf8e565cc/greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c", size = 269859, upload-time = "2025-08-07T13:16:16.003Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4d/f5/33f05dc3ba10a02dedb1485870cf81c109227d3d3aa280f0e48486cac248/greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d", size = 627610, upload-time = "2025-08-07T13:43:01.345Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b2/a7/9476decef51a0844195f99ed5dc611d212e9b3515512ecdf7321543a7225/greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58", size = 639417, upload-time = "2025-08-07T13:45:32.094Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bd/e0/849b9159cbb176f8c0af5caaff1faffdece7a8417fcc6fe1869770e33e21/greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4", size = 634751, upload-time = "2025-08-07T13:53:18.848Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5f/d3/844e714a9bbd39034144dca8b658dcd01839b72bb0ec7d8014e33e3705f0/greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433", size = 634020, upload-time = "2025-08-07T13:18:36.841Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/4c/f3de2a8de0e840ecb0253ad0dc7e2bb3747348e798ec7e397d783a3cb380/greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df", size = 582817, upload-time = "2025-08-07T13:18:35.48Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/89/80/7332915adc766035c8980b161c2e5d50b2f941f453af232c164cff5e0aeb/greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594", size = 1111985, upload-time = "2025-08-07T13:42:42.425Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/66/71/1928e2c80197353bcb9b50aa19c4d8e26ee6d7a900c564907665cf4b9a41/greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98", size = 1136137, upload-time = "2025-08-07T13:18:26.168Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/89/48/a5dc74dde38aeb2b15d418cec76ed50e1dd3d620ccda84d8199703248968/greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b", size = 281400, upload-time = "2025-08-07T14:02:20.263Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e5/44/342c4591db50db1076b8bda86ed0ad59240e3e1da17806a4cf10a6d0e447/greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb", size = 298533, upload-time = "2025-08-07T13:56:34.168Z" },
]
[[package]]
name = "identify"
version = "2.6.15"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" },
]
[[package]]
name = "imageio"
version = "2.37.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "numpy" },
{ name = "pillow" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0c/47/57e897fb7094afb2d26e8b2e4af9a45c7cf1a405acdeeca001fdf2c98501/imageio-2.37.0.tar.gz", hash = "sha256:71b57b3669666272c818497aebba2b4c5f20d5b37c81720e5e1a56d59c492996", size = 389963, upload-time = "2025-01-20T02:42:37.089Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/bd/b394387b598ed84d8d0fa90611a90bee0adc2021820ad5729f7ced74a8e2/imageio-2.37.0-py3-none-any.whl", hash = "sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed", size = 315796, upload-time = "2025-01-20T02:42:34.931Z" },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
resolution-markers = [
"python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version < '3.10' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
dependencies = [
{ name = "mdurl", marker = "python_full_version < '3.10'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.0.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
resolution-markers = [
"python_full_version >= '3.11' and sys_platform == 'darwin'",
"python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
"python_full_version == '3.10.*' and sys_platform == 'darwin'",
"python_full_version == '3.10.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version == '3.10.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.10.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
dependencies = [
{ name = "mdurl", marker = "python_full_version >= '3.10'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
]
[[package]]
name = "materialyoucolor"
version = "2.0.10"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/83/b3/8835bacf50ea32bf000f9118f60da4c8e5febe80ab5929be18ce99a4baea/materialyoucolor-2.0.10.tar.gz", hash = "sha256:31b4d407b9a4fd4b54b30559b0d0313f6d6da1f9d19b75546ed65da52671ea76", size = 250103, upload-time = "2025-01-09T07:56:43.05Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/52/36cf8bbb3989f31328ca2a06819b17c021052bcd97a7e703eadace1f225f/materialyoucolor-2.0.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4de08ee488369cf0e055ae17ac43c62684f618781939feaf0ad11bc32572a4b6", size = 316148, upload-time = "2025-01-09T09:02:52.364Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/96/4f3919588aa341f8152ad01de784924ac26059077b095069f2ca83525053/materialyoucolor-2.0.10-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad22b0fb6980e0995d5b8437cad3411f628e1ea88c223c94a20b59d5dbcbacb2", size = 206497, upload-time = "2025-01-09T08:34:31.692Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5e/91/05303aa40da51a71c4e828781a0a34b5ba8c88a9a0f2eec245c1257fe82e/materialyoucolor-2.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:997787ec4224f3e2d9c90e99d82db746f5581670564a22a0f182ad377e704761", size = 150420, upload-time = "2025-01-09T09:02:55.842Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/04/eb/78d067df3948bd6a347677c10d35d89b6092916f78e0b98c51d47fd080f5/materialyoucolor-2.0.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e9a4dd1e444a64ea54982be06314ce4611f3c38fdfae2c33cca7d106f6383288", size = 319432, upload-time = "2025-01-09T09:02:51.999Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/b1/04424f2bd811fe7e803d8a21cc28e8b4f9dea8b07cbc948318d4b4de3dd8/materialyoucolor-2.0.10-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:144fdd9dca272fee60a1779ce35acf24830cd3bc33ff20982ecba4936cb5bf91", size = 208262, upload-time = "2025-01-09T08:34:34.198Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/43/926ae82c04e5a5d02523f66e48af8021c0fdd8e01463a51e934137f7a5a0/materialyoucolor-2.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:f126196a051673707297e86b8d0fbd90805a552dc3ac79e1d90442cc3fc877cb", size = 151620, upload-time = "2025-01-09T09:02:52.028Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/69/e8/847974020cde0e09124d7fb51bf41aa92cc3a9cad83f83911e5d67c1d665/materialyoucolor-2.0.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0fc0b43a684536270177b71bf509b247dab4ff816e49d1ac1b124fc1a32e082e", size = 318810, upload-time = "2025-01-09T09:02:52.905Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fa/67/2f3e70d707f86be3c541327693db5105622aa6aa1079d48ff90876d2e650/materialyoucolor-2.0.10-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fe5bbc63124c3ea2f5560ea4a5250af72fce9ab55d29925e6bac7c7ec65329d", size = 208373, upload-time = "2025-01-09T08:34:35.656Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/f2/1119e97e741b33717e26c494b4aa3c5ca57d770df4fa42707b698bfb7588/materialyoucolor-2.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:aedc8596fd7583fd9e23f3e6d4f7b9defd7d5ef6e7889967814b9900c1e3d5b3", size = 151716, upload-time = "2025-01-09T09:02:57.1Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/49/c4517231ec21bdd91a301cb40fdf86c632a95b10d86bc3278bd5d619480c/materialyoucolor-2.0.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:71de9c581c8227c3010c61108a00af9a007c96f99358fdb0628626d0ade817d9", size = 318890, upload-time = "2025-01-09T09:02:52.276Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4d/62/807bea1db749dc45debc2afa51ea4b27f5a219fbe73f6458c2bae2a15427/materialyoucolor-2.0.10-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:978748eca7725d8b569cdecfd9f86a8e34bf946182f79a48b059c98a10f1a0de", size = 208339, upload-time = "2025-01-09T08:34:38.213Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/01/05075b959b228c5c99c80703b7c63532ac6ee49abdf8054077e70908d6e3/materialyoucolor-2.0.10-cp313-cp313-win_amd64.whl", hash = "sha256:2ffc8963a2f10d8acb6776e60e97f0e7929d3392eb282cf2bc63b8e56cd930b3", size = 151751, upload-time = "2025-01-09T09:02:53.299Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/56/29e59a5bcc92f0dc0a1de728e711edfcd971a6ef269a97cf461642fc339d/materialyoucolor-2.0.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e4827307b1c78897f97f7989237ef688a5c21663a63d327d7cec8e47f107e509", size = 316289, upload-time = "2025-01-09T09:02:53.101Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3c/be/039f46e565b77ce088902c19352c073e16c34a4a8743c95fcad1a537d81f/materialyoucolor-2.0.10-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8cebae884c6106584e1792b910d9588778353255a1a8156cb634e21f1b8670e", size = 206468, upload-time = "2025-01-09T07:56:41.508Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/b9/d8946d2d9901ca7bd088ea8efcdc975c73f51766b234fc3a83b821988b2b/materialyoucolor-2.0.10-cp39-cp39-win_amd64.whl", hash = "sha256:4c5141fdd973ab386ebd0242072a7faab28d19cc083ee0d14f5b70c31ad75d60", size = 150332, upload-time = "2025-01-09T09:02:56.525Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "nodeenv"
version = "1.9.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
]
[[package]]
name = "nuitka"
version = "2.7.16"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "ordered-set" },
{ name = "zstandard" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e5/87/36bae02fb19e98cdfa5778faf14f90acff396b9b17586355fbefbe971631/Nuitka-2.7.16.tar.gz", hash = "sha256:768909faf365b21ae4777727fc4ae88efc29239c664bd177061fc907e263e0fa", size = 3889164, upload-time = "2025-09-19T09:47:21.982Z" }
[[package]]
name = "numpy"
version = "1.26.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/78/23/f78fd8311e0f710fe1d065d50b92ce0057fe877b8ed7fd41b28ad6865bfc/numpy-1.26.1.tar.gz", hash = "sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe", size = 15651806, upload-time = "2023-10-14T20:15:36.407Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/34/11/055802bf85abbb61988e6313e8b0a85167ee0795fc2c6141ee5b539e7b11/numpy-1.26.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af", size = 20621341, upload-time = "2023-10-14T19:39:25.82Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e3/63/fd76159cb76c682171e3bf50ed0ee8704103035a9347684a2ec0914b84a1/numpy-1.26.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575", size = 13960979, upload-time = "2023-10-14T19:39:49.073Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a2/e0/008311a728bd77084b207840bbeb1e5e3a8412994851ec06856413ca7a7a/numpy-1.26.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244", size = 14169086, upload-time = "2023-10-14T19:40:11.508Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/5e/cb38e3d1916cc29880c84a9332a9122a8f49a7b57ec7aea63e0f678587a2/numpy-1.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67", size = 18178951, upload-time = "2023-10-14T19:40:40.441Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b8/d9/58db30222fcfa35411d83150d313c5028680298ec8c69f85d0fcec4d4664/numpy-1.26.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2", size = 18016432, upload-time = "2023-10-14T19:41:08.368Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/8f/7df6e01a44742088aacc985da04bbc2019575fe684b7b9d9057f4f0e22e0/numpy-1.26.1-cp310-cp310-win32.whl", hash = "sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297", size = 20757353, upload-time = "2023-10-14T19:41:40.163Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/09/fe9282ffb0217176b0185900945189b6beaec4f94ff46afb76bcd9b68e30/numpy-1.26.1-cp310-cp310-win_amd64.whl", hash = "sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab", size = 15797631, upload-time = "2023-10-14T19:42:05.418Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/9e/2d6d9d8f0ec910539ee721f86f23489a0eedb25bd51f4268ae0899f6a3ab/numpy-1.26.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a", size = 20621224, upload-time = "2023-10-14T19:42:37.88Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e8/06/0512e2582fd27bb7b358fa1e4ffc0f6c89c89f5ada31df58c5fa93171098/numpy-1.26.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9", size = 13991582, upload-time = "2023-10-14T19:42:59.875Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/21/ae3276d5f7c255a7c821d54d94c1270448b9a5936b618d7a8f5fb3c91c02/numpy-1.26.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3", size = 14175129, upload-time = "2023-10-14T19:43:22.596Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8a/08/a7e5dadc21fe193baea5f257e11b7b70cc27a89692fc9e3ed690e55cc4b6/numpy-1.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974", size = 18190343, upload-time = "2023-10-14T19:43:50.377Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/14/c2/68bbc08cd8af52f52f7d978c97062eab1e627f5423d591ba67f732b5f265/numpy-1.26.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c", size = 18028828, upload-time = "2023-10-14T19:44:19.972Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/79/9e/73af202f778cae92d3789cc06e6f2be14102e9c8f9af9bbe2cc6c81b326f/numpy-1.26.1-cp311-cp311-win32.whl", hash = "sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b", size = 20760215, upload-time = "2023-10-14T19:44:52.458Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/0f/3f712cd84371636c5375d2dd70e7514d264cec6bdfc3d7997a4236e9f948/numpy-1.26.1-cp311-cp311-win_amd64.whl", hash = "sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53", size = 15799803, upload-time = "2023-10-14T19:45:17.161Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/00/adb57a4974931c97a9bbbc92fd2cc998aa47569fcd7fb65ded4b81b72455/numpy-1.26.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f", size = 20327461, upload-time = "2023-10-14T19:45:48.113Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/65/5a/87a862acf8aa3ef5896577db5baf29e56df0fdbda025fbb67fd5039794d1/numpy-1.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24", size = 13679398, upload-time = "2023-10-14T19:46:10.162Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/70/cd8a18c109a8ba45477e4bddcff6cdbb31b1694148eb152087a3c264a4ae/numpy-1.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e", size = 13875842, upload-time = "2023-10-14T19:46:32.163Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/07/c0/ccbb2a4c75b283d6100400a907087bfa4d89cee9df73fa6af85268115d81/numpy-1.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124", size = 17889868, upload-time = "2023-10-14T19:47:00.785Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/4b/02caaf93a1afaebb12c053df360c3bec4b1818a39d24f4eca0efe1d7ab5f/numpy-1.26.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c", size = 17720430, upload-time = "2023-10-14T19:47:29.726Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/65/64/89efc3809fb6a733a6e10e7fe6498e404cabf57f063d232ded3d7cc4ef08/numpy-1.26.1-cp312-cp312-win32.whl", hash = "sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66", size = 19967092, upload-time = "2023-10-14T19:48:00.998Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/32/95/908d0caa051beae4f7c77652dbbeb781e7b717f3040c5c5fcaed4d3ed08f/numpy-1.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7", size = 15507291, upload-time = "2023-10-14T19:48:25.741Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/97/ef9a9cfabbf97092475dda30179bcd86b261e678d8968003f7e8effc4fdd/numpy-1.26.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e", size = 20626258, upload-time = "2023-10-14T19:48:57.34Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/86/54/fafc9282d2510aa56694897029a96e524c72dfe9a1148294fe8f2bcbe974/numpy-1.26.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617", size = 13965659, upload-time = "2023-10-14T19:49:19.936Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9b/07/f1d5e7a4840c96f7d44be31c39d1f8025fca32cdebf2bce36511a4a64e82/numpy-1.26.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e", size = 14176423, upload-time = "2023-10-14T19:49:43.03Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/89/ac/53100546dcd9aa400a73c7770b13cad9a3b18bf83433499e36b5efe9850f/numpy-1.26.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908", size = 18186982, upload-time = "2023-10-14T19:50:11.286Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/20/ee/8fac67ed6c74a246e3634f2e7d709f54478b09d0f5dd97361ad6b03eb5a8/numpy-1.26.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5", size = 18019713, upload-time = "2023-10-14T19:50:38.863Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1d/36/3593d482565bdb3d6a016565c56edaa18fac20f71bb18741ee030140a793/numpy-1.26.1-cp39-cp39-win32.whl", hash = "sha256:d1d2c6b7dd618c41e202c59c1413ef9b2c8e8a15f5039e344af64195459e3104", size = 20770117, upload-time = "2023-10-14T19:51:10.193Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/ed/022fc4106f6d97e41e156201274138e0369b27dbfc8c206034f24ebd97d9/numpy-1.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2", size = 15803923, upload-time = "2023-10-14T19:51:35.406Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/02/40/c7b748b9d247f7d062ed6735d5f484a7e52fe13546318872a489cfb810bf/numpy-1.26.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668", size = 20445721, upload-time = "2023-10-14T19:52:08.649Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/37/ba/2512f7b48e619259ee38d4f803fdc6ed92ec9f4975772378de1eef6e6a5c/numpy-1.26.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42", size = 17995458, upload-time = "2023-10-14T19:52:36.55Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/be/13/28dad1f91605d519b6899f7a26ab61938ecbd9e1770e219e7030b15d58e4/numpy-1.26.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f", size = 15697297, upload-time = "2023-10-14T19:53:04.904Z" },
]
[[package]]
name = "opencv-python"
version = "4.8.1.78"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "numpy" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c0/52/9fe76a56e01078a612812b40764a7b138f528b503f7653996c6cfadfa8ec/opencv-python-4.8.1.78.tar.gz", hash = "sha256:cc7adbbcd1112877a39274106cb2752e04984bc01a031162952e97450d6117f6", size = 92094255, upload-time = "2023-09-28T11:10:40.539Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/58/7ee92b21cb98689cbe28c69e3cf8ee51f261bfb6bc904ae578736d22d2e7/opencv_python-4.8.1.78-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:91d5f6f5209dc2635d496f6b8ca6573ecdad051a09e6b5de4c399b8e673c60da", size = 54739488, upload-time = "2023-09-28T11:00:22.939Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a1/f6/57de91ea40c670527cd47a6548bf2cbedc68cec57c041793b256356abad7/opencv_python-4.8.1.78-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31f47e05447da8b3089faa0a07ffe80e114c91ce0b171e6424f9badbd1c5cd", size = 33127231, upload-time = "2023-09-28T11:00:31.878Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c7/a5/dd3735d08c1afc2e801f564f6602392cd86cf59bcb5a6712582ad0610a22/opencv_python-4.8.1.78-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9814beca408d3a0eca1bae7e3e5be68b07c17ecceb392b94170881216e09b319", size = 41016265, upload-time = "2023-09-28T11:00:39.652Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/8a/b2f7e1a434d56bf1d7570fc5941ace0847404e1032d7f1f0b8fed896568d/opencv_python-4.8.1.78-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c406bdb41eb21ea51b4e90dfbc989c002786c3f601c236a99c59a54670a394", size = 61712178, upload-time = "2023-09-28T11:00:49.714Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/13/92/6f3194559d4e2a17826240f2466076728f4c92a2d124a32bfd51ca070019/opencv_python-4.8.1.78-cp37-abi3-win32.whl", hash = "sha256:a7aac3900fbacf55b551e7b53626c3dad4c71ce85643645c43e91fcb19045e47", size = 28281329, upload-time = "2023-09-28T11:00:57.464Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/d2/3e8c13ffc37ca5ebc6f382b242b44acb43eb489042e1728407ac3904e72f/opencv_python-4.8.1.78-cp37-abi3-win_amd64.whl", hash = "sha256:b983197f97cfa6fcb74e1da1802c7497a6f94ed561aba6980f1f33123f904956", size = 38065100, upload-time = "2023-09-28T11:01:04.856Z" },
]
[[package]]
name = "ordered-set"
version = "4.1.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4c/ca/bfac8bc689799bcca4157e0e0ced07e70ce125193fc2e166d2e685b7e2fe/ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8", size = 12826, upload-time = "2022-01-26T14:38:56.6Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/33/55/af02708f230eb77084a299d7b08175cff006dea4f2721074b92cdb0296c0/ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", size = 7634, upload-time = "2022-01-26T14:38:48.677Z" },
]
[[package]]
name = "pillow"
version = "10.1.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/80/d7/c4b258c9098b469c4a4e77b0a99b5f4fd21e359c2e486c977d231f52fc71/Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38", size = 50781360, upload-time = "2023-10-15T13:03:15.681Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/64/1a4fb688fb4e1a8c621b49ac398858d49f0d6c9289b06027a3f0d4027568/Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106", size = 3484047, upload-time = "2023-10-15T13:01:25.469Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/92/a4/c164eb1f692585982e1aa9bf2c1126da9721c2193cd1aba1eaf46fe7f1d7/Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273", size = 3294761, upload-time = "2023-10-15T13:01:28.27Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/07/d1/ffdda319c2f62fb20b3ece231caecedcc8af42fc2c0d4900dca92996c356/Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666", size = 3424149, upload-time = "2023-10-15T13:01:30.76Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/95/7b/71e2665760b5c33af00fa9bb6d6bca068b51bf021a4ceaeee03e18689f51/Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2", size = 3499990, upload-time = "2023-10-15T13:01:32.811Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/f0/446e3568f8365dff2c37efd35411a61ad72aa1b613ba4ac123bbacf0e5ce/Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593", size = 3475329, upload-time = "2023-10-15T13:01:34.93Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e5/b9/5c6ad3241f1ccca4b781dfeddbab2dac4480f95aedc351a0e60c9f4c8aa9/Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db", size = 3621698, upload-time = "2023-10-15T13:01:37.19Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/ce/745220339fed3a5a0052b443c482625ec6c889da1fef0f9791e30d571e19/Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f", size = 3561230, upload-time = "2023-10-15T13:01:40.922Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/ff/2c4ffdccc9a29d5f010e59abd7d62d172e84472000f32f1d64e177453906/Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818", size = 3600892, upload-time = "2023-10-15T13:01:43.089Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/7e/18ffce67b6e7637eead295b8a78d293d170d404a633010c3549da9a5e674/Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57", size = 2609029, upload-time = "2023-10-15T13:01:44.598Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/07/22/93d6b5aa5917d09ec7088a2c4d1821848f3f95fbdc2633ba9d9fc28444a1/Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7", size = 3484089, upload-time = "2023-10-15T13:01:46.981Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c3/5b/6bcfd0c2631d1ce4bb29ea597556ed2783404c5ad38635caf7b3f2b19073/Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7", size = 3294744, upload-time = "2023-10-15T13:01:49.084Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/ae/6b7673ae38cbd4821742acdb209e3e52c564dbe8ef8409d64d68ea0f9e6f/Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610", size = 3424164, upload-time = "2023-10-15T13:01:51.284Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cd/fa/87c27a90d97600edc639b06c14c63c8dac709e13e04714eb1dc949788f41/Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839", size = 3499981, upload-time = "2023-10-15T13:01:53.594Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5f/0a/8301b9384cbbd8542c2a5540fda1bce18c3203a0cc7becc9073bdee79ccb/Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172", size = 3475329, upload-time = "2023-10-15T13:01:55.827Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/d8/f31dd84b4363b5f24c71b25a13ec3855f5ff233e07e1c3f1f8e979e12be2/Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061", size = 3621689, upload-time = "2023-10-15T13:01:57.56Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/20/5000e1a9696ee28c18c4bd158aaaed0dd65d3b13b6547c43b29f9851c2cc/Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262", size = 3561247, upload-time = "2023-10-15T13:01:59.326Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cf/f7/4c4428d56df4cf7dfc6b9fc9a8b0268cdbca7c6b5130bc090fbf7562b223/Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992", size = 3600912, upload-time = "2023-10-15T13:02:01.139Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b1/38/31def4109acd4db10672df6f806b175c0d21458f845ddc0890e43238ba7c/Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a", size = 2609067, upload-time = "2023-10-15T13:02:03.443Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/40/80/9df9bb85b3209d62b85064c956a819e9e06279c6accf7e0f6a89ff4d9d6d/Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b", size = 3483728, upload-time = "2023-10-15T13:02:05.418Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3d/6f/6f716f16bcb9bf39f54b9d2d993f535a0ee42cc0fec973c80839b0720ca2/Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d", size = 3293897, upload-time = "2023-10-15T13:02:07.61Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/33/c6/1abfffbbdd803a44fb4aa009218502a0e353bfcc96b045a24ad1a194c705/Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27", size = 3424287, upload-time = "2023-10-15T13:02:09.756Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/40/ff02ca10167c3d68c84b61142a5acd28b09ea5f833fffe9a77c4d8d5f96a/Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312", size = 3500620, upload-time = "2023-10-15T13:02:11.483Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/15/57/925008390581a15c024dd57206ca622fd0ea85fbd194169efc3ff48ecda1/Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de", size = 3475368, upload-time = "2023-10-15T13:02:13.353Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/44/ed/a6f7dcd6631ec55b8d26c6a8bca762b04b7025daa3aa67e860a886abed89/Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651", size = 3621896, upload-time = "2023-10-15T13:02:15.053Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3f/e7/cf988402f838843362c471ca2a240d4be46adcabc508be4e70ba7721e9ee/Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b", size = 3561709, upload-time = "2023-10-15T13:02:16.745Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/04/3d/bf353b366d1a39a95ff861129ba3af8499d48e06634d50c10cf4136cbe7d/Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f", size = 3602069, upload-time = "2023-10-15T13:02:18.634Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/32/e4/978865107d097dd9cb650331676d8dc29ed9fcd0aaab46486e9d6e5123f0/Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996", size = 2609160, upload-time = "2023-10-15T13:02:20.875Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/2a/b87cb8b319d3d755eae0ef61129bfd83f536ef121891001733baefecebe2/Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099", size = 3484069, upload-time = "2023-10-15T13:02:41.339Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/91/67/9bf0b3c0f43d5e1aa6795318feefad2ec5e5a10b51454b047dc608619fbb/Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616", size = 3294823, upload-time = "2023-10-15T13:02:43.035Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/8c/a4abc8937041ea811fd81a4bab9205ebf78f9a4edfdb6b1a1623a10082d2/Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb", size = 3424109, upload-time = "2023-10-15T13:02:44.83Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9f/3a/ada56d489446dbb7679d242bfd7bb159cee8a7989c34dd34045103d5280d/Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219", size = 3500040, upload-time = "2023-10-15T13:02:46.981Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/54/3413b3498053af77efebc57d99a6152c67a56b3627c6cbf3a4f9da3da30f/Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34", size = 3475354, upload-time = "2023-10-15T13:02:48.68Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5c/dc/acccca38a87272cb2eed372f112595439418dfb6119770b04dc06d3b78bd/Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd", size = 3621722, upload-time = "2023-10-15T13:02:50.572Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fd/99/691942ab88b3d22c36c704f2c52b3b9d93fb86a05eaeabe006a216c3e882/Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28", size = 3561221, upload-time = "2023-10-15T13:02:52.431Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/3e/849a9958af171445cca8112d0938df7e40337c2d5b836d999edcf1c6bb27/Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2", size = 3600943, upload-time = "2023-10-15T13:02:54.264Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/98/56/0eb5a210de84b8c73720e6b691fe8fe1a99d62c8e8f0564b95f2c12988e8/Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256", size = 2609113, upload-time = "2023-10-15T13:02:56.468Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/68/39/18abea241c252a9a563e97f97af69c0f0ef34a88a84720db5b0111281a73/Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7", size = 3437796, upload-time = "2023-10-15T13:02:58.415Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/69/6ee650d047770ec1ee8844b62b4401b4e4fbac3959a7663af1b62c00fdca/Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba", size = 3413457, upload-time = "2023-10-15T13:03:00.297Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/09/07957ab28bac4e62d1417a35b3bed2298c1cb4f2869e6ba5f95ae7ccf08d/Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4", size = 3503087, upload-time = "2023-10-15T13:03:02.342Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/b4/627903ee2d02ab8685918d20a644feb32ad6c5e181aedebf57182838abca/Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9", size = 2609263, upload-time = "2023-10-15T13:03:04.461Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fe/0d/d6845b86ece023aafe023aa303092b5c7686bd88d60464a93316b28e92d5/Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e", size = 3437731, upload-time = "2023-10-15T13:03:06.16Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/07/25/723cae5b564f0f1301176b38a2ba2f36dbb5fad658ad45972496e2f24298/Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412", size = 3413318, upload-time = "2023-10-15T13:03:08.035Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/90/53/ed3059ab37e7633c6cc7c6a2decd906f3f3e104b732dfb71ac78096e1008/Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b", size = 3503107, upload-time = "2023-10-15T13:03:09.891Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/32/2d/5a62c0924dbbb7585c9e39acbfef3809109e8e08b9a3c9d8457c77132758/Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f", size = 2609332, upload-time = "2023-10-15T13:03:11.673Z" },
]
[[package]]
name = "platformdirs"
version = "4.4.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
resolution-markers = [
"python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version < '3.10' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" },
]
[[package]]
name = "platformdirs"
version = "4.5.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
resolution-markers = [
"python_full_version >= '3.11' and sys_platform == 'darwin'",
"python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
"python_full_version == '3.10.*' and sys_platform == 'darwin'",
"python_full_version == '3.10.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version == '3.10.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.10.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
]
[[package]]
name = "plum-py"
version = "0.8.7"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3a/2a/a650b0cde5a15f4a9375df43bd7eacaffb65b30c8395b6b6cd84df2bf7ae/plum-py-0.8.7.tar.gz", hash = "sha256:40a25a70f7fd213f57cdd1decc65874df2d213d19d63478a1addcca748c13c4e", size = 116231, upload-time = "2024-05-12T02:01:48.18Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2f/da/8a9bdf5d3ea4b13d3142e0c77d6e85e4c2668aef6e4b9d5e9a6619049bdc/plum_py-0.8.7-py3-none-any.whl", hash = "sha256:d791f059ef159adbe3c3b036557b5b431fa07db226a9de106a74e5e57441741d", size = 69991, upload-time = "2023-08-14T21:56:31.953Z" },
]
[[package]]
name = "pre-commit"
version = "4.3.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "cfgv" },
{ name = "identify" },
{ name = "nodeenv" },
{ name = "pyyaml" },
{ name = "virtualenv" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "pyside6"
version = "6.10.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "pyside6-addons" },
{ name = "pyside6-essentials" },
{ name = "shiboken6" },
]
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e6/98/84b16f78b5d92dd234fb1eb9890a350a5b0c83d985bb8c44a92f813a2d02/pyside6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:c2cbc5dc2a164e3c7c51b3435e24203e90e5edd518c865466afccbd2e5872bb0", size = 558115, upload-time = "2025-10-08T09:47:09.246Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4e/76/0961c8c5653ecb60a6881b649dcb6b71a6be5bd1c8d441ecc48ac7f50b1a/pyside6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ae8c3c8339cd7c3c9faa7cc5c52670dcc8662ccf4b63a6fed61c6345b90c4c01", size = 557762, upload-time = "2025-10-08T09:47:11.819Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c8/73/6187502fff8b6599443d15c46dd900b2ded24be5aacb2becce33f6faf566/pyside6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:9f402f883e640048fab246d36e298a5e16df9b18ba2e8c519877e472d3602820", size = 558299, upload-time = "2025-10-08T09:47:14.255Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/67/94794ebaf198bbdb35cb77f19f38370f9b323b036ab149874bc33c38faab/pyside6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:70a8bcc73ea8d6baab70bba311eac77b9a1d31f658d0b418e15eb6ea36c97e6f", size = 564367, upload-time = "2025-10-08T09:47:16.287Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1d/cc/552331d413c1b933d54ed45e33cc7ff29d0b239677975fe2528e7ac8bfbc/pyside6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:4b709bdeeb89d386059343a5a706fc185cee37b517bda44c7d6b64d5fdaf3339", size = 548826, upload-time = "2025-10-08T09:47:18.399Z" },
]
[[package]]
name = "pyside6-addons"
version = "6.10.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "pyside6-essentials" },
{ name = "shiboken6" },
]
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/23/9fbdec2ce16244ac3fe28e6d44c39c70465c93a03325939a792fd00fde7f/pyside6_addons-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:88e61e21ee4643cdd9efb39ec52f4dc1ac74c0b45c5b7fa453d03c094f0a8a5c", size = 322248256, upload-time = "2025-10-08T09:47:37.844Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/b8/d129210f2c7366b4e1bf5bb6230be42052b29e8ba1b1d7db6ef333cf5a39/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:08d4ed46c4c9a353a9eb84134678f8fdd4ce17fb8cce2b3686172a7575025464", size = 170238987, upload-time = "2025-10-08T09:47:51.446Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cf/ae/ede1edd009395092219f3437d2ee59f9ba93739c28c040542ed47c6cc831/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:15d32229d681be0bba1b936c4a300da43d01e1917ada5b57f9e03a387c245ab0", size = 165939425, upload-time = "2025-10-08T09:48:02.073Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/5d/a3c32f85ac7f905c95679967c0ddda0ba043c273b75623cc90d8185064e4/pyside6_addons-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:99d93a32c17c5f6d797c3b90dd58f2a8bae13abde81e85802c34ceafaee11859", size = 164814172, upload-time = "2025-10-08T09:48:12.891Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a3/2a/4ff71b09571202c8e1320c45276fc1d0cd81ee53107dfc17bb22d4243f88/pyside6_addons-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:92536427413f3b6557cf53f1a515cd766725ee46a170aff57ad2ff1dfce0ffb1", size = 34104251, upload-time = "2025-10-08T09:48:18.287Z" },
]
[[package]]
name = "pyside6-essentials"
version = "6.10.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "shiboken6" },
]
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e5/55/bad02ab890c8b8101abef0db4a2e5304be78a69e23a438e4d8555b664467/pyside6_essentials-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:003e871effe1f3e5b876bde715c15a780d876682005a6e989d89f48b8b93e93a", size = 105034090, upload-time = "2025-10-08T09:48:24.944Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5c/75/e17efc7eb900993e0e3925885635c6cf373c817196f09bcbcc102b00ac94/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1d5e013a8698e37ab8ef360e6960794eb5ef20832a8d562e649b8c5a0574b2d8", size = 76362150, upload-time = "2025-10-08T09:48:31.849Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/06/62/fbd1e81caafcda97b147c03f5b06cfaadd8da5fa8298f527d2ec648fa5b7/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b1dd0864f0577a448fb44426b91cafff7ee7cccd1782ba66491e1c668033f998", size = 75454169, upload-time = "2025-10-08T09:48:38.21Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/3a/d8211d17e6ca70f641c6ebd309f08ef18930acda60e74082c75875a274da/pyside6_essentials-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:fc167eb211dd1580e20ba90d299e74898e7a5a1306d832421e879641fc03b6fe", size = 74361794, upload-time = "2025-10-08T09:48:44.335Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/e9/0e22e3c10325c4ff09447fadb43f7962afb82cef0b65358f5704251c6b32/pyside6_essentials-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:6dd0936394cb14da2fd8e869899f5e0925a738b1c8d74c2f22503720ea363fb1", size = 55099467, upload-time = "2025-10-08T09:48:50.902Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" },
]
[[package]]
name = "rich"
version = "14.2.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "markdown-it-py", version = "3.0.0", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }, marker = "python_full_version < '3.10'" },
{ name = "markdown-it-py", version = "4.0.0", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }, marker = "python_full_version >= '3.10'" },
{ name = "pygments" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
]
[[package]]
name = "ruff"
version = "0.14.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ee/34/8218a19b2055b80601e8fd201ec723c74c7fe1ca06d525a43ed07b6d8e85/ruff-0.14.2.tar.gz", hash = "sha256:98da787668f239313d9c902ca7c523fe11b8ec3f39345553a51b25abc4629c96", size = 5539663, upload-time = "2025-10-23T19:37:00.956Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/16/dd/23eb2db5ad9acae7c845700493b72d3ae214dce0b226f27df89216110f2b/ruff-0.14.2-py3-none-linux_armv6l.whl", hash = "sha256:7cbe4e593505bdec5884c2d0a4d791a90301bc23e49a6b1eb642dd85ef9c64f1", size = 12533390, upload-time = "2025-10-23T19:36:18.044Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/8c/5f9acff43ddcf3f85130d0146d0477e28ccecc495f9f684f8f7119b74c0d/ruff-0.14.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8d54b561729cee92f8d89c316ad7a3f9705533f5903b042399b6ae0ddfc62e11", size = 12887187, upload-time = "2025-10-23T19:36:22.664Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/99/fa/047646491479074029665022e9f3dc6f0515797f40a4b6014ea8474c539d/ruff-0.14.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c8753dfa44ebb2cde10ce5b4d2ef55a41fb9d9b16732a2c5df64620dbda44a3", size = 11925177, upload-time = "2025-10-23T19:36:24.778Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/15/8b/c44cf7fe6e59ab24a9d939493a11030b503bdc2a16622cede8b7b1df0114/ruff-0.14.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d0bbeffb8d9f4fccf7b5198d566d0bad99a9cb622f1fc3467af96cb8773c9e3", size = 12358285, upload-time = "2025-10-23T19:36:26.979Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/01/47701b26254267ef40369aea3acb62a7b23e921c27372d127e0f3af48092/ruff-0.14.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7047f0c5a713a401e43a88d36843d9c83a19c584e63d664474675620aaa634a8", size = 12303832, upload-time = "2025-10-23T19:36:29.192Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/5c/ae7244ca4fbdf2bee9d6405dcd5bc6ae51ee1df66eb7a9884b77b8af856d/ruff-0.14.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bf8d2f9aa1602599217d82e8e0af7fd33e5878c4d98f37906b7c93f46f9a839", size = 13036995, upload-time = "2025-10-23T19:36:31.861Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/4c/0860a79ce6fd4c709ac01173f76f929d53f59748d0dcdd662519835dae43/ruff-0.14.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1c505b389e19c57a317cf4b42db824e2fca96ffb3d86766c1c9f8b96d32048a7", size = 14512649, upload-time = "2025-10-23T19:36:33.915Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7f/7f/d365de998069720a3abfc250ddd876fc4b81a403a766c74ff9bde15b5378/ruff-0.14.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a307fc45ebd887b3f26b36d9326bb70bf69b01561950cdcc6c0bdf7bb8e0f7cc", size = 14088182, upload-time = "2025-10-23T19:36:36.983Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6c/ea/d8e3e6b209162000a7be1faa41b0a0c16a133010311edc3329753cc6596a/ruff-0.14.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61ae91a32c853172f832c2f40bd05fd69f491db7289fb85a9b941ebdd549781a", size = 13599516, upload-time = "2025-10-23T19:36:39.208Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fa/ea/c7810322086db68989fb20a8d5221dd3b79e49e396b01badca07b433ab45/ruff-0.14.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1967e40286f63ee23c615e8e7e98098dedc7301568bd88991f6e544d8ae096", size = 13272690, upload-time = "2025-10-23T19:36:41.453Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a9/39/10b05acf8c45786ef501d454e00937e1b97964f846bf28883d1f9619928a/ruff-0.14.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2877f02119cdebf52a632d743a2e302dea422bfae152ebe2f193d3285a3a65df", size = 13496497, upload-time = "2025-10-23T19:36:43.61Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/59/a1/1f25f8301e13751c30895092485fada29076e5e14264bdacc37202e85d24/ruff-0.14.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e681c5bc777de5af898decdcb6ba3321d0d466f4cb43c3e7cc2c3b4e7b843a05", size = 12266116, upload-time = "2025-10-23T19:36:45.625Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5c/fa/0029bfc9ce16ae78164e6923ef392e5f173b793b26cc39aa1d8b366cf9dc/ruff-0.14.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e21be42d72e224736f0c992cdb9959a2fa53c7e943b97ef5d081e13170e3ffc5", size = 12281345, upload-time = "2025-10-23T19:36:47.618Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a5/ab/ece7baa3c0f29b7683be868c024f0838770c16607bea6852e46b202f1ff6/ruff-0.14.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b8264016f6f209fac16262882dbebf3f8be1629777cf0f37e7aff071b3e9b92e", size = 12629296, upload-time = "2025-10-23T19:36:49.789Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a4/7f/638f54b43f3d4e48c6a68062794e5b367ddac778051806b9e235dfb7aa81/ruff-0.14.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ca36b4cb4db3067a3b24444463ceea5565ea78b95fe9a07ca7cb7fd16948770", size = 13371610, upload-time = "2025-10-23T19:36:51.882Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8d/35/3654a973ebe5b32e1fd4a08ed2d46755af7267da7ac710d97420d7b8657d/ruff-0.14.2-py3-none-win32.whl", hash = "sha256:41775927d287685e08f48d8eb3f765625ab0b7042cc9377e20e64f4eb0056ee9", size = 12415318, upload-time = "2025-10-23T19:36:53.961Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/30/3758bcf9e0b6a4193a6f51abf84254aba00887dfa8c20aba18aa366c5f57/ruff-0.14.2-py3-none-win_amd64.whl", hash = "sha256:0df3424aa5c3c08b34ed8ce099df1021e3adaca6e90229273496b839e5a7e1af", size = 13565279, upload-time = "2025-10-23T19:36:56.578Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2e/5d/aa883766f8ef9ffbe6aa24f7192fb71632f31a30e77eb39aa2b0dc4290ac/ruff-0.14.2-py3-none-win_arm64.whl", hash = "sha256:ea9d635e83ba21569fbacda7e78afbfeb94911c9434aff06192d9bc23fd5495a", size = 12554956, upload-time = "2025-10-23T19:36:58.714Z" },
]
[[package]]
name = "shiboken6"
version = "6.10.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fd/78/3e730aea82089dd82b1e092bc265778bda329459e6ad9b7134eec5fff3f2/shiboken6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:7a5f5f400ebfb3a13616030815708289c2154e701a60b9db7833b843e0bee543", size = 476535, upload-time = "2025-10-08T09:49:08Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/09/4ffa3284a17b6b765d45b41c9a7f1b2cde6c617c853ac6f170fb62bbbece/shiboken6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:e612734da515d683696980107cdc0396a3ae0f07b059f0f422ec8a2333810234", size = 271098, upload-time = "2025-10-08T09:49:09.47Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/31/29/00e26f33a0fb259c2edce9c761a7a438d7531ca514bdb1a4c072673bd437/shiboken6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b01377e68d14132360efb0f4b7233006d26aa8ae9bb50edf00960c2a5f52d148", size = 267698, upload-time = "2025-10-08T09:49:10.694Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/11/30/e4624a7e3f0dc9796b701079b77defcce0d32d1afc86bb1d0df04bc3d9e2/shiboken6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:0bc5631c1bf150cbef768a17f5f289aae1cb4db6c6b0c19b2421394e27783717", size = 1234227, upload-time = "2025-10-08T09:49:12.774Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dd/e5/0ab862005ea87dc8647ba958a3099b3b0115fd6491c65da5c5a0f6364db1/shiboken6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:dfc4beab5fec7dbbebbb418f3bf99af865d6953aa0795435563d4cbb82093b61", size = 1794775, upload-time = "2025-10-08T09:49:14.641Z" },
]
[[package]]
name = "soupsieve"
version = "2.8"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.20"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/88/85/b7fbadd64b30e9d4b882dd323341fc263687bc95b22edec262b12cac8fd7/SQLAlchemy-2.0.20.tar.gz", hash = "sha256:ca8a5ff2aa7f3ade6c498aaafce25b1eaeabe4e42b73e25519183e4566a16fc6", size = 9437690, upload-time = "2023-08-15T18:50:38.577Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fe/1c/d3ca3095528c262125251ee42782086d1d2bad4e59adf0dbdd9540147c23/SQLAlchemy-2.0.20-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759b51346aa388c2e606ee206c0bc6f15a5299f6174d1e10cadbe4530d3c7a98", size = 2049134, upload-time = "2023-08-15T20:03:03.9Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3b/7f/9a11e808fdf1187c8206f204352fbbd0d72b68d6bc8233121058f8bde73d/SQLAlchemy-2.0.20-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1506e988ebeaaf316f183da601f24eedd7452e163010ea63dbe52dc91c7fc70e", size = 2040214, upload-time = "2023-08-15T20:03:05.888Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7f/3f/612c89009ff6194f28682929a839fcc25d46c8ccaddf4ee186debc67c7e8/SQLAlchemy-2.0.20-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5768c268df78bacbde166b48be788b83dddaa2a5974b8810af422ddfe68a9bc8", size = 3023253, upload-time = "2023-08-15T22:46:47.043Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9f/94/8a20e0b867857cacf7e1269efc02aa81866071df844a2598fa34245a50df/SQLAlchemy-2.0.20-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3f0dd6d15b6dc8b28a838a5c48ced7455c3e1fb47b89da9c79cc2090b072a50", size = 3022383, upload-time = "2023-08-15T20:44:30.135Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b5/fb/6089bd8137570e90a2afdc51043a0eeedb6fd818ff91a5b02862f6eb7da9/SQLAlchemy-2.0.20-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:243d0fb261f80a26774829bc2cee71df3222587ac789b7eaf6555c5b15651eed", size = 3040062, upload-time = "2023-08-15T22:46:49.408Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f3/d5/99c6f160ec195dd1fbe7bd1f0d715b9598496825f8ec05be7089508a735f/SQLAlchemy-2.0.20-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6eb6d77c31e1bf4268b4d61b549c341cbff9842f8e115ba6904249c20cb78a61", size = 3036901, upload-time = "2023-08-15T20:44:32.506Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/a8/9bc1bffb61b68ad3cc8a7cfb1412b59059fc97c9dbeb42d5da865e4ba8c9/SQLAlchemy-2.0.20-cp310-cp310-win32.whl", hash = "sha256:bcb04441f370cbe6e37c2b8d79e4af9e4789f626c595899d94abebe8b38f9a4d", size = 2019158, upload-time = "2023-08-15T19:55:13.495Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/01/e251a1e721d93c584471d56f7f6d1ae310831522b04d74306a0f522e7c1a/SQLAlchemy-2.0.20-cp310-cp310-win_amd64.whl", hash = "sha256:d32b5ffef6c5bcb452723a496bad2d4c52b346240c59b3e6dba279f6dcc06c14", size = 2044344, upload-time = "2023-08-15T19:55:16.593Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/2b/570a1caa015115a34562965498276de4251bc6cb049a349541f5693e43c3/SQLAlchemy-2.0.20-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd81466bdbc82b060c3c110b2937ab65ace41dfa7b18681fdfad2f37f27acdd7", size = 2052682, upload-time = "2023-08-15T19:23:17.432Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/a7/3c8c8a36f336880e4dca47bf30d5c723384c40b67e649b35a582d6df45ef/SQLAlchemy-2.0.20-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6fe7d61dc71119e21ddb0094ee994418c12f68c61b3d263ebaae50ea8399c4d4", size = 2042936, upload-time = "2023-08-15T19:23:20.046Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fb/2e/015ad3b62da87717666e6ac2959085fda651d667f572f86490008e07c0d8/SQLAlchemy-2.0.20-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4e571af672e1bb710b3cc1a9794b55bce1eae5aed41a608c0401885e3491179", size = 3160735, upload-time = "2023-08-15T20:15:00.251Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/3e/d0c97146e4c707451c05b80ac712025d635b19aed1d867fed351de70c71c/SQLAlchemy-2.0.20-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3364b7066b3c7f4437dd345d47271f1251e0cfb0aba67e785343cdbdb0fff08c", size = 3156962, upload-time = "2023-08-15T19:27:15.86Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/03/9ae3fd33d743219ec3651404b509af4aff1d7ed729be5aec56aa4f760579/SQLAlchemy-2.0.20-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1be86ccea0c965a1e8cd6ccf6884b924c319fcc85765f16c69f1ae7148eba64b", size = 3162243, upload-time = "2023-08-15T20:15:04.422Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/c5/ce43c4734585c393f0cf000e75f454e46a71828b3e21f78cb4560c7bdd94/SQLAlchemy-2.0.20-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1d35d49a972649b5080557c603110620a86aa11db350d7a7cb0f0a3f611948a0", size = 3155001, upload-time = "2023-08-15T19:27:19.323Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/ee/dd75fd26a485a53c1f8eefad4a94ab811ec95584725fc6823e15283275c1/SQLAlchemy-2.0.20-cp311-cp311-win32.whl", hash = "sha256:27d554ef5d12501898d88d255c54eef8414576f34672e02fe96d75908993cf53", size = 2020912, upload-time = "2023-08-15T19:34:36.643Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/10/81/230bec5713913a472003b71c8dd7e3572de44499e741df576fd6b39d82c8/SQLAlchemy-2.0.20-cp311-cp311-win_amd64.whl", hash = "sha256:411e7f140200c02c4b953b3dbd08351c9f9818d2bd591b56d0fa0716bd014f1e", size = 2047477, upload-time = "2023-08-15T19:34:38.939Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4b/dd/8fe0fc21fc4338e7479f1b254a67b5515bd31b85c28925045bc8b0d5a1c3/SQLAlchemy-2.0.20-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:26a3399eaf65e9ab2690c07bd5cf898b639e76903e0abad096cd609233ce5208", size = 2052105, upload-time = "2023-08-15T20:03:07.991Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/07/04/ac0cb7e5164d9c6e27dd80c2031e9c0ee3763d0d9c12688ba5c98499d83e/SQLAlchemy-2.0.20-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4cde2e1096cbb3e62002efdb7050113aa5f01718035ba9f29f9d89c3758e7e4e", size = 2043162, upload-time = "2023-08-15T20:03:09.931Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/6c/722ba2885fe78f4467b9b0f76aeb7ac9387362a5c5ffc22aa270294921d2/SQLAlchemy-2.0.20-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b09ba72e4e6d341bb5bdd3564f1cea6095d4c3632e45dc69375a1dbe4e26ec", size = 3039009, upload-time = "2023-08-15T22:46:51.641Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/91/2b/92aadcea86b9ebd681de0b6b2cbfa75193227e607893cfb5feea0cefc461/SQLAlchemy-2.0.20-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b74eeafaa11372627ce94e4dc88a6751b2b4d263015b3523e2b1e57291102f0", size = 3038404, upload-time = "2023-08-15T20:44:34.765Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/ea/b06f96c04c6bf3dec93be59d44aed9a777addbbeb8c611da75c01f7d76b6/SQLAlchemy-2.0.20-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:77d37c1b4e64c926fa3de23e8244b964aab92963d0f74d98cbc0783a9e04f501", size = 3059584, upload-time = "2023-08-15T22:46:53.999Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ab/1d/146d589a252c5e18da25b91ff657cf8f2546c4c5d573a6d9ca0d84158591/SQLAlchemy-2.0.20-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:eefebcc5c555803065128401a1e224a64607259b5eb907021bf9b175f315d2a6", size = 3054874, upload-time = "2023-08-15T20:44:37.017Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/15/55/70a0da35ee555769cfe92b70666c3af7a1fc2340bd3faf5105a52d5fd030/SQLAlchemy-2.0.20-cp39-cp39-win32.whl", hash = "sha256:3423dc2a3b94125094897118b52bdf4d37daf142cbcf26d48af284b763ab90e9", size = 2021584, upload-time = "2023-08-15T19:55:18.079Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/fb/becd158acfec58b7a34d04affe79df9f8e5f95aa2114b5dacf50a8752f20/SQLAlchemy-2.0.20-cp39-cp39-win_amd64.whl", hash = "sha256:5ed61e3463021763b853628aef8bc5d469fe12d95f82c74ef605049d810f3267", size = 2046802, upload-time = "2023-08-15T19:55:20.162Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/b4/018f41f16d6c2c198f36ef10799ae8f96daa4f17f887105224918fea501a/SQLAlchemy-2.0.20-py3-none-any.whl", hash = "sha256:63a368231c53c93e2b67d0c5556a9836fdcd383f7e3026a39602aad775b14acf", size = 1840591, upload-time = "2023-08-15T20:17:56.794Z" },
]
[[package]]
name = "sqlalchemy-utils"
version = "0.41.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "sqlalchemy" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a3/e0/6906a8a9b8e9deb82923e02e2c1f750c567d69a34f6e1fe566792494a682/SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74", size = 136579, upload-time = "2023-04-27T11:40:14.959Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/d8/3863fdfe6b27f6c0dffc650aaa2929f313b33aea615b102279fd46ab550b/SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801", size = 92612, upload-time = "2023-04-27T11:40:12.624Z" },
]
[[package]]
name = "structlog"
version = "25.4.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/79/b9/6e672db4fec07349e7a8a8172c1a6ae235c58679ca29c3f86a61b5e59ff3/structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4", size = 1369138, upload-time = "2025-06-02T08:21:12.971Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/4a/97ee6973e3a73c74c8120d59829c3861ea52210667ec3e7a16045c62b64d/structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c", size = 68720, upload-time = "2025-06-02T08:21:11.43Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "virtualenv"
version = "20.35.3"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
dependencies = [
{ name = "distlib" },
{ name = "filelock", version = "3.19.1", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }, marker = "python_full_version < '3.10'" },
{ name = "filelock", version = "3.20.0", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }, marker = "python_full_version >= '3.10'" },
{ name = "platformdirs", version = "4.4.0", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }, marker = "python_full_version < '3.10'" },
{ name = "platformdirs", version = "4.5.0", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }, marker = "python_full_version >= '3.10'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" },
]
[[package]]
name = "whoosh"
version = "2.7.4"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz", hash = "sha256:7ca5633dbfa9e0e0fa400d3151a8a0c4bec53bd2ecedc0a67705b17565c31a83", size = 968741, upload-time = "2016-04-04T01:19:32.327Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/19/24d0f1f454a2c1eb689ca28d2f178db81e5024f42d82729a4ff6771155cf/Whoosh-2.7.4-py2.py3-none-any.whl", hash = "sha256:aa39c3c3426e3fd107dcb4bde64ca1e276a65a889d9085a6e4b54ba82420a852", size = 468790, upload-time = "2016-04-04T01:19:40.379Z" },
]
[[package]]
name = "zstandard"
version = "0.25.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/56/7a/28efd1d371f1acd037ac64ed1c5e2b41514a6cc937dd6ab6a13ab9f0702f/zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd", size = 795256, upload-time = "2025-09-14T22:15:56.415Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/96/34/ef34ef77f1ee38fc8e4f9775217a613b452916e633c4f1d98f31db52c4a5/zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7", size = 640565, upload-time = "2025-09-14T22:15:58.177Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/1b/4fdb2c12eb58f31f28c4d28e8dc36611dd7205df8452e63f52fb6261d13e/zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550", size = 5345306, upload-time = "2025-09-14T22:16:00.165Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/28/a44bdece01bca027b079f0e00be3b6bd89a4df180071da59a3dd7381665b/zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d", size = 5055561, upload-time = "2025-09-14T22:16:02.22Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/74/68341185a4f32b274e0fc3410d5ad0750497e1acc20bd0f5b5f64ce17785/zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b", size = 5402214, upload-time = "2025-09-14T22:16:04.109Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8b/67/f92e64e748fd6aaffe01e2b75a083c0c4fd27abe1c8747fee4555fcee7dd/zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0", size = 5449703, upload-time = "2025-09-14T22:16:06.312Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fd/e5/6d36f92a197c3c17729a2125e29c169f460538a7d939a27eaaa6dcfcba8e/zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0", size = 5556583, upload-time = "2025-09-14T22:16:08.457Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d7/83/41939e60d8d7ebfe2b747be022d0806953799140a702b90ffe214d557638/zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd", size = 5045332, upload-time = "2025-09-14T22:16:10.444Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/87/d3ee185e3d1aa0133399893697ae91f221fda79deb61adbe998a7235c43f/zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701", size = 5572283, upload-time = "2025-09-14T22:16:12.128Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0a/1d/58635ae6104df96671076ac7d4ae7816838ce7debd94aecf83e30b7121b0/zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1", size = 4959754, upload-time = "2025-09-14T22:16:14.225Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/75/d6/57e9cb0a9983e9a229dd8fd2e6e96593ef2aa82a3907188436f22b111ccd/zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150", size = 5266477, upload-time = "2025-09-14T22:16:16.343Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d1/a9/ee891e5edf33a6ebce0a028726f0bbd8567effe20fe3d5808c42323e8542/zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab", size = 5440914, upload-time = "2025-09-14T22:16:18.453Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/58/08/a8522c28c08031a9521f27abc6f78dbdee7312a7463dd2cfc658b813323b/zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e", size = 5819847, upload-time = "2025-09-14T22:16:20.559Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/11/4c91411805c3f7b6f31c60e78ce347ca48f6f16d552fc659af6ec3b73202/zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74", size = 5363131, upload-time = "2025-09-14T22:16:22.206Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/d6/8c4bd38a3b24c4c7676a7a3d8de85d6ee7a983602a734b9f9cdefb04a5d6/zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa", size = 436469, upload-time = "2025-09-14T22:16:25.002Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/93/90/96d50ad417a8ace5f841b3228e93d1bb13e6ad356737f42e2dde30d8bd68/zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e", size = 506100, upload-time = "2025-09-14T22:16:23.569Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/14/0d/d0a405dad6ab6f9f759c26d866cca66cb209bff6f8db656074d662a953dd/zstandard-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0", size = 795263, upload-time = "2025-09-14T22:18:21.683Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ca/aa/ceb8d79cbad6dabd4cb1178ca853f6a4374d791c5e0241a0988173e2a341/zstandard-0.25.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2", size = 640560, upload-time = "2025-09-14T22:18:22.867Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/88/cd/2cf6d476131b509cc122d25d3416a2d0aa17687ddbada7599149f9da620e/zstandard-0.25.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df", size = 5344244, upload-time = "2025-09-14T22:18:24.724Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5c/71/e14820b61a1c137966b7667b400b72fa4a45c836257e443f3d77607db268/zstandard-0.25.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53", size = 5054550, upload-time = "2025-09-14T22:18:26.445Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f9/ce/26dc5a6fa956be41d0e984909224ed196ee6f91d607f0b3fd84577741a77/zstandard-0.25.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3", size = 5401150, upload-time = "2025-09-14T22:18:28.745Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f2/1b/402cab5edcfe867465daf869d5ac2a94930931c0989633bc01d6a7d8bd68/zstandard-0.25.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362", size = 5448595, upload-time = "2025-09-14T22:18:30.475Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/86/b2/fc50c58271a1ead0e5a0a0e6311f4b221f35954dce438ce62751b3af9b68/zstandard-0.25.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530", size = 5555290, upload-time = "2025-09-14T22:18:32.336Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/20/5f72d6ba970690df90fdd37195c5caa992e70cb6f203f74cc2bcc0b8cf30/zstandard-0.25.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb", size = 5043898, upload-time = "2025-09-14T22:18:34.215Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e4/f1/131a0382b8b8d11e84690574645f528f5c5b9343e06cefd77f5fd730cd2b/zstandard-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751", size = 5571173, upload-time = "2025-09-14T22:18:36.117Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/f6/2a37931023f737fd849c5c28def57442bbafadb626da60cf9ed58461fe24/zstandard-0.25.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577", size = 4958261, upload-time = "2025-09-14T22:18:38.098Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b5/52/ca76ed6dbfd8845a5563d3af4e972da3b9da8a9308ca6b56b0b929d93e23/zstandard-0.25.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7", size = 5265680, upload-time = "2025-09-14T22:18:39.834Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7a/59/edd117dedb97a768578b49fb2f1156defb839d1aa5b06200a62be943667f/zstandard-0.25.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936", size = 5439747, upload-time = "2025-09-14T22:18:41.647Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/75/71/c2e9234643dcfbd6c5e975e9a2b0050e1b2afffda6c3a959e1b87997bc80/zstandard-0.25.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388", size = 5818805, upload-time = "2025-09-14T22:18:43.602Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f5/93/8ebc19f0a31c44ea0e7348f9b0d4b326ed413b6575a3c6ff4ed50222abb6/zstandard-0.25.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27", size = 5362280, upload-time = "2025-09-14T22:18:45.625Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b8/e9/29cc59d4a9d51b3fd8b477d858d0bd7ab627f700908bf1517f46ddd470ae/zstandard-0.25.0-cp39-cp39-win32.whl", hash = "sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649", size = 436460, upload-time = "2025-09-14T22:18:49.077Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/41/b5/bc7a92c116e2ef32dc8061c209d71e97ff6df37487d7d39adb51a343ee89/zstandard-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860", size = 506097, upload-time = "2025-09-14T22:18:47.342Z" },
]