Files
hy3d/hunyuan3dweb/browser/login.py
T
Claude bf4e1e5755 feat: add pure Python COS upload and improve login detection
- feat(cos): add cos_upload.py for direct file upload without browser
  - implements COS V1 signature algorithm with temporary credentials
  - upload_image() pipeline: get_upload_info → sign → PUT to COS
- feat(api): auto-load cookies from file when cookies arg is omitted
  - both Hunyuan3DAPI and Hunyuan3DAPIComplete now fall back to
    ~/.config/hunyuan3dweb/cookies.txt automatically
- fix(login): strengthen login-state detection using both URL and DOM
  - checks "login" not in page.url AND no login button on page
- docs: update README / README_CN with COS upload examples

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 00:47:11 +08:00

159 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
腾讯混元3D 邮箱验证码登录 CLI 工具 (CloakBrowser 持久化版本)
第一次输入邮箱回车后自动发送验证码
第二次输入验证码后回车自动点击登录按钮
登录状态会自动保存到 ~/.config/hunyuan3dweb/profile,下次运行无需重新登录
"""
import argparse
import os
import sys
import time
from cloakbrowser import launch_persistent_context
from ..config import get_profile_dir, get_cookie_path
PROFILE_DIR = str(get_profile_dir())
def _extract_and_save_cookies(context):
"""从浏览器上下文提取 Cookie 并保存到文件"""
try:
cookies = context.cookies()
relevant_names = {"hunyuan_user", "hunyuan_token", "hunyuan_source", "hy_user"}
cookie_dict = {c["name"]: c["value"] for c in cookies if c["name"] in relevant_names}
if cookie_dict:
cookie_str = "; ".join(f"{k}={v}" for k, v in cookie_dict.items())
cookie_path = get_cookie_path()
cookie_path.write_text(cookie_str, encoding="utf-8")
print(f"Cookie 已自动保存到: {cookie_path}")
return True
else:
print("警告: 未找到 hunyuan 相关 CookieAPI 客户端可能无法使用")
except Exception as e:
print(f"自动提取 Cookie 失败: {e}")
return False
def main():
parser = argparse.ArgumentParser(description="腾讯混元3D 邮箱验证码登录")
parser.add_argument(
"--no-headless",
action="store_true",
help="显示浏览器窗口(默认无头模式,适用于服务器)"
)
args = parser.parse_args()
headless = not args.no_headless
# 使用持久化上下文,cookie 和登录状态会保存到 PROFILE_DIR
context = None
try:
context = launch_persistent_context(PROFILE_DIR, headless=headless)
page = context.new_page()
print("正在打开腾讯混元3D...")
page.goto("https://3d.hunyuan.tencent.com/", timeout=60000)
page.wait_for_timeout(2000)
# 检查是否已经是登录状态:URL 不含 login 且页面上没有登录按钮
is_login_page = "login" in page.url
login_btn = page.locator("button").filter(has_text="登录").first
has_login_btn = login_btn.count() > 0
if not is_login_page and not has_login_btn:
print("检测到已有登录状态,无需重新登录。")
print(f"当前页面: {page.url}")
_extract_and_save_cookies(context)
if headless:
return
else:
# 未登录,走登录流程
login_btn.click()
page.locator("text=邮箱").wait_for(state="visible", timeout=10000)
# 定位元素
email_input = page.locator('input[placeholder="请输入邮箱地址"]')
code_input = page.locator('input[type="number"][placeholder="请输入邮箱验证码"]')
# 切换到邮箱登录
email_tab = page.locator("text=邮箱").first
if email_tab.count() > 0:
email_tab.click()
email_input.wait_for(state="visible", timeout=10000)
else:
print("未找到邮箱登录选项")
return
send_code_btn = page.locator("a.hyc-email-login__send-code")
login_submit_btn = page.locator("button.hyc-email-login__btn")
checkbox = page.locator(".t-checkbox__former")
# 第一次交互:输入邮箱并发送验证码
print("\n============================================")
email = input("请输入邮箱地址,按回车发送验证码: ").strip()
if not email:
print("邮箱不能为空,退出")
return
email_input.fill(email)
# 勾选协议(如果未勾选)
if checkbox.count() > 0:
is_checked = checkbox.evaluate("el => el.checked")
if not is_checked:
checkbox.evaluate("el => el.click()")
page.wait_for_timeout(300)
if send_code_btn.count() > 0:
send_code_btn.click()
print("已点击发送验证码,请查收邮件...")
else:
print("未找到发送验证码按钮")
return
# 第二次交互:输入验证码并登录
print("\n============================================")
code = input("请输入邮箱验证码,按回车登录: ").strip()
if not code:
print("验证码不能为空,退出")
return
code_input.fill(code)
# 点击登录
if login_submit_btn.count() > 0:
login_submit_btn.click()
print("已点击登录按钮,等待跳转...")
else:
print("未找到登录提交按钮")
return
# 等待登录成功跳转
try:
page.wait_for_url(lambda url: "login" not in url, timeout=30000)
print(f"\n登录成功!当前页面: {page.url}")
print(f"登录状态已保存到: {os.path.abspath(PROFILE_DIR)}")
_extract_and_save_cookies(context)
except Exception:
print("\n登录可能仍在处理中,或出现错误。请检查浏览器状态。")
# 保持浏览器打开(仅非无头模式)
if not headless:
print("\n按 Enter 键关闭浏览器并退出...")
input()
finally:
if context is not None:
try:
context.close()
except Exception:
pass
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n已取消")
sys.exit(0)