mirror of
https://github.com/timerring/bilive.git
synced 2026-05-06 21:40:20 +08:00
feat: tencent hunyuan (#263)
* feat: tencent hunyuan * docs: update docs
This commit is contained in:
20
README.md
20
README.md
@@ -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
1
assets/hunyuan-color.svg
Normal 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 |
@@ -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
|
||||
@@ -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')
|
||||
@@ -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
|
||||
|
||||
153
src/cover/image_model_sdk/tencent_sdk.py
Normal file
153
src/cover/image_model_sdk/tencent_sdk.py
Normal 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(""))
|
||||
Reference in New Issue
Block a user