"""GUI本地状态存储。""" from __future__ import annotations import json from pathlib import Path from typing import Any from loguru import logger from douyu.config import Account, ProxyConfig from douyu.email_verifier import get_email_config_for_account DEFAULT_STATE_PATH = Path("data/gui_state.json") def account_to_dict(account: Account) -> dict[str, Any]: """把账号对象转换成可保存的字典。""" return { "username": account.username, "password": account.password, "email": account.email, "email_password": account.email_password, "email_imap_server": account.email_imap_server, "email_imap_port": account.email_imap_port, } def account_from_dict(data: dict[str, Any]) -> Account | None: """从本地状态恢复账号对象。""" username = str(data.get("username", "")).strip() password = str(data.get("password", "")).strip() email = str(data.get("email", "")).strip() email_password = str(data.get("email_password", "")).strip() if not all([username, password, email, email_password]): return None email_config = get_email_config_for_account(email) imap_server = str(data.get("email_imap_server") or email_config["server"]).strip() try: imap_port = int(data.get("email_imap_port") or email_config["port"]) except (TypeError, ValueError): imap_port = int(email_config["port"]) return Account( username=username, password=password, email=email, email_password=email_password, email_imap_server=imap_server, email_imap_port=imap_port, ) class GuiStateStore: """负责保存和读取GUI状态。""" def __init__(self, path: str | Path = DEFAULT_STATE_PATH): self.path = Path(path) def load(self) -> dict[str, Any]: """读取本地状态文件。""" if not self.path.exists(): return {} try: with self.path.open("r", encoding="utf-8") as f: data = json.load(f) return data if isinstance(data, dict) else {} except Exception as exc: logger.warning(f"读取GUI状态失败,将使用默认值: {exc}") return {} def save(self, state: dict[str, Any]) -> None: """原子写入本地状态文件。""" self.path.parent.mkdir(parents=True, exist_ok=True) tmp_path = self.path.with_suffix(f"{self.path.suffix}.tmp") with tmp_path.open("w", encoding="utf-8") as f: json.dump(state, f, ensure_ascii=False, indent=2) tmp_path.replace(self.path) @staticmethod def accounts_from_state(state: dict[str, Any]) -> list[Account]: """从状态字典恢复账号列表。""" accounts = [] raw_accounts = state.get("accounts", []) if not isinstance(raw_accounts, list): return accounts for raw_account in raw_accounts: if not isinstance(raw_account, dict): continue account = account_from_dict(raw_account) if account: accounts.append(account) return accounts @staticmethod def proxy_from_state(state: dict[str, Any]) -> ProxyConfig: """从状态字典恢复代理配置。""" proxy = state.get("proxy", {}) if not isinstance(proxy, dict): return ProxyConfig() return ProxyConfig( enabled=bool(proxy.get("enabled", False)), api_url=str(proxy.get("api_url", "")).strip(), http=str(proxy.get("http", "")).strip(), https=str(proxy.get("https", "")).strip(), whitelist_enabled=bool(proxy.get("whitelist_enabled", False)), whitelist_uid=str(proxy.get("whitelist_uid", "")).strip(), whitelist_ukey=str(proxy.get("whitelist_ukey", "")).strip(), )