Add AIServer, character art pipeline, and Art package boot integration.
Bring in MaleExplorer assets/shaders, model tooling and Hunyuan updates, plus boot/YooAsset Art package support. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+98
-13
@@ -1,6 +1,7 @@
|
||||
"""WeChat login session for https://3d.hunyuan.tencent.com via Playwright.
|
||||
|
||||
CLI 友好:同步阻塞扫码登录(非后台线程)。
|
||||
未登录 / 凭证失效 / 额度满换号 时弹出有头 Edge 供用户扫码。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -11,7 +12,7 @@ from pathlib import Path
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
HUNYUAN_URL = "https://3d.hunyuan.tencent.com/"
|
||||
LOGIN_TIMEOUT_SEC = 180
|
||||
LOGIN_TIMEOUT_SEC = 300
|
||||
|
||||
UpdateFn = Callable[..., None]
|
||||
|
||||
@@ -140,32 +141,87 @@ def _looks_logged_in(page) -> bool:
|
||||
has_generate = bool(page.get_by_text("立即生成").count())
|
||||
except Exception:
|
||||
pass
|
||||
return bool((interesting or storage_hit) and has_generate)
|
||||
# 有生成入口,且 cookie/storage 任一命中,或页面上已无「登录」
|
||||
if has_generate and (interesting or storage_hit):
|
||||
return True
|
||||
if has_generate and not _page_shows_login_cta(page):
|
||||
return True
|
||||
return bool(interesting and storage_hit)
|
||||
|
||||
|
||||
def _page_shows_login_cta(page) -> bool:
|
||||
try:
|
||||
if page.get_by_text("登录", exact=True).count():
|
||||
return page.get_by_text("登录", exact=True).first.is_visible(timeout=300)
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _try_site_logout(page) -> None:
|
||||
"""尽量在官网侧退出,方便额度满后换号扫码。"""
|
||||
for label in ("退出登录", "退出", "登出", "注销"):
|
||||
if _click_first(page, (label,), exact=False):
|
||||
page.wait_for_timeout(800)
|
||||
break
|
||||
# 常见头像/用户菜单再点一次退出
|
||||
try:
|
||||
avatar = page.locator(
|
||||
'[class*="avatar"], [class*="user-info"], [class*="UserInfo"]'
|
||||
).first
|
||||
if avatar.count() and avatar.is_visible(timeout=500):
|
||||
avatar.click(timeout=2000)
|
||||
page.wait_for_timeout(400)
|
||||
_click_first(page, ("退出登录", "退出", "登出"), exact=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def start_wechat_login_blocking(
|
||||
timeout_sec: int = LOGIN_TIMEOUT_SEC,
|
||||
*,
|
||||
update: Optional[UpdateFn] = None,
|
||||
reason: str = "auth",
|
||||
clear_existing: bool = False,
|
||||
) -> dict:
|
||||
"""打开有头浏览器供微信扫码,阻塞直到成功 / 超时 / 失败。"""
|
||||
"""打开有头浏览器供微信扫码,阻塞直到成功 / 超时 / 失败。
|
||||
|
||||
reason:
|
||||
- auth: 未登录或凭证失效
|
||||
- quota: 当前账号额度满,需换号
|
||||
clear_existing: 先清本地会话;quota 场景会再尝试官网退出。
|
||||
"""
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def _emit(event: str, **payload: Any) -> None:
|
||||
if update:
|
||||
update(event=event, **payload)
|
||||
|
||||
if clear_existing or reason == "quota":
|
||||
logout()
|
||||
|
||||
if reason == "quota":
|
||||
message = (
|
||||
"当前混元账号额度/次数已满,请在弹出的浏览器中退出旧号并用微信扫码登录其他账号"
|
||||
)
|
||||
else:
|
||||
message = "请在弹出的浏览器窗口中用微信扫码登录混元3D"
|
||||
|
||||
_write_status(
|
||||
logged_in=False,
|
||||
state="waiting_scan",
|
||||
message="请在弹出的浏览器窗口中用微信扫码登录",
|
||||
message=message,
|
||||
reason=reason,
|
||||
)
|
||||
_emit(
|
||||
"login_waiting",
|
||||
stage="请在弹出的浏览器窗口中用微信扫码登录",
|
||||
stage=message,
|
||||
progress=5,
|
||||
timeout_sec=timeout_sec,
|
||||
reason=reason,
|
||||
)
|
||||
# 同步打到 stderr,方便 Agent/终端注意弹窗
|
||||
print(f"[hunyuan] {message}(最长等待 {timeout_sec}s)", flush=True)
|
||||
|
||||
try:
|
||||
with sync_playwright() as playwright:
|
||||
@@ -177,8 +233,15 @@ def start_wechat_login_blocking(
|
||||
)
|
||||
page = context.new_page()
|
||||
page.goto(HUNYUAN_URL, wait_until="domcontentloaded", timeout=60000)
|
||||
page.wait_for_timeout(2500)
|
||||
page.wait_for_timeout(2000)
|
||||
|
||||
if reason == "quota":
|
||||
_try_site_logout(page)
|
||||
page.goto(HUNYUAN_URL, wait_until="domcontentloaded", timeout=60000)
|
||||
page.wait_for_timeout(1500)
|
||||
|
||||
_click_first(page, ("登录", "立即登录"), exact=True)
|
||||
page.wait_for_timeout(800)
|
||||
_click_first(page, ("微信", "微信登录", "微信扫码"), exact=False)
|
||||
|
||||
deadline = time.monotonic() + timeout_sec
|
||||
@@ -187,15 +250,22 @@ def start_wechat_login_blocking(
|
||||
session_dir().mkdir(parents=True, exist_ok=True)
|
||||
context.storage_state(path=str(storage_state_path()))
|
||||
result = _write_status(
|
||||
logged_in=True, state="ready", message="微信登录成功"
|
||||
logged_in=True,
|
||||
state="ready",
|
||||
message="微信登录成功",
|
||||
reason=reason,
|
||||
)
|
||||
browser.close()
|
||||
_emit("login_ok", stage="微信登录成功", progress=15, **result)
|
||||
print("[hunyuan] 微信登录成功,会话已保存", flush=True)
|
||||
return result
|
||||
page.wait_for_timeout(1500)
|
||||
|
||||
result = _write_status(
|
||||
logged_in=False, state="timeout", message="微信登录超时,请重试"
|
||||
logged_in=False,
|
||||
state="timeout",
|
||||
message="微信登录超时,请重试",
|
||||
reason=reason,
|
||||
)
|
||||
browser.close()
|
||||
_emit("login_failed", stage="微信登录超时", progress=0, **result)
|
||||
@@ -204,7 +274,7 @@ def start_wechat_login_blocking(
|
||||
raise
|
||||
except Exception as exc:
|
||||
result = _write_status(
|
||||
logged_in=False, state="error", message=f"登录失败:{exc}"
|
||||
logged_in=False, state="error", message=f"登录失败:{exc}", reason=reason
|
||||
)
|
||||
_emit("login_failed", stage=str(exc), progress=0, **result)
|
||||
raise RuntimeError(f"登录失败:{exc}") from exc
|
||||
@@ -215,9 +285,14 @@ def ensure_login(
|
||||
interactive: bool = True,
|
||||
timeout_sec: int = LOGIN_TIMEOUT_SEC,
|
||||
update: Optional[UpdateFn] = None,
|
||||
force: bool = False,
|
||||
reason: str = "auth",
|
||||
) -> dict:
|
||||
"""若无会话则弹微信登录;interactive=False 且未登录则抛错。"""
|
||||
if is_logged_in():
|
||||
"""若无会话则弹微信登录;interactive=False 且未登录则抛错。
|
||||
|
||||
force=True 时忽略本地凭证,强制打开登录页(换号 / 重登)。
|
||||
"""
|
||||
if is_logged_in() and not force:
|
||||
current = status()
|
||||
if update:
|
||||
update(
|
||||
@@ -234,7 +309,17 @@ def ensure_login(
|
||||
if update:
|
||||
update(
|
||||
event="login_required",
|
||||
stage="需要微信登录混元3D",
|
||||
stage=(
|
||||
"当前账号额度已满,需要换号登录"
|
||||
if reason == "quota"
|
||||
else "需要微信登录混元3D"
|
||||
),
|
||||
progress=2,
|
||||
reason=reason,
|
||||
)
|
||||
return start_wechat_login_blocking(timeout_sec=timeout_sec, update=update)
|
||||
return start_wechat_login_blocking(
|
||||
timeout_sec=timeout_sec,
|
||||
update=update,
|
||||
reason=reason,
|
||||
clear_existing=force or reason == "quota",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user