Merge pull request #181 from linyqh/dev072

新增大模型代理,新增腾讯tts,优化 gemini 请求
This commit is contained in:
viccy 2025-09-17 00:11:50 +08:00 committed by GitHub
commit b8013ee468
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 122 additions and 509 deletions

5
.gitignore vendored
View File

@ -36,4 +36,7 @@ app/models/faster-whisper-large-v3/*
app/models/bert/*
bug清单.md
task.md
task.md
.claude/*
.serena/*
CLAUDE.md

View File

@ -25,16 +25,13 @@ NarratoAI 是一个自动化影视解说工具基于LLM实现文案撰写、
![](docs/index-zh.png)
<h3>视频审查界面</h3>
![](docs/check-zh.png)
</div>
## 许可证
本项目仅供学习和研究使用,不得商用。如需商业授权,请联系作者。
## 最新资讯
- 2025.08.18 发布新版本 0.7.1,支持 **语音克隆** 和 最新大模型
- 2025.05.11 发布新版本 0.6.0,支持 **短剧解说** 和 优化剪辑流程
- 2025.03.06 发布新版本 0.5.2,支持 DeepSeek R1 和 DeepSeek V3 模型进行短剧混剪
- 2024.12.16 发布新版本 0.3.9,支持阿里 Qwen2-VL 模型理解视频;支持短剧混剪
@ -44,38 +41,45 @@ NarratoAI 是一个自动化影视解说工具基于LLM实现文案撰写、
- 2024.11.10 发布新版本 v0.3.5;优化视频剪辑流程,
## 重磅福利 🎉
即日起全面支持DeepSeek模型注册即享2000万免费Token价值14元平台配额剪辑10分钟视频仅需0.1元!
> 1
> **开发者专属福利一站式AI平台注册即送体验金**
>
> 还在为接入各种AI模型烦恼吗向您推荐 302.ai一个企业级的AI资源中心。一次接入即可调用上百种AI模型涵盖语言、图像、音视频等按量付费极大降低开发成本。
>
> 通过下方我的专属链接注册,**立获1美元免费体验金**助您轻松开启AI开发之旅。
>
> **立即注册领取:** [https://share.302.ai/I9P6mP](https://share.302.ai/I9P6mP)
🔥 快速领福利:
1⃣ 点击链接注册https://cloud.siliconflow.cn/i/pyOKqFCV
2⃣ 使用手机号登录,**务必填写邀请码pyOKqFCV**
3⃣ 领取14元配额极速体验高性价比AI剪辑
---
> 2
> 即日起全面支持硅基流动注册即享2000万免费Token价值14元平台配额剪辑10分钟视频仅需0.1元!
>
> 🔥 快速领福利:
> 1⃣ 点击链接注册https://cloud.siliconflow.cn/i/pyOKqFCV
> 2⃣ 使用手机号登录,**务必填写邀请码pyOKqFCV**
> 3⃣ 领取14元配额极速体验高性价比AI剪辑
>
> 💡 小成本大创作:
> 硅基流动API Key一键接入智能剪辑效率翻倍
> (注:邀请码为福利领取唯一凭证,注册后自动到账)
>
> 立即行动用「pyOKqFCV」解锁你的AI生产力
💡 小成本大创作:
硅基流动API Key一键接入智能剪辑效率翻倍
(注:邀请码为福利领取唯一凭证,注册后自动到账)
立即行动用「pyOKqFCV」解锁你的AI生产力
😊 更新步骤:
整合包:点击 update.bat 一键更新脚本
代码构建:使用 git pull 拉去最新代码
## ⚠️谨防被骗 📢
_**1. NarratoAI 是一款完全免费的软件,近期在社交媒体(抖音等)上发现,有人将 NarratoAI 改名后售卖,下面是部分截图,切忌不要上当受骗!!!**_
_**1. NarratoAI 是一款完全免费的软件,近期在社交媒体(抖音,B站等)上发现,有人将 NarratoAI 改名后售卖,下面是部分截图,请大家务必提高警惕,切勿上当受骗**_
<img src="https://github.com/user-attachments/assets/464b877c-b061-4856-8260-a0ef6fad7e52" alt="Screenshot_20250109_114131_Samsung Internet" style="width:30%; height:auto;">
<img src="https://github.com/user-attachments/assets/9cc0e5e4-bd5b-4655-b5ef-7d9085cdbc50" alt="Screenshot_20250109_114131_Samsung Internet" style="width:30%; height:auto;">
<img src="https://github.com/user-attachments/assets/9d7a6ea9-4bca-42b5-a61e-7e464037930f" alt="Screenshot_20250109_114131_Samsung Internet" style="width:30%; height:auto;">
<img src="https://github.com/user-attachments/assets/09eeb94d-c670-4d7d-ba19-c0468bed3291" alt="Screenshot_20250109_114131_Samsung Internet" style="width:30%; height:auto;">
---
_**2. 近期在 x (推特) 上发现有人冒充作者在 pump.fun 平台上发行代币! 这是骗子!!! 不要被割了韭菜
!!!目前 NarratoAI 没有在 x(推特) 上做任何官方宣传,注意甄别**_
<div style="display: flex; flex-wrap: wrap; justify-content: space-around; align-items: flex-start; gap: 10px;">
<img src="https://github.com/user-attachments/assets/9cc0e5e4-bd5b-4655-b5ef-7d9085cdbc50" alt="诈骗截图 1" style="width: 23%; max-width: 250px; height: auto; border: 1px solid #ddd; border-radius: 5px; box-shadow: 2px 2px 8px rgba(0,0,0,0.1);">
<img src="https://github.com/user-attachments/assets/464b877c-b061-4856-8260-a0ef6fad7e52" alt="诈骗截图 2" style="width: 23%; max-width: 250px; height: auto; border: 1px solid #ddd; border-radius: 5px; box-shadow: 2px 2px 8px rgba(0,0,0,0.1);">
<img src="https://github.com/user-attachments/assets/9d7a6ea9-4bca-42b5-a61e-7e464037930f" alt="诈骗截图 3" style="width: 23%; max-width: 250px; height: auto; border: 1px solid #ddd; border-radius: 5px; box-shadow: 2px 2px 8px rgba(0,0,0,0.1);">
<img src="https://github.com/user-attachments/assets/09eeb94d-c670-4d7d-ba19-c0468bed3291" alt="诈骗截图 4" style="width: 23%; max-width: 250px; height: auto; border: 1px solid #ddd; border-radius: 5px; box-shadow: 2px 2px 8px rgba(0,0,0,0.1);">
</div>
下面是此人 x(推特) 首页截图
<img src="https://github.com/user-attachments/assets/c492ab99-52cd-4ba2-8695-1bd2073ecf12" alt="Screenshot_20250109_114131_Samsung Internet" style="width:30%; height:auto;">
---
## 未来计划 🥳
- [x] windows 整合包发布
@ -93,6 +97,44 @@ _**2. 近期在 x (推特) 上发现有人冒充作者在 pump.fun 平台上发
- [ ] 支持更多 TTS 引擎
- [ ] ...
## 快速启动 🚀
### 方式一macos Docker 部署macos 推荐)
```bash
# 1. 克隆项目
git clone https://github.com/linyqh/NarratoAI.git
cd NarratoAI
# 2. 一键部署
docker compose up -d
# 3. 访问应用
# 浏览器打开 http://localhost:8501
```
### 方式二整合包Windows 推荐)
> *关注微信公众号 **NarratoAI 助手** 右下角菜单栏获取下载链接*
### 方式三:本地运行
```bash
# 1. 克隆项目
git clone https://github.com/linyqh/NarratoAI.git
cd NarratoAI
# 2. 安装依赖
pip install -r requirements.txt
# 3. 复制配置文件
cp config.example.toml config.toml
# 4. 编辑 config.toml配置你的 API 密钥
# 5. 启动应用
streamlit run webui.py --server.maxUploadSize=2048
# 6. 访问应用
# 浏览器打开 http://localhost:8501
```
## 配置要求 📦
- 建议最低 CPU 4核或以上内存 8G 或以上,显卡非必须

View File

@ -176,7 +176,7 @@ class VideoClipParams(BaseModel):
voice_volume: Optional[float] = Field(default=AudioVolumeDefaults.VOICE_VOLUME, description="解说语音音量")
voice_rate: Optional[float] = Field(default=1.0, description="语速")
voice_pitch: Optional[float] = Field(default=1.0, description="语调")
tts_engine: Optional[str] = Field(default="tencent", description="TTS 引擎")
tts_engine: Optional[str] = Field(default="", description="TTS 引擎")
bgm_name: Optional[str] = Field(default="random", description="背景音乐名称")
bgm_type: Optional[str] = Field(default="random", description="背景音乐类型")
bgm_file: Optional[str] = Field(default="", description="背景音乐文件")

View File

@ -152,13 +152,13 @@ class SubtitleAnalyzer:
}
# 构建请求URL
url = f"{self.base_url}/models/{self.model}:generateContent?key={self.api_key}"
url = f"{self.base_url}/models/{self.model}:generateContent"
# 发送请求
response = requests.post(
url,
json=payload,
headers={"Content-Type": "application/json", "User-Agent": "NarratoAI/1.0"},
headers={"Content-Type": "application/json", "x-goog-api-key": self.api_key},
timeout=120
)
@ -440,13 +440,13 @@ class SubtitleAnalyzer:
}
# 构建请求URL
url = f"{self.base_url}/models/{self.model}:generateContent?key={self.api_key}"
url = f"{self.base_url}/models/{self.model}:generateContent"
# 发送请求
response = requests.post(
url,
json=payload,
headers={"Content-Type": "application/json", "User-Agent": "NarratoAI/1.0"},
headers={"Content-Type": "application/json", "x-goog-api-key": self.api_key},
timeout=120
)

View File

@ -140,7 +140,7 @@ class GeminiVisionProvider(VisionModelProvider):
"""执行原生Gemini API调用包含重试机制"""
from app.config import config
url = f"{self.base_url}/models/{self.model_name}:generateContent?key={self.api_key}"
url = f"{self.base_url}/models/{self.model_name}:generateContent"
max_retries = config.app.get('llm_max_retries', 3)
base_timeout = config.app.get('llm_vision_timeout', 120)
@ -157,7 +157,7 @@ class GeminiVisionProvider(VisionModelProvider):
json=payload,
headers={
"Content-Type": "application/json",
"User-Agent": "NarratoAI/1.0"
"x-goog-api-key": self.api_key
},
timeout=timeout
)
@ -330,7 +330,7 @@ class GeminiTextProvider(TextModelProvider):
"""执行原生Gemini API调用包含重试机制"""
from app.config import config
url = f"{self.base_url}/models/{self.model_name}:generateContent?key={self.api_key}"
url = f"{self.base_url}/models/{self.model_name}:generateContent"
max_retries = config.app.get('llm_max_retries', 3)
base_timeout = config.app.get('llm_text_timeout', 180) # 文本生成任务使用更长的基础超时时间
@ -347,7 +347,7 @@ class GeminiTextProvider(TextModelProvider):
json=payload,
headers={
"Content-Type": "application/json",
"User-Agent": "NarratoAI/1.0"
"x-goog-api-key": self.api_key
},
timeout=timeout
)

View File

@ -1085,7 +1085,7 @@ def tts(
) -> Union[SubMaker, None]:
logger.info(f"使用 TTS 引擎: '{tts_engine}', 语音: '{voice_name}'")
if tts_engine == "tencent":
if tts_engine == "tencent_tts":
logger.info("分发到腾讯云 TTS")
return tencent_tts(text, voice_name, voice_file, speed=voice_rate)
@ -1093,12 +1093,16 @@ def tts(
logger.info("分发到 SoulVoice TTS")
return soulvoice_tts(text, voice_name, voice_file, speed=voice_rate)
if tts_engine == "azure":
if tts_engine == "azure_speech":
if should_use_azure_speech_services(voice_name):
logger.info("分发到 Azure Speech Services (V2)")
return azure_tts_v2(text, voice_name, voice_file)
logger.info("分发到 Edge TTS (Azure V1)")
return azure_tts_v1(text, voice_name, voice_rate, voice_pitch, voice_file)
if tts_engine == "edge_tts":
logger.info("分发到 Edge TTS")
return azure_tts_v1(text, voice_name, voice_rate, voice_pitch, voice_file)
# Fallback for unknown engine - default to azure v1
logger.warning(f"未知的 TTS 引擎: '{tts_engine}', 将默认使用 Edge TTS (Azure V1)。")

View File

@ -107,7 +107,7 @@ class VisionAnalyzer:
}
# 构建请求URL
url = f"{self.base_url}/models/{self.model_name}:generateContent?key={self.api_key}"
url = f"{self.base_url}/models/{self.model_name}:generateContent"
# 发送请求
response = await asyncio.to_thread(
@ -116,7 +116,7 @@ class VisionAnalyzer:
json=request_data,
headers={
"Content-Type": "application/json",
"User-Agent": "NarratoAI/1.0"
"x-goog-api-key": self.api_key
},
timeout=120 # 增加超时时间
)

View File

@ -230,7 +230,7 @@ class GeminiOpenAIGenerator(BaseGenerator):
}
# 构建请求URL
url = f"{self.base_url}/models/{self.model_name}:generateContent?key={self.api_key}"
url = f"{self.base_url}/models/{self.model_name}:generateContent"
# 发送请求
response = requests.post(
@ -238,7 +238,7 @@ class GeminiOpenAIGenerator(BaseGenerator):
json=request_data,
headers={
"Content-Type": "application/json",
"User-Agent": "NarratoAI/1.0"
"x-goog-api-key": self.api_key
},
timeout=120
)

View File

@ -1,5 +1,5 @@
[app]
project_version="0.7.1"
project_version="0.7.2"
# 模型验证模式配置
# true: 严格模式,只允许使用预定义支持列表中的模型(默认)

View File

@ -1 +1 @@
0.7.1
0.7.2

376
webui.txt
View File

@ -1,376 +0,0 @@
@echo off
set CURRENT_DIR=%CD%
echo ***** Current directory: %CURRENT_DIR% *****
set PYTHONPATH=%CURRENT_DIR%
set "vpn_proxy_url=%http://127.0.0.1:7890%"
:: 使用VPN代理进行一些操作例如通过代理下载文件
set "http_proxy=%vpn_proxy_url%"
set "https_proxy=%vpn_proxy_url%"
@echo off
setlocal enabledelayedexpansion
rem 创建链接和路径的数组
set "urls_paths[0]=https://zenodo.org/records/13293144/files/MicrosoftYaHeiBold.ttc|.\resource\fonts"
set "urls_paths[1]=https://zenodo.org/records/13293144/files/MicrosoftYaHeiNormal.ttc|.\resource\fonts"
set "urls_paths[2]=https://zenodo.org/records/13293144/files/STHeitiLight.ttc|.\resource\fonts"
set "urls_paths[3]=https://zenodo.org/records/13293144/files/STHeitiMedium.ttc|.\resource\fonts"
set "urls_paths[4]=https://zenodo.org/records/13293144/files/UTM%20Kabel%20KT.ttf|.\resource\fonts"
set "urls_paths[5]=https://zenodo.org/records/14167125/files/test.mp4|.\resource\videos"
set "urls_paths[6]=https://zenodo.org/records/13293150/files/output000.mp3|.\resource\songs"
set "urls_paths[7]=https://zenodo.org/records/13293150/files/output001.mp3|.\resource\songs"
set "urls_paths[8]=https://zenodo.org/records/13293150/files/output002.mp3|.\resource\songs"
set "urls_paths[9]=https://zenodo.org/records/13293150/files/output003.mp3|.\resource\songs"
set "urls_paths[10]=https://zenodo.org/records/13293150/files/output004.mp3|.\resource\songs"
set "urls_paths[11]=https://zenodo.org/records/13293150/files/output005.mp3|.\resource\songs"
set "urls_paths[12]=https://zenodo.org/records/13293150/files/output006.mp3|.\resource\songs"
set "urls_paths[13]=https://zenodo.org/records/13293150/files/output007.mp3|.\resource\songs"
set "urls_paths[14]=https://zenodo.org/records/13293150/files/output008.mp3|.\resource\songs"
set "urls_paths[15]=https://zenodo.org/records/13293150/files/output009.mp3|.\resource\songs"
set "urls_paths[16]=https://zenodo.org/records/13293150/files/output010.mp3|.\resource\songs"
rem 循环下载所有文件并保存到指定路径
for /L %%i in (0,1,16) do (
for /f "tokens=1,2 delims=|" %%a in ("!urls_paths[%%i]!") do (
if not exist "%%b" mkdir "%%b"
echo 正在下载 %%a 到 %%b
curl -o "%%b\%%~nxa" %%a
)
)
echo 所有文件已成功下载到指定目录
endlocal
pause
rem set HF_ENDPOINT=https://hf-mirror.com
streamlit run webui.py --browser.serverAddress="127.0.0.1" --server.enableCORS=True --server.maxUploadSize=2048 --browser.gatherUsageStats=False
streamlit run webui.py --server.maxUploadSize=2048
请求0
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/youtube/download' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://www.youtube.com/watch?v=Kenm35gdqtk",
"resolution": "1080p",
"output_format": "mp4",
"rename": "2024-11-19"
}'
{
"url": "https://www.youtube.com/watch?v=Kenm35gdqtk",
"resolution": "1080p",
"output_format": "mp4",
"rename": "2024-11-19"
}
请求1
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/scripts/generate' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"video_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"skip_seconds": 0,
"threshold": 30,
"vision_batch_size": 10,
"vision_llm_provider": "gemini"
}'
{
"video_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"skip_seconds": 0,
"threshold": 30,
"vision_batch_size": 10,
"vision_llm_provider": "gemini"
}
请求2
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/scripts/crop' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"video_origin_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"video_script": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是视频画面的客观描述:\n\n视频展现一名留着胡须的男子在森林里挖掘。\n\n画面首先展现男子从后方视角背着军绿色背包穿着卡其色长裤和深色T恤走向一个泥土斜坡。背包上似乎有一个镐头。\n\n下一个镜头特写展现了该背包一个镐头从背包里伸出来包里还有一些其他工具。\n\n然后视频显示该男子用镐头挖掘泥土斜坡。\n\n接下来是一些近景镜头展现男子的靴子在泥土中行走以及男子用手清理泥土。\n\n其他镜头从不同角度展现该男子在挖掘包括从侧面和上方。\n\n可以看到他用工具挖掘清理泥土并检查挖出的土壤。\n\n最后一个镜头展现了挖出的土壤的质地和颜色。",
"narration": "好的,接下来就是我们这位“胡须大侠”的精彩冒险了!只见他背着军绿色的背包,迈着比我上班还不情愿的步伐走向那泥土斜坡。哎呀,这个背包可真是个宝贝,里面藏着一把镐头和一些工具,简直像是个随身携带的“建筑工具箱”! \n\n看他挥舞着镐头挖掘泥土的姿势仿佛在进行一场“挖土大赛”结果却比我做饭还要糟糕。泥土飞扬中他的靴子也成了“泥巴艺术家”。最后那堆色泽各异的土壤就像他心情的写照——五彩斑斓又略显混乱真是一次让人捧腹的建造之旅",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是视频画面的客观描述:\n\n视频以一系列森林环境的镜头开头。\n\n第一个镜头是一个特写镜头镜头中显示的是一些带有水滴的绿色叶子。\n\n第二个镜头显示一个留着胡须的男子在森林中挖掘一个洞。 他跪在地上,用工具挖土。\n\n第三个镜头是一个中等镜头显示同一个人坐在他挖好的洞边休息。\n\n第四个镜头显示该洞的内部结构该洞在树根和地面之间。\n\n第五个镜头显示该男子用斧头砍树枝。\n\n第六个镜头显示一堆树枝横跨一个泥泞的小水坑。\n\n第七个镜头显示更多茂盛的树叶和树枝在阳光下。\n\n第八个镜头显示更多茂盛的树叶和树枝。\n\n\n",
"narration": "接下来,我们的“挖土大师”又开始了他的森林探险。看这镜头,水滴在叶子上闪烁,仿佛在说:“快来,快来,这里有故事!”他一边挖洞,一边像个新手厨师试图切洋葱——每一下都小心翼翼,生怕自己不小心挖出个“历史遗址”。坐下休息的时候,脸上的表情就像发现新大陆一样!然后,他拿起斧头砍树枝,简直是现代版的“神雕侠侣”,只不过对象是树木。最后,那堆树枝架过泥泞的小水坑,仿佛在说:“我就是不怕湿脚的勇士!”这就是我们的建造之旅!",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
]
}'
{
"video_origin_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"video_script": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是视频画面的客观描述:\n\n视频展现一名留着胡须的男子在森林里挖掘。\n\n画面首先展现男子从后方视角背着军绿色背包穿着卡其色长裤和深色T恤走向一个泥土斜坡。背包上似乎有一个镐头。\n\n下一个镜头特写展现了该背包一个镐头从背包里伸出来包里还有一些其他工具。\n\n然后视频显示该男子用镐头挖掘泥土斜坡。\n\n接下来是一些近景镜头展现男子的靴子在泥土中行走以及男子用手清理泥土。\n\n其他镜头从不同角度展现该男子在挖掘包括从侧面和上方。\n\n可以看到他用工具挖掘清理泥土并检查挖出的土壤。\n\n最后一个镜头展现了挖出的土壤的质地和颜色。",
"narration": "好的,接下来就是我们这位“胡须大侠”的精彩冒险了!只见他背着军绿色的背包,迈着比我上班还不情愿的步伐走向那泥土斜坡。哎呀,这个背包可真是个宝贝,里面藏着一把镐头和一些工具,简直像是个随身携带的“建筑工具箱”! \n\n看他挥舞着镐头挖掘泥土的姿势仿佛在进行一场“挖土大赛”结果却比我做饭还要糟糕。泥土飞扬中他的靴子也成了“泥巴艺术家”。最后那堆色泽各异的土壤就像他心情的写照——五彩斑斓又略显混乱真是一次让人捧腹的建造之旅",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是视频画面的客观描述:\n\n视频以一系列森林环境的镜头开头。\n\n第一个镜头是一个特写镜头镜头中显示的是一些带有水滴的绿色叶子。\n\n第二个镜头显示一个留着胡须的男子在森林中挖掘一个洞。 他跪在地上,用工具挖土。\n\n第三个镜头是一个中等镜头显示同一个人坐在他挖好的洞边休息。\n\n第四个镜头显示该洞的内部结构该洞在树根和地面之间。\n\n第五个镜头显示该男子用斧头砍树枝。\n\n第六个镜头显示一堆树枝横跨一个泥泞的小水坑。\n\n第七个镜头显示更多茂盛的树叶和树枝在阳光下。\n\n第八个镜头显示更多茂盛的树叶和树枝。\n\n\n",
"narration": "接下来,我们的“挖土大师”又开始了他的森林探险。看这镜头,水滴在叶子上闪烁,仿佛在说:“快来,快来,这里有故事!”他一边挖洞,一边像个新手厨师试图切洋葱——每一下都小心翼翼,生怕自己不小心挖出个“历史遗址”。坐下休息的时候,脸上的表情就像发现新大陆一样!然后,他拿起斧头砍树枝,简直是现代版的“神雕侠侣”,只不过对象是树木。最后,那堆树枝架过泥泞的小水坑,仿佛在说:“我就是不怕湿脚的勇士!”这就是我们的建造之旅!",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
]
}
请求3
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/scripts/start-subclip?task_id=12121' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"request": {
"video_clip_json": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是视频画面的客观描述:\n\n视频展现一名留着胡须的男子在森林里挖掘。\n\n画面首先展现男子从后方视角背着军绿色背包穿着卡其色长裤和深色T恤走向一个泥土斜坡。背包上似乎有一个镐头。\n\n下一个镜头特写展现了该背包一个镐头从背包里伸出来包里还有一些其他工具。\n\n然后视频显示该男子用镐头挖掘泥土斜坡。\n\n接下来是一些近景镜头展现男子的靴子在泥土中行走以及男子用手清理泥土。\n\n其他镜头从不同角度展现该男子在挖掘包括从侧面和上方。\n\n可以看到他用工具挖掘清理泥土并检查挖出的土壤。\n\n最后一个镜头展现了挖出的土壤的质地和颜色。",
"narration": "好的,接下来就是我们这位“胡须大侠”的精彩冒险了!只见他背着军绿色的背包,迈着比我上班还不情愿的步伐走向那泥土斜坡。哎呀,这个背包可真是个宝贝,里面藏着一把镐头和一些工具,简直像是个随身携带的“建筑工具箱”! \n\n看他挥舞着镐头挖掘泥土的姿势仿佛在进行一场“挖土大赛”结果却比我做饭还要糟糕。泥土飞扬中他的靴子也成了“泥巴艺术家”。最后那堆色泽各异的土壤就像他心情的写照——五彩斑斓又略显混乱真是一次让人捧腹的建造之旅",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是视频画面的客观描述:\n\n视频以一系列森林环境的镜头开头。\n\n第一个镜头是一个特写镜头镜头中显示的是一些带有水滴的绿色叶子。\n\n第二个镜头显示一个留着胡须的男子在森林中挖掘一个洞。 他跪在地上,用工具挖土。\n\n第三个镜头是一个中等镜头显示同一个人坐在他挖好的洞边休息。\n\n第四个镜头显示该洞的内部结构该洞在树根和地面之间。\n\n第五个镜头显示该男子用斧头砍树枝。\n\n第六个镜头显示一堆树枝横跨一个泥泞的小水坑。\n\n第七个镜头显示更多茂盛的树叶和树枝在阳光下。\n\n第八个镜头显示更多茂盛的树叶和树枝。\n\n\n",
"narration": "接下来,我们的“挖土大师”又开始了他的森林探险。看这镜头,水滴在叶子上闪烁,仿佛在说:“快来,快来,这里有故事!”他一边挖洞,一边像个新手厨师试图切洋葱——每一下都小心翼翼,生怕自己不小心挖出个“历史遗址”。坐下休息的时候,脸上的表情就像发现新大陆一样!然后,他拿起斧头砍树枝,简直是现代版的“神雕侠侣”,只不过对象是树木。最后,那堆树枝架过泥泞的小水坑,仿佛在说:“我就是不怕湿脚的勇士!”这就是我们的建造之旅!",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
],
"video_clip_json_path": "E:\\projects\\NarratoAI\\resource\\scripts\\2024-1118-230421.json",
"video_origin_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"video_aspect": "16:9",
"video_language": "zh-CN",
"voice_name": "zh-CN-YunjianNeural",
"voice_volume": 1,
"voice_rate": 1.2,
"voice_pitch": 1,
"bgm_name": "random",
"bgm_type": "random",
"bgm_file": "",
"bgm_volume": 0.3,
"subtitle_enabled": true,
"subtitle_position": "bottom",
"font_name": "STHeitiMedium.ttc",
"text_fore_color": "#FFFFFF",
"text_background_color": "transparent",
"font_size": 75,
"stroke_color": "#000000",
"stroke_width": 1.5,
"custom_position": 70,
"n_threads": 8
},
"subclip_videos": {
"00:10-01:01": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-00_10-01_01.mp4",
"01:07-01:53": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-01_07-01_53.mp4"
}
}'
{
"request": {
"video_clip_json": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是视频画面的客观描述:\n\n视频展现一名留着胡须的男子在森林里挖掘。\n\n画面首先展现男子从后方视角背着军绿色背包穿着卡其色长裤和深色T恤走向一个泥土斜坡。背包上似乎有一个镐头。\n\n下一个镜头特写展现了该背包一个镐头从背包里伸出来包里还有一些其他工具。\n\n然后视频显示该男子用镐头挖掘泥土斜坡。\n\n接下来是一些近景镜头展现男子的靴子在泥土中行走以及男子用手清理泥土。\n\n其他镜头从不同角度展现该男子在挖掘包括从侧面和上方。\n\n可以看到他用工具挖掘清理泥土并检查挖出的土壤。\n\n最后一个镜头展现了挖出的土壤的质地和颜色。",
"narration": "好的,接下来就是我们这位“胡须大侠”的精彩冒险了!只见他背着军绿色的背包,迈着比我上班还不情愿的步伐走向那泥土斜坡。哎呀,这个背包可真是个宝贝,里面藏着一把镐头和一些工具,简直像是个随身携带的“建筑工具箱”! \n\n看他挥舞着镐头挖掘泥土的姿势仿佛在进行一场“挖土大赛”结果却比我做饭还要糟糕。泥土飞扬中他的靴子也成了“泥巴艺术家”。最后那堆色泽各异的土壤就像他心情的写照——五彩斑斓又略显混乱真是一次让人捧腹的建造之旅",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是视频画面的客观描述:\n\n视频以一系列森林环境的镜头开头。\n\n第一个镜头是一个特写镜头镜头中显示的是一些带有水滴的绿色叶子。\n\n第二个镜头显示一个留着胡须的男子在森林中挖掘一个洞。 他跪在地上,用工具挖土。\n\n第三个镜头是一个中等镜头显示同一个人坐在他挖好的洞边休息。\n\n第四个镜头显示该洞的内部结构该洞在树根和地面之间。\n\n第五个镜头显示该男子用斧头砍树枝。\n\n第六个镜头显示一堆树枝横跨一个泥泞的小水坑。\n\n第七个镜头显示更多茂盛的树叶和树枝在阳光下。\n\n第八个镜头显示更多茂盛的树叶和树枝。\n\n\n",
"narration": "接下来,我们的“挖土大师”又开始了他的森林探险。看这镜头,水滴在叶子上闪烁,仿佛在说:“快来,快来,这里有故事!”他一边挖洞,一边像个新手厨师试图切洋葱——每一下都小心翼翼,生怕自己不小心挖出个“历史遗址”。坐下休息的时候,脸上的表情就像发现新大陆一样!然后,他拿起斧头砍树枝,简直是现代版的“神雕侠侣”,只不过对象是树木。最后,那堆树枝架过泥泞的小水坑,仿佛在说:“我就是不怕湿脚的勇士!”这就是我们的建造之旅!",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
],
"video_clip_json_path": "E:\\projects\\NarratoAI\\resource\\scripts\\2024-1118-230421.json",
"video_origin_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"video_aspect": "16:9",
"video_language": "zh-CN",
"voice_name": "zh-CN-YunjianNeural",
"voice_volume": 1,
"voice_rate": 1.2,
"voice_pitch": 1,
"bgm_name": "random",
"bgm_type": "random",
"bgm_file": "",
"bgm_volume": 0.3,
"subtitle_enabled": true,
"subtitle_position": "bottom",
"font_name": "STHeitiMedium.ttc",
"text_fore_color": "#FFFFFF",
"text_background_color": "transparent",
"font_size": 75,
"stroke_color": "#000000",
"stroke_width": 1.5,
"custom_position": 70,
"n_threads": 8
},
"subclip_videos": {
"00:10-01:01": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-00_10-01_01.mp4",
"01:07-01:53": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-01_07-01_53.mp4"
}
}
请在最外层新建一个pipeline 工作流执行逻辑的代码;
他会按照下面的顺序请求接口
1.下载视频
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/youtube/download' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://www.youtube.com/watch?v=Kenm35gdqtk",
"resolution": "1080p",
"output_format": "mp4",
"rename": "2024-11-19"
}'
2.生成脚本
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/scripts/generate' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"video_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"skip_seconds": 0,
"threshold": 30,
"vision_batch_size": 10,
"vision_llm_provider": "gemini"
}'
3. 剪辑视频
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/scripts/crop' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"video_origin_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"video_script": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是视频画面的客观描述:\n\n视频展现一名留着胡须的男子在森林里挖掘。\n\n画面首先展现男子从后方视角背着军绿色背包穿着卡其色长裤和深色T恤走向一个泥土斜坡。背包上似乎有一个镐头。\n\n下一个镜头特写展现了该背包一个镐头从背包里伸出来包里还有一些其他工具。\n\n然后视频显示该男子用镐头挖掘泥土斜坡。\n\n接下来是一些近景镜头展现男子的靴子在泥土中行走以及男子用手清理泥土。\n\n其他镜头从不同角度展现该男子在挖掘包括从侧面和上方。\n\n可以看到他用工具挖掘清理泥土并检查挖出的土壤。\n\n最后一个镜头展现了挖出的土壤的质地和颜色。",
"narration": "好的,接下来就是我们这位“胡须大侠”的精彩冒险了!只见他背着军绿色的背包,迈着比我上班还不情愿的步伐走向那泥土斜坡。哎呀,这个背包可真是个宝贝,里面藏着一把镐头和一些工具,简直像是个随身携带的“建筑工具箱”! \n\n看他挥舞着镐头挖掘泥土的姿势仿佛在进行一场“挖土大赛”结果却比我做饭还要糟糕。泥土飞扬中他的靴子也成了“泥巴艺术家”。最后那堆色泽各异的土壤就像他心情的写照——五彩斑斓又略显混乱真是一次让人捧腹的建造之旅",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是视频画面的客观描述:\n\n视频以一系列森林环境的镜头开头。\n\n第一个镜头是一个特写镜头镜头中显示的是一些带有水滴的绿色叶子。\n\n第二个镜头显示一个留着胡须的男子在森林中挖掘一个洞。 他跪在地上,用工具挖土。\n\n第三个镜头是一个中等镜头显示同一个人坐在他挖好的洞边休息。\n\n第四个镜头显示该洞的内部结构该洞在树根和地面之间。\n\n第五个镜头显示该男子用斧头砍树枝。\n\n第六个镜头显示一堆树枝横跨一个泥泞的小水坑。\n\n第七个镜头显示更多茂盛的树叶和树枝在阳光下。\n\n第八个镜头显示更多茂盛的树叶和树枝。\n\n\n",
"narration": "接下来,我们的“挖土大师”又开始了他的森林探险。看这镜头,水滴在叶子上闪烁,仿佛在说:“快来,快来,这里有故事!”他一边挖洞,一边像个新手厨师试图切洋葱——每一下都小心翼翼,生怕自己不小心挖出个“历史遗址”。坐下休息的时候,脸上的表情就像发现新大陆一样!然后,他拿起斧头砍树枝,简直是现代版的“神雕侠侣”,只不过对象是树木。最后,那堆树枝架过泥泞的小水坑,仿佛在说:“我就是不怕湿脚的勇士!”这就是我们的建造之旅!",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
]
}'
4.生成视频
curl -X 'POST' \
'http://127.0.0.1:8080/api/v2/scripts/start-subclip?task_id=12121' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"request": {
"video_clip_json": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是视频画面的客观描述:\n\n视频展现一名留着胡须的男子在森林里挖掘。\n\n画面首先展现男子从后方视角背着军绿色背包穿着卡其色长裤和深色T恤走向一个泥土斜坡。背包上似乎有一个镐头。\n\n下一个镜头特写展现了该背包一个镐头从背包里伸出来包里还有一些其他工具。\n\n然后视频显示该男子用镐头挖掘泥土斜坡。\n\n接下来是一些近景镜头展现男子的靴子在泥土中行走以及男子用手清理泥土。\n\n其他镜头从不同角度展现该男子在挖掘包括从侧面和上方。\n\n可以看到他用工具挖掘清理泥土并检查挖出的土壤。\n\n最后一个镜头展现了挖出的土壤的质地和颜色。",
"narration": "好的,接下来就是我们这位“胡须大侠”的精彩冒险了!只见他背着军绿色的背包,迈着比我上班还不情愿的步伐走向那泥土斜坡。哎呀,这个背包可真是个宝贝,里面藏着一把镐头和一些工具,简直像是个随身携带的“建筑工具箱”! \n\n看他挥舞着镐头挖掘泥土的姿势仿佛在进行一场“挖土大赛”结果却比我做饭还要糟糕。泥土飞扬中他的靴子也成了“泥巴艺术家”。最后那堆色泽各异的土壤就像他心情的写照——五彩斑斓又略显混乱真是一次让人捧腹的建造之旅",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是视频画面的客观描述:\n\n视频以一系列森林环境的镜头开头。\n\n第一个镜头是一个特写镜头镜头中显示的是一些带有水滴的绿色叶子。\n\n第二个镜头显示一个留着胡须的男子在森林中挖掘一个洞。 他跪在地上,用工具挖土。\n\n第三个镜头是一个中等镜头显示同一个人坐在他挖好的洞边休息。\n\n第四个镜头显示该洞的内部结构该洞在树根和地面之间。\n\n第五个镜头显示该男子用斧头砍树枝。\n\n第六个镜头显示一堆树枝横跨一个泥泞的小水坑。\n\n第七个镜头显示更多茂盛的树叶和树枝在阳光下。\n\n第八个镜头显示更多茂盛的树叶和树枝。\n\n\n",
"narration": "接下来,我们的“挖土大师”又开始了他的森林探险。看这镜头,水滴在叶子上闪烁,仿佛在说:“快来,快来,这里有故事!”他一边挖洞,一边像个新手厨师试图切洋葱——每一下都小心翼翼,生怕自己不小心挖出个“历史遗址”。坐下休息的时候,脸上的表情就像发现新大陆一样!然后,他拿起斧头砍树枝,简直是现代版的“神雕侠侣”,只不过对象是树木。最后,那堆树枝架过泥泞的小水坑,仿佛在说:“我就是不怕湿脚的勇士!”这就是我们的建造之旅!",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
],
"video_clip_json_path": "E:\\projects\\NarratoAI\\resource\\scripts\\2024-1118-230421.json",
"video_origin_path": "E:\\projects\\NarratoAI\\resource\\videos\\test.mp4",
"video_aspect": "16:9",
"video_language": "zh-CN",
"voice_name": "zh-CN-YunjianNeural",
"voice_volume": 1,
"voice_rate": 1.2,
"voice_pitch": 1,
"bgm_name": "random",
"bgm_type": "random",
"bgm_file": "",
"bgm_volume": 0.3,
"subtitle_enabled": true,
"subtitle_position": "bottom",
"font_name": "STHeitiMedium.ttc",
"text_fore_color": "#FFFFFF",
"text_background_color": "transparent",
"font_size": 75,
"stroke_color": "#000000",
"stroke_width": 1.5,
"custom_position": 70,
"n_threads": 8
},
"subclip_videos": {
"00:10-01:01": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-00_10-01_01.mp4",
"01:07-01:53": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-01_07-01_53.mp4"
}
}'
请求1返回的参数是
{
"task_id": "4e9b575f-68c0-4ae1-b218-db42b67993d0",
"output_path": "E:\\projects\\NarratoAI\\resource\\videos\\2024-11-19.mp4",
"resolution": "1080p",
"format": "mp4",
"filename": "2024-11-19.mp4"
}
output_path需要传递给请求2
请求2返回数据为
{
"task_id": "04497017-953c-44b4-bf1d-9d8ed3ebbbce",
"script": [
{
"timestamp": "00:10-01:01",
"picture": "好的,以下是對影片畫面的客觀描述:\n\n影片顯示一名留著鬍鬚的男子在一處樹林茂密的斜坡上挖掘。\n\n畫面一男子從後方出現背著一個軍綠色的背包背包裡似乎裝有工具。他穿著卡其色的長褲和深色的登山鞋。\n\n畫面二特寫鏡頭顯示男子的背包一個舊的鎬頭從包裡露出來包裡還有其他工具包括一個鏟子。\n\n畫面三男子用鎬頭在斜坡上挖土背包放在他旁邊。\n\n畫面四特寫鏡頭顯示男子的登山鞋在泥土中。\n\n畫面五男子坐在斜坡上用手清理樹根和泥土。\n\n畫面六地上有一些鬆動的泥土和落葉。\n\n畫面七男子的背包近景鏡頭他正在挖掘。\n\n畫面八男子在斜坡上挖掘揚起一陣塵土。\n\n畫面九特寫鏡頭顯示男子用手清理泥土。\n\n畫面十特寫鏡頭顯示挖出的泥土剖面可以看到土壤的層次。",
"narration": "上一个画面是我在绝美的自然中,准备开启我的“土豪”挖掘之旅。现在,你们看到这位留着胡子的“大哥”,他背着个军绿色的包,里面装的可不仅仅是工具,还有我对生活的无限热爱(以及一丝不安)。看!这把旧镐头就像我的前任——用起来费劲,但又舍不得扔掉。\n\n他在斜坡上挖土泥土飞扬仿佛在跟大地进行一场“泥巴大战”。每一铲下去都能听到大地微微的呻吟哎呀我这颗小树根可比我当年的情感纠葛还难处理呢别担心这些泥土层次分明简直可以开个“泥土博物馆”。所以朋友们跟着我一起享受这场泥泞中的乐趣吧",
"OST": 2,
"new_timestamp": "00:00-00:51"
},
{
"timestamp": "01:07-01:53",
"picture": "好的,以下是對影片畫面內容的客觀描述:\n\n影片以一系列森林環境的鏡頭開始。第一個鏡頭展示了綠葉植物的特寫鏡頭葉子上有一些水珠。接下來的鏡頭是一個男人在森林裡挖掘一個小坑他跪在地上用鏟子挖土。\n\n接下來的鏡頭是同一個男人坐在他挖的坑旁邊望著前方。然後鏡頭顯示該坑的廣角鏡頭顯示其結構和大小。\n\n之後的鏡頭同一個男人在樹林裡劈柴。鏡頭最後呈現出一潭渾濁的水周圍環繞著樹枝。然後鏡頭又回到了森林裡生長茂盛的植物特寫鏡頭。",
"narration": "好嘞,朋友们,我们已经在泥土博物馆里捣鼓了一阵子,现在是时候跟大自然亲密接触了!看看这片森林,绿叶上水珠闪闪发光,就像我曾经的爱情,虽然短暂,却美得让人心碎。\n\n现在我在这里挖个小坑感觉自己就像是一位新晋“挖土大王”不过说实话这手艺真不敢恭维连铲子都快对我崩溃了。再说劈柴这动作简直比我前任的情绪波动还要激烈最后这一潭浑浊的水别担心它只是告诉我生活就像这水总有些杂质但也别忘了要勇敢面对哦",
"OST": 2,
"new_timestamp": "00:51-01:37"
}
]
}
output_path和script参数需要传递给请求3
请求3返回参数是
{
"task_id": "b6f5a98a-b2e0-4e3d-89c5-64fb90db2ec1",
"subclip_videos": {
"00:10-01:01": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-00_10-01_01.mp4",
"01:07-01:53": "E:\\projects\\NarratoAI\\storage\\cache_videos/vid-01_07-01_53.mp4"
}
}
subclip_videos和 output_path和script参数需要传递给请求4
最后完成工作流
0代表只播放文案音频禁用视频原声1代表只播放视频原声不需要播放文案音频和字幕2代表即播放文案音频也要播放视频原声

View File

@ -112,6 +112,7 @@ def render_tts_settings(tr):
# 保存TTS引擎选择
config.ui["tts_engine"] = selected_engine
st.session_state['tts_engine'] = selected_engine
# 2. 显示引擎详细说明
if selected_engine in engine_descriptions:
@ -490,17 +491,14 @@ def render_soulvoice_engine_settings(tr):
# 音色 URI 输入
voice_uri = st.text_input(
"音色URI",
value=config.soulvoice.get("voice_uri", "speech:mcg3fdnx:clzkyf4vy00e5qr6hywum4u84:bzznlkuhcjzpbosexitr"),
value=config.soulvoice.get("voice_uri", "speech:2c2hp73s:clzkyf4vy00e5qr6hywum4u84:itjmezhxyynkyzrhhjav"),
help="请输入 SoulVoice 音色标识符",
placeholder="speech:mcg3fdnx:clzkyf4vy00e5qr6hywum4u84:bzznlkuhcjzpbosexitr"
placeholder="speech:2c2hp73s:clzkyf4vy00e5qr6hywum4u84:itjmezhxyynkyzrhhjav"
)
# 模型名称选择
model_options = [
"FunAudioLLM/CosyVoice2-0.5B",
"FunAudioLLM/CosyVoice-300M",
"FunAudioLLM/CosyVoice-300M-SFT",
"FunAudioLLM/CosyVoice-300M-Instruct"
"FunAudioLLM/CosyVoice2-0.5B"
]
saved_model = config.soulvoice.get("model", "FunAudioLLM/CosyVoice2-0.5B")
@ -636,7 +634,7 @@ def render_soulvoice_settings(tr):
saved_api_key = config.soulvoice.get("api_key", "")
saved_api_url = config.soulvoice.get("api_url", "https://tts.scsmtech.cn/tts")
saved_model = config.soulvoice.get("model", "FunAudioLLM/CosyVoice2-0.5B")
saved_voice_uri = config.soulvoice.get("voice_uri", "speech:mcg3fdnx:clzkyf4vy00e5qr6hywum4u84:bzznlkuhcjzpbosexitr")
saved_voice_uri = config.soulvoice.get("voice_uri", "speech:2c2hp73s:clzkyf4vy00e5qr6hywum4u84:itjmezhxyynkyzrhhjav")
# API Key 输入
api_key = st.text_input(
@ -650,8 +648,8 @@ def render_soulvoice_settings(tr):
voice_uri = st.text_input(
"音色 URI",
value=saved_voice_uri,
help="请输入 SoulVoice 音色标识符格式如speech:mcg3fdnx:clzkyf4vy00e5qr6hywum4u84:bzznlkuhcjzpbosexitr",
placeholder="speech:mcg3fdnx:clzkyf4vy00e5qr6hywum4u84:bzznlkuhcjzpbosexitr"
help="请输入 SoulVoice 音色标识符格式如speech:2c2hp73s:clzkyf4vy00e5qr6hywum4u84:itjmezhxyynkyzrhhjav",
placeholder="speech:2c2hp73s:clzkyf4vy00e5qr6hywum4u84:itjmezhxyynkyzrhhjav"
)
# API URL 输入(可选)
@ -822,4 +820,5 @@ def get_audio_params():
'bgm_type': st.session_state.get('bgm_type', 'random'),
'bgm_file': st.session_state.get('bgm_file', ''),
'bgm_volume': st.session_state.get('bgm_volume', AudioVolumeDefaults.BGM_VOLUME),
'tts_engine': st.session_state.get('tts_engine', "edge_tts"),
}

View File

@ -138,6 +138,7 @@ def test_vision_model_connection(api_key, base_url, model_name, provider, tr):
str: 测试结果消息
"""
import requests
logger.debug(f"大模型连通性测试: {base_url} 模型: {model_name} apikey: {api_key}")
if provider.lower() == 'gemini':
# 原生Gemini API测试
try:
@ -145,43 +146,21 @@ def test_vision_model_connection(api_key, base_url, model_name, provider, tr):
request_data = {
"contents": [{
"parts": [{"text": "直接回复我文本'当前网络可用'"}]
}],
"generationConfig": {
"temperature": 1.0,
"topK": 40,
"topP": 0.95,
"maxOutputTokens": 100,
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_NONE"
}
]
}]
}
# 构建请求URL
api_base_url = base_url or "https://generativelanguage.googleapis.com/v1beta"
url = f"{api_base_url}/models/{model_name}:generateContent?key={api_key}"
api_base_url = base_url
url = f"{api_base_url}/models/{model_name}:generateContent"
# 发送请求
response = requests.post(
url,
json=request_data,
headers={"Content-Type": "application/json"},
timeout=30
headers={
"x-goog-api-key": api_key,
"Content-Type": "application/json"
},
timeout=10
)
if response.status_code == 200:
@ -190,7 +169,6 @@ def test_vision_model_connection(api_key, base_url, model_name, provider, tr):
return False, f"{tr('原生Gemini模型连接失败')}: HTTP {response.status_code}"
except Exception as e:
return False, f"{tr('原生Gemini模型连接失败')}: {str(e)}"
elif provider.lower() == 'gemini(openai)':
# OpenAI兼容的Gemini代理测试
try:
@ -215,23 +193,6 @@ def test_vision_model_connection(api_key, base_url, model_name, provider, tr):
return False, f"{tr('OpenAI兼容Gemini代理连接失败')}: HTTP {response.status_code}"
except Exception as e:
return False, f"{tr('OpenAI兼容Gemini代理连接失败')}: {str(e)}"
elif provider.lower() == 'narratoapi':
try:
# 构建测试请求
headers = {
"Authorization": f"Bearer {api_key}"
}
test_url = f"{base_url.rstrip('/')}/health"
response = requests.get(test_url, headers=headers, timeout=10)
if response.status_code == 200:
return True, tr("NarratoAPI is available")
else:
return False, f"{tr('NarratoAPI is not available')}: HTTP {response.status_code}"
except Exception as e:
return False, f"{tr('NarratoAPI is not available')}: {str(e)}"
else:
from openai import OpenAI
try:
@ -441,7 +402,8 @@ def test_text_model_connection(api_key, base_url, model_name, provider, tr):
str: 测试结果消息
"""
import requests
logger.debug(f"大模型连通性测试: {base_url} 模型: {model_name} apikey: {api_key}")
try:
# 构建统一的测试请求遵循OpenAI格式
headers = {
@ -457,43 +419,22 @@ def test_text_model_connection(api_key, base_url, model_name, provider, tr):
request_data = {
"contents": [{
"parts": [{"text": "直接回复我文本'当前网络可用'"}]
}],
"generationConfig": {
"temperature": 1.0,
"topK": 40,
"topP": 0.95,
"maxOutputTokens": 100,
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_NONE"
}
]
}]
}
# 构建请求URL
api_base_url = base_url or "https://generativelanguage.googleapis.com/v1beta"
url = f"{api_base_url}/models/{model_name}:generateContent?key={api_key}"
api_base_url = base_url
url = f"{api_base_url}/models/{model_name}:generateContent"
# 发送请求
response = requests.post(
url,
json=request_data,
headers={"Content-Type": "application/json"},
timeout=30
headers={
"x-goog-api-key": api_key,
"Content-Type": "application/json"
},
timeout=10
)
if response.status_code == 200: