diff --git a/README_REVERSE_ENGINEERING.md b/README_REVERSE_ENGINEERING.md index a83db3b..d424f2e 100644 --- a/README_REVERSE_ENGINEERING.md +++ b/README_REVERSE_ENGINEERING.md @@ -47,14 +47,23 @@ param_str = "nonce=yyy×tamp=xxx" sign = HMAC-SHA256(param_str, key="Hf6d6KFB3D") ``` -### 3. Image URL Format +### 3. Image Upload Evolution -Must use Tencent's internal resource format: -``` -https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=<32-digit-hex> -``` +#### Method 1: Browser Automation Upload (Original) +Upload via `generator.py` inside the browser to obtain a Tencent internal `resourceId`. -You cannot directly use a COS URL; you must upload through the browser to obtain a resourceId. +#### Method 2: Pure Python COS Direct Upload (New) +Upload directly via `cos_upload.py` without a browser: +1. Call `/resource/genUploadInfo` to obtain temporary STS credentials (`encryptTmpSecretId` / `encryptTmpSecretKey` / `encryptToken`) +2. Generate the `Authorization` header using the COS V1 signing algorithm +3. `PUT` directly to `*.cos.accelerate.myqcloud.com` +4. Obtain `resourceUrl` for subsequent image-to-3D API calls + +```python +from hunyuan3dweb.cos_upload import upload_image + +resource_url = upload_image("/path/to/image.png") +``` --- @@ -63,9 +72,14 @@ You cannot directly use a COS URL; you must upload through the browser to obtain | File | Description | |------|-------------| | `hunyuan3dweb/sign.py` | Signing algorithm in pure Python | -| `hunyuan3dweb/api.py` | Pure Python API client | +| `hunyuan3dweb/api.py` | Basic API client (image2model, text2model) | +| `hunyuan3dweb/api_complete.py` | Full API client (all generation modes) | +| `hunyuan3dweb/cos_upload.py` | Pure Python COS upload helper (no browser needed) | +| `hunyuan3dweb/config.py` | User config path management (cookies, profile) | +| `hunyuan3dweb/cli.py` | CLI entry point | | `hunyuan3dweb/browser/login.py` | Browser automation login tool | -| `doc/api.md` | API endpoint documentation | +| `hunyuan3dweb/browser/generator.py` | Browser automation image-to-3D | +| `hunyuan3dweb/browser/sniffer.py` | API request sniffer | --- @@ -77,29 +91,46 @@ You cannot directly use a COS URL; you must upload through the browser to obtain hunyuan3dweb-login # Follow prompts to enter email and verification code # Login state is automatically saved to ~/.config/hunyuan3dweb/profile +# Cookies are also exported to ~/.config/hunyuan3dweb/cookies.txt ``` +**Login State Detection Logic** (dual verification): +- Check if the current page URL contains `"login"` +- Check if a "Login" button exists on the page +- Only considered logged in when URL does NOT contain login **AND** no login button is present + ### 2. Pure Python 3D Generation ```python from hunyuan3dweb import Hunyuan3DAPI +# Automatically load cookie from ~/.config/hunyuan3dweb/cookies.txt api = Hunyuan3DAPI() # Check quota quota = api.get_quota_info() -# Generate 3D model -result = api.generate_3d( - image_url="https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=...", - scene_type="playGround3D-2.0", - model_type="image2ModelV3.1" -) +# Text-to-3D +result = api.generate_text("a cute panda", title="Panda Model") # Query status status = api.get_generation_status(result["creationsId"]) ``` +### 3. Local Image Upload + Image-to-3D (Pure Python) + +```python +from hunyuan3dweb.cos_upload import upload_image +from hunyuan3dweb import Hunyuan3DAPIComplete + +# Upload local image to COS +resource_url = upload_image("/path/to/image.png") + +# Call image-to-3D API +api = Hunyuan3DAPIComplete() +result = api.generate_from_image(resource_url, title="My Model") +``` + --- ## API Endpoints @@ -110,6 +141,14 @@ status = api.get_generation_status(result["creationsId"]) | Quota query | POST | `/quotainfo` | Query params | | Generate 3D | POST | `/creations/generations` | Query params | | Query status | GET | `/creations/detail` | Query params | +| Creation list | POST | `/creations/list` | Query params | +| Creation count | POST | `/creations/count` | Query params | +| Cancel generation | POST | `/creations/cancel` | Query params | +| Upload credentials | POST | `/resource/genUploadInfo` | Query params | +| Resource review | POST | `/resource/review` | Query params | +| Create share | POST | `/share` | Query params | +| Global config | GET | `/config` | Query params | +| Animation templates | GET | `/workflow/action/templates` | Query params | --- @@ -146,6 +185,32 @@ def derive_key(c: bytes) -> str: return n[:r].decode('utf-8') ``` +### COS V1 Signature (for Direct Upload) + +```python +def _cos_v1_auth(method, pathname, query_params, headers, secret_id, secret_key, key_time): + sign_key = hmac.new(secret_key.encode("utf-8"), key_time.encode("utf-8"), hashlib.sha1).hexdigest() + + query_str = _obj2str(query_params, True) + headers_str = _obj2str(headers, True) + format_string = f"{method}\n{pathname}\n{query_str}\n{headers_str}\n" + + format_string_sha1 = hashlib.sha1(format_string.encode("utf-8")).hexdigest() + string_to_sign = f"sha1\n{key_time}\n{format_string_sha1}\n" + + signature = hmac.new(sign_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1).hexdigest() + + return "&".join([ + "q-sign-algorithm=sha1", + f"q-ak={secret_id}", + f"q-sign-time={key_time}", + f"q-key-time={key_time}", + f"q-header-list={';'.join(sorted(headers.keys())).lower()}", + f"q-url-param-list={';'.join(sorted(query_params.keys())).lower()}", + f"q-signature={signature}", + ]) +``` + ### Request Signing ```python @@ -180,15 +245,16 @@ def sign(params: dict) -> dict: | Quota query | Works | Works | ✅ | | Generate request | Works | Works | ✅ | | Status query | Works | Works | ✅ | +| COS direct upload | Browser upload | Python upload | ✅ | --- ## Limitations and Notes -1. **Image Upload**: Still requires a browser environment to upload images and obtain resourceId (Tencent COS requires temporary signatures) -2. **Cookie Expiration**: Login sessions expire and need periodic re-login -3. **Quota Limit**: Each account has a generation quota limit (default 20/day) -4. **Rate Limiting**: Frequent calls may trigger anti-bot measures +1. **Cookie Expiration**: Login sessions expire and need periodic re-login +2. **Quota Limit**: Each account has a generation quota limit (default 20/day) +3. **Rate Limiting**: Frequent calls may trigger anti-bot measures +4. **Concurrency Safety**: When multiple tools run simultaneously, `login.py` exclusively uses the standard profile directory, while `sniffer.py` / `generator.py` automatically copy the profile to a temporary directory to avoid Chromium `SingletonLock` conflicts --- @@ -196,7 +262,7 @@ def sign(params: dict) -> dict: ``` requests -cloakbrowser (for login and upload) +cloakbrowser (for login and browser automation) ``` --- diff --git a/README_REVERSE_ENGINEERING_CN.md b/README_REVERSE_ENGINEERING_CN.md index 27ffc74..f494563 100644 --- a/README_REVERSE_ENGINEERING_CN.md +++ b/README_REVERSE_ENGINEERING_CN.md @@ -47,14 +47,23 @@ param_str = "nonce=yyy×tamp=xxx" sign = HMAC-SHA256(param_str, key="Hf6d6KFB3D") ``` -### 3. 图片URL格式 +### 3. 图片上传方式演进 -必须使用腾讯内部资源格式: -``` -https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=<32位十六进制> -``` +#### 方式一:浏览器自动化上传(原始方式) +通过 `generator.py` 在浏览器内完成上传,获取腾讯内部资源格式的 `resourceId`。 -不能直接上传 COS URL,需要通过浏览器上传获取 resourceId。 +#### 方式二:纯 Python COS 直传(新方式) +通过 `cos_upload.py` 直接调用腾讯 COS API 上传文件,无需浏览器: +1. 调用 `/resource/genUploadInfo` 获取临时 STS 凭证(`encryptTmpSecretId` / `encryptTmpSecretKey` / `encryptToken`) +2. 使用 COS V1 签名算法生成 `Authorization` 请求头 +3. 直接 `PUT` 上传到 `*.cos.accelerate.myqcloud.com` +4. 获得 `resourceUrl` 供后续图生3D API 使用 + +```python +from hunyuan3dweb.cos_upload import upload_image + +resource_url = upload_image("/path/to/image.png") +``` --- @@ -62,12 +71,15 @@ https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=<32位十六 | 文件 | 说明 | |------|------| -| `hunyuan3d_sign.py` | 签名算法纯 Python 实现 | -| `hunyuan3d_api.py` | 纯 Python API 客户端 | -| `hunyuan3d_login.py` | 浏览器自动化登录工具 | -| `get_resource_id.py` | 获取图片 resourceId 工具 | -| `cookies.txt` | Cookie 存储文件 | -| `uploaded_image_url.txt` | 上传后的图片 URL | +| `hunyuan3dweb/sign.py` | 签名算法纯 Python 实现 | +| `hunyuan3dweb/api.py` | 基础 API 客户端(图生3D、文生3D) | +| `hunyuan3dweb/api_complete.py` | 完整 API 客户端(所有生成模式) | +| `hunyuan3dweb/cos_upload.py` | 纯 Python COS 上传工具(无需浏览器) | +| `hunyuan3dweb/config.py` | 用户配置路径管理(cookie、profile) | +| `hunyuan3dweb/cli.py` | CLI 入口 | +| `hunyuan3dweb/browser/login.py` | 浏览器自动化登录工具 | +| `hunyuan3dweb/browser/generator.py` | 浏览器自动化图生3D | +| `hunyuan3dweb/browser/sniffer.py` | API 请求嗅探工具 | --- @@ -76,46 +88,49 @@ https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=<32位十六 ### 1. 登录获取 Cookie ```bash -python hunyuan3d_login.py +hunyuan3dweb-login # 按提示输入邮箱和验证码 -# 登录状态自动保存到 ./hunyuan3d_profile +# 登录状态自动保存到 ~/.config/hunyuan3dweb/profile +# Cookie 同时导出到 ~/.config/hunyuan3dweb/cookies.txt ``` -### 2. 提取 Cookie +**登录状态检测逻辑**(双重验证): +- 检查当前页面 URL 是否包含 `"login"` +- 检查页面上是否存在"登录"按钮 +- 只有 URL 不含 login **且** 没有登录按钮时,才判定为已登录 -```bash -python get_cookie_persistent.py -# 生成 cookies.txt -``` - -### 3. 上传图片获取 resourceId - -```bash -python get_resource_id_v2.py -# 生成 uploaded_image_url.txt -``` - -### 4. 纯 Python 生成3D模型 +### 2. 纯 Python 生成3D模型 ```python -from hunyuan3d_api import Hunyuan3DAPI +from hunyuan3dweb import Hunyuan3DAPI -api = Hunyuan3DAPI("hunyuan_user=xxx; hunyuan_token=yyy") +# 自动从 ~/.config/hunyuan3dweb/cookies.txt 加载 cookie +api = Hunyuan3DAPI() # 查询配额 quota = api.get_quota_info() -# 生成3D模型 -result = api.generate_3d( - image_url="https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=...", - scene_type="playGround3D-2.0", - model_type="image2ModelV3.1" -) +# 文生3D +result = api.generate_text("一只可爱的熊猫", title="熊猫模型") # 查询状态 status = api.get_generation_status(result["creationsId"]) ``` +### 3. 本地图片上传 + 图生3D(纯 Python) + +```python +from hunyuan3dweb.cos_upload import upload_image +from hunyuan3dweb import Hunyuan3DAPIComplete + +# 上传本地图片到 COS +resource_url = upload_image("/path/to/image.png") + +# 调用图生3D API +api = Hunyuan3DAPIComplete() +result = api.generate_from_image(resource_url, title="我的模型") +``` + --- ## API 端点 @@ -126,6 +141,14 @@ status = api.get_generation_status(result["creationsId"]) | 配额查询 | POST | `/quotainfo` | 查询参数 | | 生成3D | POST | `/creations/generations` | 查询参数 | | 查询状态 | GET | `/creations/detail` | 查询参数 | +| 作品列表 | POST | `/creations/list` | 查询参数 | +| 作品数量 | POST | `/creations/count` | 查询参数 | +| 取消生成 | POST | `/creations/cancel` | 查询参数 | +| 上传凭证 | POST | `/resource/genUploadInfo` | 查询参数 | +| 资源审核 | POST | `/resource/review` | 查询参数 | +| 创建分享 | POST | `/share` | 查询参数 | +| 全局配置 | GET | `/config` | 查询参数 | +| 动画模板 | GET | `/workflow/action/templates` | 查询参数 | --- @@ -162,6 +185,32 @@ def derive_key(c: bytes) -> str: return n[:r].decode('utf-8') ``` +### COS V1 签名(用于直传) + +```python +def _cos_v1_auth(method, pathname, query_params, headers, secret_id, secret_key, key_time): + sign_key = hmac.new(secret_key.encode("utf-8"), key_time.encode("utf-8"), hashlib.sha1).hexdigest() + + query_str = _obj2str(query_params, True) + headers_str = _obj2str(headers, True) + format_string = f"{method}\n{pathname}\n{query_str}\n{headers_str}\n" + + format_string_sha1 = hashlib.sha1(format_string.encode("utf-8")).hexdigest() + string_to_sign = f"sha1\n{key_time}\n{format_string_sha1}\n" + + signature = hmac.new(sign_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1).hexdigest() + + return "&".join([ + "q-sign-algorithm=sha1", + f"q-ak={secret_id}", + f"q-sign-time={key_time}", + f"q-key-time={key_time}", + f"q-header-list={';'.join(sorted(headers.keys())).lower()}", + f"q-url-param-list={';'.join(sorted(query_params.keys())).lower()}", + f"q-signature={signature}", + ]) +``` + ### 请求签名 ```python @@ -196,15 +245,16 @@ def sign(params: dict) -> dict: | 配额查询 | 可用 | 可用 | ✅ | | 生成请求 | 可用 | 可用 | ✅ | | 状态查询 | 可用 | 可用 | ✅ | +| COS 直传 | 浏览器上传 | Python 上传 | ✅ | --- ## 限制与注意事项 -1. **图片上传**: 仍需浏览器环境上传图片获取 resourceId(腾讯 COS 需要临时签名) -2. **Cookie 有效期**: 登录状态会过期,需要定期重新登录 -3. **配额限制**: 每个账号有生成配额限制(默认20次/天) -4. **风控检测**: 频繁调用可能触发风控 +1. **Cookie 有效期**: 登录状态会过期,需要定期重新登录 +2. **配额限制**: 每个账号有生成配额限制(默认20次/天) +3. **风控检测**: 频繁调用可能触发风控 +4. **并发安全**: 多个工具同时运行时,`login.py` 独享标准 profile 目录,`sniffer.py` / `generator.py` 会自动复制 profile 到临时目录启动,避免 Chromium `SingletonLock` 冲突 --- @@ -212,7 +262,7 @@ def sign(params: dict) -> dict: ``` requests -cloakbrowser (用于登录和上传) +cloakbrowser (用于登录和浏览器自动化) ``` ---