feat: tencent hunyuan (#263)

* feat: tencent hunyuan
* docs: update docs
This commit is contained in:
John Howe
2025-04-05 21:35:27 +08:00
committed by GitHub
parent b97f291f53
commit c01de9c748
6 changed files with 181 additions and 7 deletions

View File

@@ -16,6 +16,7 @@
<img src="assets/zhipu-color.svg" alt="Zhipu GLM-4V-PLUS" width="60" height="60" />
<img src="assets/gemini-brand-color.svg" alt="Google Gemini 1.5 Pro" width="60" height="60" />
<img src="assets/qwen-color.svg" alt="Qwen-2.5-72B-Instruct" width="60" height="60" />
<img src="assets/hunyuan-color.svg" alt="Tencent Hunyuan" width="50" height="60" />
<img src="assets/minimax-color.svg" alt="Minimax" width="20" height="60" />
<img src="assets/minimax-text.svg" alt="Minimax" width="60" height="60" />
<img src="assets/siliconcloud-color.svg" alt="SiliconFlow" width="15" height="60" />
@@ -47,6 +48,7 @@
- **( :tada: NEW)自动生成风格变换的视频封面**:采用图生图多模态模型,自动获取视频截图并上传风格变换后的视频封面。
- `Minimax image-01`
- `Kwai Kolors`
- `Tencent Hunyuan`
项目架构流程如下:
@@ -191,18 +193,28 @@ MLLM 模型主要用于自动切片后的切片标题生成,此功能默认关
在项目的自动切片功能需要使用到 Qwen-2.5-72B-Instruct 模型,请自行[注册账号](https://bailian.console.aliyun.com/?apiKey=1)并申请 API Key填写到 `bilive.toml` 文件中对应的 `QWEN_API_KEY` 中。
##### 3.2.4 Minimax 模型
#### 3.3 Image Generation Model
> 如需使用 Minimax 模型,请将 `bilive.toml` 文件中 `generate_cover` 参数设置为 `true`,并将 `IMAGE_GEN_MODEL` 参数设置为 `minimax`
采用图生图多模态模型,自动获取视频截图并上传风格变换后的视频封面,如需使用本功能,请将 `bilive.toml` 文件中 `generate_cover` 参数设置为 `true`
##### 3.3.1 Minimax 模型
> 如需使用 Minimax 模型,请将 `IMAGE_GEN_MODEL` 参数设置为 `minimax`。
在项目的自动切片功能需要使用到 Minimax 模型,请自行[注册账号](https://www.minimax.chat/)并申请 API Key填写到 `bilive.toml` 文件中对应的 `MINIMAX_API_KEY` 中。
##### 3.2.5 Kwai Kolors 模型
##### 3.3.2 Kwai Kolors 模型
> 如需使用 Kwai Kolors 模型,请将 `bilive.toml` 文件中 `generate_cover` 参数设置为 `true`,并将 `IMAGE_GEN_MODEL` 参数设置为 `siliconflow`,采用 siliconflow 部署的 Kolors 模型。
> 如需使用 Kwai Kolors 模型,请将 `IMAGE_GEN_MODEL` 参数设置为 `siliconflow`,采用 siliconflow 部署的 Kolors 模型。
请自行[注册账号](https://cloud.siliconflow.cn/i/3Szr5BVg)并申请 API Key填写到 `bilive.toml` 文件中对应的 `SILICONFLOW_API_KEY` 中。
##### 3.3.3 Tencent Hunyuan 模型
> 如需使用 Tencent Hunyuan 模型,请将 `IMAGE_GEN_MODEL` 参数设置为 `tencent`。
请自行[注册账号](https://console.cloud.tencent.com/cam/capi)并申请 API Key填写到 `bilive.toml` 文件中对应的 `TENCENT_SECRET_ID``TENCENT_SECRET_KEY` 中。
#### 4. bilitool 登录
> 由于一般日志打印不出二维码效果docker 的日志不确定是否能打印等发布新image时再修改docker 版本请先参考文档 [bilive](https://bilive.timerring.com),本 README 只针对源码部署),所以这步需要提前在机器上安装 [bilitool](https://github.com/timerring/bilitool):

1
assets/hunyuan-color.svg Normal file
View File

@@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Hunyuan</title><g fill="none" fill-rule="evenodd"><circle cx="12" cy="12" fill="#0055E9" r="12"></circle><path d="M12 0c.518 0 1.028.033 1.528.096A6.188 6.188 0 0112.12 12.28l-.12.001c-2.99 0-5.242 2.179-5.554 5.11-.223 2.086.353 4.412 2.242 6.146C3.672 22.1 0 17.479 0 12 0 5.373 5.373 0 12 0z" fill="#A8DFF5"></path><path d="M5.286 5a2.438 2.438 0 01.682 3.38c-3.962 5.966-3.215 10.743 2.648 15.136C3.636 22.056 0 17.452 0 12c0-1.787.39-3.482 1.09-5.006.253-.435.525-.872.817-1.311A2.438 2.438 0 015.286 5z" fill="#0055E9"></path><path d="M12.98.04c.272.021.543.053.81.093.583.106 1.117.254 1.538.44 6.638 2.927 8.07 10.052 1.748 15.642a4.125 4.125 0 01-5.822-.358c-1.51-1.706-1.3-4.184.357-5.822.858-.848 3.108-1.223 4.045-2.441 1.257-1.634 2.122-6.009-2.523-7.506L12.98.039z" fill="#00BCFF"></path><path d="M13.528.096A6.187 6.187 0 0112 12.281a5.75 5.75 0 00-1.71.255c.147-.905.595-1.784 1.321-2.501.858-.848 3.108-1.223 4.045-2.441 1.27-1.651 2.14-6.104-2.676-7.554.184.014.367.033.548.056z" fill="#ECECEE"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -37,6 +37,8 @@ qwen_api_key = "" # Apply for your own Qwen API key at https://bailian.console.a
[cover]
generate_cover = false # whether to generate cover
image_gen_model = "minimax" # the image generation model, can be "minimax" or "siliconflow"
image_gen_model = "minimax" # the image generation model, can be "minimax" or "siliconflow" or "tencent"
minimax_api_key = "" # Apply for your own Minimax API key at https://platform.minimaxi.com/user-center/basic-information/interface-key
siliconflow_api_key = "" # Apply for your own SiliconFlow API key at https://cloud.siliconflow.cn/i/3Szr5BVg
tencent_secret_id = "" # Apply for your own Tencent Cloud API key at https://console.cloud.tencent.com/cam/capi
tencent_secret_key = "" # Apply for your own Tencent Cloud secret key as above

View File

@@ -76,3 +76,5 @@ GENERATE_COVER = config.get('cover', {}).get('generate_cover')
IMAGE_GEN_MODEL = config.get('cover', {}).get('image_gen_model')
MINIMAX_API_KEY = config.get('cover', {}).get('minimax_api_key')
SILICONFLOW_API_KEY = config.get('cover', {}).get('siliconflow_api_key')
TENCENT_SECRET_ID = config.get('cover', {}).get('tencent_secret_id')
TENCENT_SECRET_KEY = config.get('cover', {}).get('tencent_secret_key')

View File

@@ -59,6 +59,10 @@ def cover_generator(model_type):
from .image_model_sdk.kolors_sdk import kolors_generate_cover
return kolors_generate_cover(cover_path)
elif model_type == "tencent":
from .image_model_sdk.tencent_sdk import hunyuan_generate_cover
return hunyuan_generate_cover(cover_path)
else:
upload_log.error(f"Unsupported model type: {model_type}")
return None

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
import hashlib
import hmac
import json
import sys
import time
import base64
from datetime import datetime
import os
import requests
from src.config import TENCENT_SECRET_ID, TENCENT_SECRET_KEY
if sys.version_info[0] <= 2:
from httplib import HTTPSConnection
else:
from http.client import HTTPSConnection
def sign(key, msg):
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
def post_request(action, payload):
secret_id = TENCENT_SECRET_ID
secret_key = TENCENT_SECRET_KEY
token = ""
service = "hunyuan"
host = "hunyuan.tencentcloudapi.com"
region = "ap-guangzhou"
version = "2023-09-01"
params = json.loads(payload)
endpoint = "https://hunyuan.tencentcloudapi.com"
algorithm = "TC3-HMAC-SHA256"
timestamp = int(time.time())
date = datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d")
# ************* step 1 canonical request *************
http_request_method = "POST"
canonical_uri = "/"
canonical_querystring = ""
ct = "application/json; charset=utf-8"
canonical_headers = "content-type:%s\nhost:%s\nx-tc-action:%s\n" % (
ct,
host,
action.lower(),
)
signed_headers = "content-type;host;x-tc-action"
hashed_request_payload = hashlib.sha256(payload.encode("utf-8")).hexdigest()
canonical_request = (
http_request_method
+ "\n"
+ canonical_uri
+ "\n"
+ canonical_querystring
+ "\n"
+ canonical_headers
+ "\n"
+ signed_headers
+ "\n"
+ hashed_request_payload
)
# ************* step 2 string to sign *************
credential_scope = date + "/" + service + "/" + "tc3_request"
hashed_canonical_request = hashlib.sha256(
canonical_request.encode("utf-8")
).hexdigest()
string_to_sign = (
algorithm
+ "\n"
+ str(timestamp)
+ "\n"
+ credential_scope
+ "\n"
+ hashed_canonical_request
)
# ************* step 3 calculate signature *************
secret_date = sign(("TC3" + secret_key).encode("utf-8"), date)
secret_service = sign(secret_date, service)
secret_signing = sign(secret_service, "tc3_request")
signature = hmac.new(
secret_signing, string_to_sign.encode("utf-8"), hashlib.sha256
).hexdigest()
# ************* step 4 build authorization *************
authorization = (
algorithm
+ " "
+ "Credential="
+ secret_id
+ "/"
+ credential_scope
+ ", "
+ "SignedHeaders="
+ signed_headers
+ ", "
+ "Signature="
+ signature
)
# ************* step 5 build and send request *************
headers = {
"Authorization": authorization,
"Content-Type": "application/json; charset=utf-8",
"Host": host,
"X-TC-Action": action,
"X-TC-Timestamp": timestamp,
"X-TC-Version": version,
}
if region:
headers["X-TC-Region"] = region
if token:
headers["X-TC-Token"] = token
try:
req = HTTPSConnection(host)
req.request("POST", "/", headers=headers, body=payload.encode("utf-8"))
resp = req.getresponse()
return resp.read()
except Exception as err:
print(err)
def hunyuan_generate_cover(your_file_path):
submit_action = "SubmitHunyuanImageJob"
with open(your_file_path, "rb") as image_file:
data = base64.b64encode(image_file.read()).decode("utf-8")
payload = f'{{"Prompt":"这是一个视频截图,请尝试根据该图生成对应的动漫类型的封面","Style":"riman","ContentImage":{{"ImageBase64":"data:image/png;base64,{data}"}}}}'
submit_return = post_request(submit_action, payload).decode("utf-8")
job_id = json.loads(submit_return)["Response"]["JobId"]
query_action = "QueryHunyuanImageJob"
payload = f'{{"JobId":"{job_id}"}}'
max_retries = 30
retries = 0
while retries < max_retries:
query_return = post_request(query_action, payload).decode("utf-8")
if json.loads(query_return)["Response"]["JobStatusCode"] == "5":
image_url = json.loads(query_return)["Response"]["ResultImage"][0]
img_data = requests.get(image_url).content
cover_name = time.strftime("%Y%m%d%H%M%S") + ".png"
temp_cover_path = os.path.join(os.path.dirname(your_file_path), cover_name)
with open(temp_cover_path, "wb") as handler:
handler.write(img_data)
os.remove(your_file_path)
return temp_cover_path
else:
time.sleep(1)
retries += 1
return None
if __name__ == "__main__":
print(hunyuan_generate_cover(""))