feat: add cmhub settings panel
This commit is contained in:
+379
-10
@@ -4,14 +4,26 @@ from __future__ import annotations
|
||||
|
||||
from ..widgets import *
|
||||
from ..workers import AIModelTestWorker as _RealAIModelTestWorker
|
||||
from ..workers import CMHubSettingsWorker as _RealCMHubSettingsWorker
|
||||
|
||||
|
||||
PLAINTEXT_CMHUB_API_KEY_WARNING = (
|
||||
"cmhub API Key 会以本地明文保存到 config/cmhub.json,仅供本机调用 cmhub 网关使用。"
|
||||
"该文件已 gitignore,UI 打码显示,日志/导出不记录明文。"
|
||||
)
|
||||
|
||||
|
||||
def AIModelTestWorker(*args, **kwargs):
|
||||
return _call_package_attr("AIModelTestWorker", _RealAIModelTestWorker, *args, **kwargs)
|
||||
|
||||
|
||||
def CMHubSettingsWorker(*args, **kwargs):
|
||||
return _call_package_attr("CMHubSettingsWorker", _RealCMHubSettingsWorker, *args, **kwargs)
|
||||
|
||||
class SettingsTab(QWidget):
|
||||
"""Tab 5: AI model definitions stored in config/ai_models.json."""
|
||||
|
||||
BACKEND_ITEMS = [("直连模型", "direct"), ("cmhub 网关", "cmhub")]
|
||||
CATEGORY_ITEMS = [("文本", "text"), ("图像", "image")]
|
||||
API_TYPE_ITEMS = [("chat", "chat"), ("images_edits", "images_edits"), ("auto", "auto")]
|
||||
RESOLUTION_ITEMS = ["512", "1k", "2k", "4k"]
|
||||
@@ -36,13 +48,54 @@ class SettingsTab(QWidget):
|
||||
or self.config.get("ai_models_path")
|
||||
or appconfig.AI_MODELS_PATH
|
||||
)
|
||||
self.cmhub_config_path = (
|
||||
self.config.get("cmhub_config_path")
|
||||
or self._default_cmhub_config_path(self.config_path)
|
||||
)
|
||||
self.status_callback = status_callback
|
||||
self.models = []
|
||||
self.current_model_name = None
|
||||
self.test_worker = None
|
||||
self.test_thread = None
|
||||
self.cmhub_models = []
|
||||
self.cmhub_worker = None
|
||||
self.cmhub_thread = None
|
||||
self._loaded_cmhub_api_key = ""
|
||||
self._cmhub_auto_refresh_done = False
|
||||
self._compat_test_item_id = ""
|
||||
|
||||
self.backend_combo = QComboBox()
|
||||
self.backend_combo.setObjectName("aiBackendCombo")
|
||||
for label, value in self.BACKEND_ITEMS:
|
||||
self.backend_combo.addItem(label, value)
|
||||
self.cmhub_base_url_edit = QLineEdit()
|
||||
self.cmhub_base_url_edit.setObjectName("cmhubBaseUrlEdit")
|
||||
self.cmhub_base_url_edit.setPlaceholderText("https://<cmhub>")
|
||||
self.cmhub_api_key_edit = QLineEdit()
|
||||
self.cmhub_api_key_edit.setObjectName("cmhubApiKeyEdit")
|
||||
self.cmhub_api_key_edit.setEchoMode(QLineEdit.Password)
|
||||
self.cmhub_api_key_edit.setPlaceholderText("从 cmhub 网页端复制 API Key")
|
||||
self.cmhub_title_alias_combo = QComboBox()
|
||||
self.cmhub_title_alias_combo.setObjectName("cmhubTitleAliasCombo")
|
||||
self.cmhub_image_alias_combo = QComboBox()
|
||||
self.cmhub_image_alias_combo.setObjectName("cmhubImageAliasCombo")
|
||||
self.cmhub_connect_timeout_spin = QSpinBox()
|
||||
self.cmhub_connect_timeout_spin.setObjectName("cmhubConnectTimeoutSpin")
|
||||
self.cmhub_connect_timeout_spin.setRange(1, 3600)
|
||||
self.cmhub_connect_timeout_spin.setValue(10)
|
||||
self.cmhub_check_balance_checkbox = QCheckBox("批量生成前检查余额")
|
||||
self.cmhub_check_balance_checkbox.setObjectName("cmhubCheckBalanceCheckbox")
|
||||
self.cmhub_refresh_button = QPushButton("刷新别名")
|
||||
self.cmhub_refresh_button.setObjectName("cmhubRefreshButton")
|
||||
self.cmhub_test_button = QPushButton("测试连接/查余额")
|
||||
self.cmhub_test_button.setObjectName("cmhubTestButton")
|
||||
self.cmhub_result_label = QLabel("")
|
||||
self.cmhub_result_label.setObjectName("cmhubResultLabel")
|
||||
self.cmhub_result_label.setWordWrap(True)
|
||||
self.cmhub_key_hint_label = QLabel("API Key 仅在 cmhub 网页端创建时显示一次;复制到此处后会本地明文保存并打码显示。")
|
||||
self.cmhub_key_hint_label.setObjectName("cmhubKeyHintLabel")
|
||||
self.cmhub_key_hint_label.setWordWrap(True)
|
||||
|
||||
self.model_combo = QComboBox()
|
||||
self.model_combo.setObjectName("aiModelCombo")
|
||||
self.add_model_button = QPushButton("新增")
|
||||
@@ -165,10 +218,15 @@ class SettingsTab(QWidget):
|
||||
]
|
||||
)
|
||||
|
||||
ai_form = self._three_column_form(
|
||||
direct_role_form = self._three_column_form(
|
||||
[
|
||||
("标题大模型", self.default_text_model_combo),
|
||||
("图片大模型", self.default_image_model_combo),
|
||||
]
|
||||
)
|
||||
|
||||
ai_form = self._three_column_form(
|
||||
[
|
||||
("标题并发数", self.title_concurrency_spin),
|
||||
("图片并发数", self.image_concurrency_spin),
|
||||
("失败重试次数", self.retry_spin),
|
||||
@@ -208,6 +266,47 @@ class SettingsTab(QWidget):
|
||||
]
|
||||
)
|
||||
|
||||
backend_form = self._three_column_form([("AI 后端", self.backend_combo)])
|
||||
|
||||
self.model_picker_panel = QWidget()
|
||||
self.model_picker_panel.setLayout(model_picker_layout)
|
||||
|
||||
self.model_detail_panel = QWidget()
|
||||
model_detail_layout = QVBoxLayout(self.model_detail_panel)
|
||||
model_detail_layout.setContentsMargins(0, 0, 0, 0)
|
||||
model_detail_layout.setSpacing(8)
|
||||
model_detail_layout.addLayout(form)
|
||||
model_detail_layout.addLayout(action_layout)
|
||||
model_detail_layout.addWidget(self.test_result_label)
|
||||
|
||||
self.direct_role_panel = QWidget()
|
||||
self.direct_role_panel.setLayout(direct_role_form)
|
||||
|
||||
cmhub_form = self._three_column_form(
|
||||
[
|
||||
("网关 Base URL", self.cmhub_base_url_edit, True),
|
||||
("API Key", self.cmhub_api_key_edit, True),
|
||||
("连接超时(秒)", self.cmhub_connect_timeout_spin),
|
||||
("生文别名", self.cmhub_title_alias_combo),
|
||||
("生图别名", self.cmhub_image_alias_combo),
|
||||
("", self.cmhub_check_balance_checkbox),
|
||||
]
|
||||
)
|
||||
cmhub_action_layout = QHBoxLayout()
|
||||
cmhub_action_layout.setContentsMargins(0, 0, 0, 0)
|
||||
cmhub_action_layout.addWidget(self.cmhub_refresh_button)
|
||||
cmhub_action_layout.addWidget(self.cmhub_test_button)
|
||||
cmhub_action_layout.addStretch(1)
|
||||
self.cmhub_panel = QWidget()
|
||||
self.cmhub_panel.setObjectName("cmhubSettingsPanel")
|
||||
cmhub_panel_layout = QVBoxLayout(self.cmhub_panel)
|
||||
cmhub_panel_layout.setContentsMargins(0, 0, 0, 0)
|
||||
cmhub_panel_layout.setSpacing(8)
|
||||
cmhub_panel_layout.addLayout(cmhub_form)
|
||||
cmhub_panel_layout.addWidget(self.cmhub_key_hint_label)
|
||||
cmhub_panel_layout.addLayout(cmhub_action_layout)
|
||||
cmhub_panel_layout.addWidget(self.cmhub_result_label)
|
||||
|
||||
panel = QWidget()
|
||||
panel.setMaximumWidth(1800)
|
||||
panel_layout = QVBoxLayout(panel)
|
||||
@@ -234,14 +333,15 @@ class SettingsTab(QWidget):
|
||||
"settingsInfrastructureSectionTitle",
|
||||
)
|
||||
panel_layout.addWidget(self.ai_model_section_title)
|
||||
panel_layout.addLayout(model_picker_layout)
|
||||
panel_layout.addLayout(backend_form)
|
||||
panel_layout.addWidget(self.model_picker_panel)
|
||||
panel_layout.addSpacing(14)
|
||||
panel_layout.addWidget(self.model_detail_section_title)
|
||||
panel_layout.addLayout(form)
|
||||
panel_layout.addLayout(action_layout)
|
||||
panel_layout.addWidget(self.test_result_label)
|
||||
panel_layout.addWidget(self.model_detail_panel)
|
||||
panel_layout.addWidget(self.cmhub_panel)
|
||||
panel_layout.addSpacing(18)
|
||||
panel_layout.addWidget(self.generation_section_title)
|
||||
panel_layout.addWidget(self.direct_role_panel)
|
||||
panel_layout.addLayout(ai_form)
|
||||
panel_layout.addSpacing(18)
|
||||
panel_layout.addWidget(self.shopee_update_section_title)
|
||||
@@ -271,6 +371,9 @@ class SettingsTab(QWidget):
|
||||
self.delete_model_button.clicked.connect(self.delete_model)
|
||||
self.save_model_button.clicked.connect(self.save_model)
|
||||
self.test_connection_button.clicked.connect(self.test_connection)
|
||||
self.backend_combo.currentIndexChanged.connect(self._on_backend_changed)
|
||||
self.cmhub_refresh_button.clicked.connect(self.refresh_cmhub_models)
|
||||
self.cmhub_test_button.clicked.connect(self.test_cmhub_connection)
|
||||
self.resolution_combo.currentIndexChanged.connect(
|
||||
self._update_response_timeout_label
|
||||
)
|
||||
@@ -323,10 +426,43 @@ class SettingsTab(QWidget):
|
||||
label.setStyleSheet("color: #24292f; font-weight: 600; padding-top: 4px;")
|
||||
return label
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
self._maybe_auto_refresh_cmhub_models()
|
||||
|
||||
def _set_status(self, message):
|
||||
if self.status_callback is not None:
|
||||
self.status_callback(message)
|
||||
|
||||
def _maybe_auto_refresh_cmhub_models(self):
|
||||
if self._cmhub_auto_refresh_done:
|
||||
return
|
||||
if (self.backend_combo.currentData() or "direct") != "cmhub":
|
||||
return
|
||||
if not self.cmhub_base_url_edit.text().strip() or not self.cmhub_api_key_edit.text():
|
||||
return
|
||||
self._cmhub_auto_refresh_done = True
|
||||
self.refresh_cmhub_models()
|
||||
|
||||
def _default_cmhub_config_path(self, config_path):
|
||||
if config_path and config_path != appconfig.CONFIG_PATH:
|
||||
return os.path.join(
|
||||
os.path.dirname(os.path.abspath(config_path)),
|
||||
"config",
|
||||
"cmhub.json",
|
||||
)
|
||||
return appconfig.CMHUB_CONFIG_PATH
|
||||
|
||||
def _on_backend_changed(self, index=None):
|
||||
is_cmhub = (self.backend_combo.currentData() or "direct") == "cmhub"
|
||||
self.model_picker_panel.setVisible(not is_cmhub)
|
||||
self.model_detail_section_title.setVisible(not is_cmhub)
|
||||
self.model_detail_panel.setVisible(not is_cmhub)
|
||||
self.direct_role_panel.setVisible(not is_cmhub)
|
||||
self.cmhub_panel.setVisible(is_cmhub)
|
||||
self._set_cmhub_running(self.cmhub_thread is not None)
|
||||
self._update_button_state()
|
||||
|
||||
def refresh_models(self, selected=None):
|
||||
try:
|
||||
self.models = appconfig.list_ai_models(
|
||||
@@ -459,7 +595,14 @@ class SettingsTab(QWidget):
|
||||
settings = self._app_settings_values()
|
||||
if settings is None:
|
||||
return
|
||||
cmhub_key = self.cmhub_api_key_edit.text()
|
||||
if self._should_warn_plaintext_cmhub_api_key(cmhub_key):
|
||||
self._show_plaintext_cmhub_api_key_warning()
|
||||
try:
|
||||
appconfig.save_cmhub_config(
|
||||
{"api_key": cmhub_key},
|
||||
path=self.cmhub_config_path,
|
||||
)
|
||||
saved = appconfig.save_config(settings, path=self.config_path)
|
||||
except Exception as exc:
|
||||
self._show_error(exc)
|
||||
@@ -479,17 +622,23 @@ class SettingsTab(QWidget):
|
||||
if not (start_port <= default_port <= end_port):
|
||||
self._show_error("默认调试端口必须在调试端口范围内")
|
||||
return None
|
||||
text_model = self.default_text_model_combo.currentData()
|
||||
image_model = self.default_image_model_combo.currentData()
|
||||
if not text_model or not image_model:
|
||||
ai_cfg = appconfig.ai_config(self.config)
|
||||
backend = self.backend_combo.currentData() or "direct"
|
||||
text_model = self.default_text_model_combo.currentData() or ai_cfg.get("default_text_model")
|
||||
image_model = self.default_image_model_combo.currentData() or ai_cfg.get("default_image_model")
|
||||
if backend == "direct" and (not text_model or not image_model):
|
||||
self._show_error("标题大模型和图片大模型不能为空")
|
||||
return None
|
||||
cmhub_cfg = self._cmhub_settings_values(backend)
|
||||
if cmhub_cfg is None:
|
||||
return None
|
||||
|
||||
ai_cfg = appconfig.ai_config(self.config)
|
||||
ai_cfg.update(
|
||||
{
|
||||
"backend": backend,
|
||||
"default_text_model": text_model,
|
||||
"default_image_model": image_model,
|
||||
"cmhub": cmhub_cfg,
|
||||
"title_concurrency": self.title_concurrency_spin.value(),
|
||||
"image_concurrency": self.image_concurrency_spin.value(),
|
||||
"retry": self.retry_spin.value(),
|
||||
@@ -502,7 +651,7 @@ class SettingsTab(QWidget):
|
||||
settings = {
|
||||
key: value
|
||||
for key, value in self.config.items()
|
||||
if key not in {"config_path", "ai_models_path"}
|
||||
if key not in {"config_path", "ai_models_path", "cmhub_config_path"}
|
||||
}
|
||||
settings.update(
|
||||
{
|
||||
@@ -528,12 +677,41 @@ class SettingsTab(QWidget):
|
||||
)
|
||||
return settings
|
||||
|
||||
def _cmhub_settings_values(self, backend):
|
||||
current = appconfig.cmhub_config(self.config)
|
||||
values = {
|
||||
"base_url": self.cmhub_base_url_edit.text().strip(),
|
||||
"title_alias": self.cmhub_title_alias_combo.currentData() or "",
|
||||
"image_alias": self.cmhub_image_alias_combo.currentData() or "",
|
||||
"connect_timeout": self.cmhub_connect_timeout_spin.value(),
|
||||
"check_balance_before_batch": self.cmhub_check_balance_checkbox.isChecked(),
|
||||
}
|
||||
if backend != "cmhub":
|
||||
merged = dict(current)
|
||||
merged.update(values)
|
||||
return merged
|
||||
missing = []
|
||||
if not values["base_url"]:
|
||||
missing.append("Base URL")
|
||||
if not self.cmhub_api_key_edit.text():
|
||||
missing.append("API Key")
|
||||
if not values["title_alias"]:
|
||||
missing.append("生文别名")
|
||||
if not values["image_alias"]:
|
||||
missing.append("生图别名")
|
||||
if missing:
|
||||
self._show_error("cmhub 配置不完整:缺少 " + "、".join(missing))
|
||||
return None
|
||||
return values
|
||||
|
||||
def _replace_config(self, saved):
|
||||
internal = {}
|
||||
if self.config_path != appconfig.CONFIG_PATH:
|
||||
internal["config_path"] = self.config_path
|
||||
if self.ai_models_path != appconfig.AI_MODELS_PATH:
|
||||
internal["ai_models_path"] = self.ai_models_path
|
||||
if self.cmhub_config_path != appconfig.CMHUB_CONFIG_PATH:
|
||||
internal["cmhub_config_path"] = self.cmhub_config_path
|
||||
self.config.clear()
|
||||
self.config.update(saved)
|
||||
self.config.update(internal)
|
||||
@@ -541,6 +719,23 @@ class SettingsTab(QWidget):
|
||||
def _populate_app_settings(self):
|
||||
self._populate_role_model_combos()
|
||||
ai_cfg = appconfig.ai_config(self.config)
|
||||
backend = ai_cfg.get("backend", "direct")
|
||||
self._set_combo_by_data(self.backend_combo, backend)
|
||||
cmhub_cfg = appconfig.cmhub_config(self.config)
|
||||
self.cmhub_base_url_edit.setText(cmhub_cfg.get("base_url", ""))
|
||||
self._loaded_cmhub_api_key = appconfig.get_cmhub_api_key(path=self.cmhub_config_path)
|
||||
self.cmhub_api_key_edit.setText(self._loaded_cmhub_api_key)
|
||||
self.cmhub_connect_timeout_spin.setValue(
|
||||
max(1, int(cmhub_cfg.get("connect_timeout", 10) or 10))
|
||||
)
|
||||
self.cmhub_check_balance_checkbox.setChecked(
|
||||
bool(cmhub_cfg.get("check_balance_before_batch", False))
|
||||
)
|
||||
self._populate_cmhub_alias_combos(
|
||||
self.cmhub_models,
|
||||
title_selected=cmhub_cfg.get("title_alias", ""),
|
||||
image_selected=cmhub_cfg.get("image_alias", ""),
|
||||
)
|
||||
self._set_combo_by_data(
|
||||
self.default_text_model_combo,
|
||||
ai_cfg.get("default_text_model", ""),
|
||||
@@ -595,6 +790,7 @@ class SettingsTab(QWidget):
|
||||
max(1, int(update_cfg.get("max_parallel_accounts", 2) or 2))
|
||||
)
|
||||
self._update_response_timeout_label()
|
||||
self._on_backend_changed()
|
||||
|
||||
def _shopee_update_config(self):
|
||||
defaults = appconfig.default_config().get("shopee_update", {})
|
||||
@@ -753,6 +949,168 @@ class SettingsTab(QWidget):
|
||||
self.test_result_label.setText(message)
|
||||
self._set_status(message)
|
||||
|
||||
def refresh_cmhub_models(self, checked=False):
|
||||
self._start_cmhub_worker(include_balance=False)
|
||||
|
||||
def test_cmhub_connection(self, checked=False):
|
||||
self._start_cmhub_worker(include_balance=True)
|
||||
|
||||
def _start_cmhub_worker(self, include_balance):
|
||||
if self.cmhub_thread is not None:
|
||||
self._set_status("cmhub 检测正在进行...")
|
||||
return
|
||||
base_url = self.cmhub_base_url_edit.text().strip()
|
||||
api_key = self.cmhub_api_key_edit.text()
|
||||
missing = []
|
||||
if not base_url:
|
||||
missing.append("Base URL")
|
||||
if not api_key:
|
||||
missing.append("API Key")
|
||||
if missing:
|
||||
self._show_error("cmhub 配置不完整:缺少 " + "、".join(missing))
|
||||
return
|
||||
worker = CMHubSettingsWorker(
|
||||
base_url,
|
||||
api_key,
|
||||
connect_timeout=self.cmhub_connect_timeout_spin.value(),
|
||||
include_balance=include_balance,
|
||||
db_path=_database_path(config=self.config),
|
||||
diagnostic_log_dir=diagnostics.DEFAULT_LOG_DIR,
|
||||
)
|
||||
worker.finished.connect(self._on_cmhub_finished)
|
||||
worker.failed.connect(self._on_cmhub_failed)
|
||||
thread = run_worker(worker, thread_name="CMHubSettingsWorker", start=False)
|
||||
thread.finished.connect(lambda: self._forget_cmhub_thread(thread))
|
||||
self.cmhub_worker = worker
|
||||
self.cmhub_thread = thread
|
||||
self._set_cmhub_running(True)
|
||||
message = "正在测试 cmhub 连接并查询余额..." if include_balance else "正在刷新 cmhub 别名..."
|
||||
self.cmhub_result_label.setText(message)
|
||||
self._set_status(message)
|
||||
thread.start()
|
||||
|
||||
def _set_cmhub_running(self, running):
|
||||
enabled = not running
|
||||
for widget in (
|
||||
self.cmhub_base_url_edit,
|
||||
self.cmhub_api_key_edit,
|
||||
self.cmhub_title_alias_combo,
|
||||
self.cmhub_image_alias_combo,
|
||||
self.cmhub_connect_timeout_spin,
|
||||
self.cmhub_check_balance_checkbox,
|
||||
self.cmhub_refresh_button,
|
||||
self.cmhub_test_button,
|
||||
):
|
||||
widget.setEnabled(enabled)
|
||||
|
||||
def _forget_cmhub_thread(self, thread):
|
||||
if self.cmhub_thread is thread:
|
||||
self.cmhub_thread = None
|
||||
self.cmhub_worker = None
|
||||
self._set_cmhub_running(False)
|
||||
|
||||
def _on_cmhub_finished(self, payload):
|
||||
if not payload.get("ok", True):
|
||||
self._on_cmhub_failed(-1, payload.get("error") or "连接失败")
|
||||
return
|
||||
models = [model for model in payload.get("models", []) if isinstance(model, dict)]
|
||||
self.cmhub_models = models
|
||||
current_cfg = appconfig.cmhub_config(self.config)
|
||||
title_selected = self.cmhub_title_alias_combo.currentData() or current_cfg.get("title_alias", "")
|
||||
image_selected = self.cmhub_image_alias_combo.currentData() or current_cfg.get("image_alias", "")
|
||||
self._populate_cmhub_alias_combos(
|
||||
models,
|
||||
title_selected=title_selected,
|
||||
image_selected=image_selected,
|
||||
)
|
||||
title_count = self._cmhub_alias_count("title")
|
||||
image_count = self._cmhub_alias_count("image")
|
||||
balance = payload.get("points_balance")
|
||||
balance_text = f";余额 {balance}" if balance is not None else ""
|
||||
message = f"cmhub 连接成功:生文别名 {title_count} 个,生图别名 {image_count} 个{balance_text}"
|
||||
self.cmhub_result_label.setText(message)
|
||||
self._set_status(message)
|
||||
|
||||
def _on_cmhub_failed(self, _task_id, error):
|
||||
message = f"cmhub 连接失败:{error}"
|
||||
self.cmhub_result_label.setText(message)
|
||||
self._set_status(message)
|
||||
|
||||
def _populate_cmhub_alias_combos(self, models, title_selected="", image_selected=""):
|
||||
self._populate_cmhub_alias_combo(
|
||||
self.cmhub_title_alias_combo,
|
||||
models,
|
||||
"title",
|
||||
title_selected,
|
||||
)
|
||||
self._populate_cmhub_alias_combo(
|
||||
self.cmhub_image_alias_combo,
|
||||
models,
|
||||
"image",
|
||||
image_selected,
|
||||
)
|
||||
|
||||
def _populate_cmhub_alias_combo(self, combo, models, operation, selected):
|
||||
combo.blockSignals(True)
|
||||
combo.clear()
|
||||
selected = str(selected or "").strip()
|
||||
added = set()
|
||||
for model in self._cmhub_priced_models(models, operation):
|
||||
alias = str(model.get("alias") or "").strip()
|
||||
if not alias or alias in added:
|
||||
continue
|
||||
combo.addItem(self._cmhub_alias_label(model), alias)
|
||||
added.add(alias)
|
||||
if selected and selected not in added:
|
||||
combo.addItem(f"{selected}(已保存)", selected)
|
||||
if combo.count() == 0:
|
||||
combo.addItem("无可用别名", None)
|
||||
index = combo.findData(selected)
|
||||
combo.setCurrentIndex(index if index >= 0 else 0)
|
||||
combo.blockSignals(False)
|
||||
|
||||
def _cmhub_priced_models(self, models, operation):
|
||||
items = []
|
||||
for model in models or []:
|
||||
if not isinstance(model, dict):
|
||||
continue
|
||||
alias = str(model.get("alias") or "").strip()
|
||||
op = str(model.get("operation_type") or "").lower()
|
||||
pricing_status = str(model.get("pricing_status") or "").lower()
|
||||
if alias and op == operation and pricing_status != "unpriced":
|
||||
items.append(model)
|
||||
return items
|
||||
|
||||
def _cmhub_alias_label(self, model):
|
||||
alias = str(model.get("alias") or "").strip()
|
||||
price_text = self._cmhub_price_text(model.get("prices"))
|
||||
parts = [alias]
|
||||
if price_text:
|
||||
parts.append(price_text)
|
||||
if model.get("requires_image"):
|
||||
parts.append("需参考图")
|
||||
return " · ".join(parts)
|
||||
|
||||
def _cmhub_price_text(self, prices):
|
||||
if not isinstance(prices, list):
|
||||
return ""
|
||||
parts = []
|
||||
for item in prices[:3]:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
cost = item.get("points_cost")
|
||||
if cost is None:
|
||||
cost = item.get("cost")
|
||||
if cost is None:
|
||||
continue
|
||||
resolution = item.get("resolution") or item.get("name") or ""
|
||||
parts.append(f"{resolution}:{cost}点" if resolution else f"{cost}点")
|
||||
return "/".join(parts)
|
||||
|
||||
def _cmhub_alias_count(self, operation):
|
||||
combo = self.cmhub_title_alias_combo if operation == "title" else self.cmhub_image_alias_combo
|
||||
return sum(1 for index in range(combo.count()) if combo.itemData(index))
|
||||
|
||||
def _show_error(self, error):
|
||||
message = str(error)
|
||||
QMessageBox.warning(self, "设置", message)
|
||||
@@ -763,6 +1121,10 @@ class SettingsTab(QWidget):
|
||||
current_key = str((current or {}).get("api_key") or "")
|
||||
return bool(new_key) and new_key != current_key
|
||||
|
||||
def _should_warn_plaintext_cmhub_api_key(self, key):
|
||||
new_key = str(key or "")
|
||||
return bool(new_key) and new_key != str(self._loaded_cmhub_api_key or "")
|
||||
|
||||
def _show_plaintext_api_key_warning(self):
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
@@ -770,6 +1132,13 @@ class SettingsTab(QWidget):
|
||||
PLAINTEXT_API_KEY_WARNING,
|
||||
)
|
||||
|
||||
def _show_plaintext_cmhub_api_key_warning(self):
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
PLAINTEXT_SECRET_TITLE,
|
||||
PLAINTEXT_CMHUB_API_KEY_WARNING,
|
||||
)
|
||||
|
||||
def _current_model(self):
|
||||
return self._model_by_name(self.current_model_name)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user