mirror of
https://github.com/linyqh/NarratoAI.git
synced 2025-12-11 02:12:50 +00:00
refactor(webui): 优化音频设置界面并添加代理配置
- 修改支持的语音列表,仅保留中文语音 - 在主程序中添加代理配置环境变量 -优化剪辑视频函数,改为返回字典类型 - 更新任务服务中的剪辑视频函数,适应新的参数类型 - 修改测试用例中的视频剪辑函数,增加输出路径参数 - 更新脚本控制器中的剪辑视频函数,集成任务 ID 和子视频字典
This commit is contained in:
parent
1d5585e752
commit
ee710499b9
@ -132,6 +132,8 @@ async def download_youtube_video(
|
||||
)
|
||||
async def start_subclip(
|
||||
request: VideoClipParams,
|
||||
task_id: str,
|
||||
subclip_videos: dict,
|
||||
background_tasks: BackgroundTasks
|
||||
):
|
||||
"""
|
||||
@ -153,13 +155,13 @@ async def start_subclip(
|
||||
# 在后台任务中执行视频剪辑
|
||||
background_tasks.add_task(
|
||||
task_service.start_subclip,
|
||||
task_id=request.task_id,
|
||||
task_id=task_id,
|
||||
params=params,
|
||||
subclip_path_videos=request.subclip_videos
|
||||
subclip_path_videos=subclip_videos
|
||||
)
|
||||
|
||||
return {
|
||||
"task_id": request.task_id,
|
||||
"task_id": task_id,
|
||||
"state": "PROCESSING" # 初始状态
|
||||
}
|
||||
|
||||
|
||||
162
app/pipeline/video_pipeline.py
Normal file
162
app/pipeline/video_pipeline.py
Normal file
@ -0,0 +1,162 @@
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
|
||||
class VideoPipeline:
|
||||
def __init__(self, base_url: str = "http://127.0.0.1:8080"):
|
||||
self.base_url = base_url
|
||||
|
||||
def download_video(self, url: str, resolution: str = "1080p",
|
||||
output_format: str = "mp4", rename: str = None) -> Dict[str, Any]:
|
||||
"""下载视频的第一步"""
|
||||
endpoint = f"{self.base_url}/api/v2/youtube/download"
|
||||
payload = {
|
||||
"url": url,
|
||||
"resolution": resolution,
|
||||
"output_format": output_format,
|
||||
"rename": rename or time.strftime("%Y-%m-%d")
|
||||
}
|
||||
|
||||
response = requests.post(endpoint, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def generate_script(self, video_path: str, skip_seconds: int = 0,
|
||||
threshold: int = 30, vision_batch_size: int = 10,
|
||||
vision_llm_provider: str = "gemini") -> Dict[str, Any]:
|
||||
"""生成脚本的第二步"""
|
||||
endpoint = f"{self.base_url}/api/v2/scripts/generate"
|
||||
payload = {
|
||||
"video_path": video_path,
|
||||
"skip_seconds": skip_seconds,
|
||||
"threshold": threshold,
|
||||
"vision_batch_size": vision_batch_size,
|
||||
"vision_llm_provider": vision_llm_provider
|
||||
}
|
||||
|
||||
response = requests.post(endpoint, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def crop_video(self, video_path: str, script: list) -> Dict[str, Any]:
|
||||
"""剪辑视频的第三步"""
|
||||
endpoint = f"{self.base_url}/api/v2/scripts/crop"
|
||||
payload = {
|
||||
"video_origin_path": video_path,
|
||||
"video_script": script
|
||||
}
|
||||
|
||||
response = requests.post(endpoint, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def generate_final_video(self, task_id: str, video_path: str,
|
||||
script_path: str, script: list, subclip_videos: Dict[str, str]) -> Dict[str, Any]:
|
||||
"""生成最终视频的第四步"""
|
||||
endpoint = f"{self.base_url}/api/v2/scripts/start-subclip"
|
||||
|
||||
request_data = {
|
||||
"video_clip_json": script,
|
||||
"video_clip_json_path": script_path,
|
||||
"video_origin_path": video_path,
|
||||
"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
|
||||
}
|
||||
|
||||
payload = {
|
||||
"request": request_data,
|
||||
"subclip_videos": subclip_videos
|
||||
}
|
||||
|
||||
params = {"task_id": task_id}
|
||||
response = requests.post(endpoint, params=params, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def save_script_to_json(self, script: list) -> str:
|
||||
"""保存脚本到json文件"""
|
||||
timestamp = time.strftime("%Y-%m%d-%H%M%S")
|
||||
script_path = f"E:\\projects\\NarratoAI\\resource\\scripts\\{timestamp}.json"
|
||||
|
||||
try:
|
||||
with open(script_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(script, f, ensure_ascii=False, indent=2)
|
||||
print(f"脚本已保存到: {script_path}")
|
||||
return script_path
|
||||
except Exception as e:
|
||||
print(f"保存脚本失败: {str(e)}")
|
||||
raise
|
||||
|
||||
def run_pipeline(self, youtube_url: str) -> Dict[str, Any]:
|
||||
"""运行完整的pipeline"""
|
||||
try:
|
||||
# 1. 下载视频
|
||||
print("开始下载视频...")
|
||||
download_result = self.download_video(youtube_url)
|
||||
video_path = download_result["output_path"]
|
||||
|
||||
# 2. 生成脚本
|
||||
print("开始生成脚本...")
|
||||
script_result = self.generate_script(video_path)
|
||||
script = script_result["script"]
|
||||
|
||||
# 2.1 保存脚本到json文件
|
||||
print("保存脚本到json文件...")
|
||||
script_path = self.save_script_to_json(script)
|
||||
script_result["script_path"] = script_path
|
||||
|
||||
# 3. 剪辑视频
|
||||
print("开始剪辑视频...")
|
||||
crop_result = self.crop_video(video_path, script)
|
||||
subclip_videos = crop_result["subclip_videos"]
|
||||
|
||||
# 4. 生成最终视频
|
||||
print("开始生成最终视频...")
|
||||
final_result = self.generate_final_video(
|
||||
crop_result["task_id"],
|
||||
video_path,
|
||||
script_path,
|
||||
script,
|
||||
subclip_videos
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"download_result": download_result,
|
||||
"script_result": script_result,
|
||||
"crop_result": crop_result,
|
||||
"final_result": final_result
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
# 使用示例
|
||||
if __name__ == "__main__":
|
||||
pipeline = VideoPipeline()
|
||||
result = pipeline.run_pipeline("https://www.youtube.com/watch?v=Kenm35gdqtk")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
result2 = pipeline.run_pipeline("https://www.youtube.com/watch?v=aEsHAcedzgw")
|
||||
print(json.dumps(result2, indent=2, ensure_ascii=False))
|
||||
@ -363,7 +363,7 @@ def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> di
|
||||
return {}
|
||||
|
||||
|
||||
def clip_videos(task_id: str, timestamp_terms: List[str], origin_video: str, progress_callback=None):
|
||||
def clip_videos(task_id: str, timestamp_terms: List[str], origin_video: str, progress_callback=None) -> dict:
|
||||
"""
|
||||
剪辑视频
|
||||
Args:
|
||||
|
||||
@ -324,7 +324,7 @@ def start(task_id, params: VideoParams, stop_at: str = "video"):
|
||||
return kwargs
|
||||
|
||||
|
||||
def start_subclip(task_id: str, params: VideoClipParams, subclip_path_videos: list):
|
||||
def start_subclip(task_id: str, params: VideoClipParams, subclip_path_videos: dict):
|
||||
"""
|
||||
后台任务(自动剪辑视频进行剪辑)
|
||||
|
||||
|
||||
@ -989,6 +989,9 @@ Gender: Female
|
||||
|
||||
Name: zh-CN-XiaoxiaoMultilingualNeural-V2
|
||||
Gender: Female
|
||||
|
||||
Name: zh-CN-YunxiNeural-V2
|
||||
Gender: Male
|
||||
""".strip()
|
||||
voices = []
|
||||
name = ""
|
||||
@ -1034,8 +1037,8 @@ def is_azure_v2_voice(voice_name: str):
|
||||
def tts(
|
||||
text: str, voice_name: str, voice_rate: float, voice_pitch: float, voice_file: str
|
||||
) -> [SubMaker, None]:
|
||||
# if is_azure_v2_voice(voice_name):
|
||||
# return azure_tts_v2(text, voice_name, voice_file)
|
||||
if is_azure_v2_voice(voice_name):
|
||||
return azure_tts_v2(text, voice_name, voice_file)
|
||||
return azure_tts_v1(text, voice_name, voice_rate, voice_pitch, voice_file)
|
||||
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ from loguru import logger
|
||||
from uuid import uuid4
|
||||
|
||||
from app.utils import utils
|
||||
from app.services import video as VideoService
|
||||
|
||||
|
||||
class YoutubeService:
|
||||
@ -61,6 +62,7 @@ class YoutubeService:
|
||||
Args:
|
||||
url: YouTube视频URL
|
||||
resolution: 目标分辨率 ('2160p', '1440p', '1080p', '720p' etc.)
|
||||
注意:对于类似'1080p60'的输入会被处理为'1080p'
|
||||
output_format: 输出视频格式
|
||||
rename: 可选的重命名
|
||||
|
||||
@ -71,23 +73,32 @@ class YoutubeService:
|
||||
task_id = str(uuid4())
|
||||
self._validate_format(output_format)
|
||||
|
||||
# 标准化分辨率格式
|
||||
base_resolution = resolution.split('p')[0] + 'p'
|
||||
|
||||
# 获取所有可用格式
|
||||
formats = self._get_video_formats(url)
|
||||
|
||||
# 查找指定分辨率的最佳视频格式
|
||||
target_format = None
|
||||
for fmt in formats:
|
||||
if fmt['resolution'] == resolution and fmt['vcodec'] != 'none':
|
||||
target_format = fmt
|
||||
break
|
||||
fmt_resolution = fmt['resolution']
|
||||
# 将格式的分辨率也标准化后进行比较
|
||||
if fmt_resolution != 'N/A':
|
||||
fmt_base_resolution = fmt_resolution.split('p')[0] + 'p'
|
||||
if fmt_base_resolution == base_resolution and fmt['vcodec'] != 'none':
|
||||
target_format = fmt
|
||||
break
|
||||
|
||||
if target_format is None:
|
||||
# 收集可用分辨率时也进行标准化
|
||||
available_resolutions = set(
|
||||
fmt['resolution'] for fmt in formats
|
||||
fmt['resolution'].split('p')[0] + 'p'
|
||||
for fmt in formats
|
||||
if fmt['resolution'] != 'N/A' and fmt['vcodec'] != 'none'
|
||||
)
|
||||
raise ValueError(
|
||||
f"未找到 {resolution} 分辨率的视频。"
|
||||
f"未找到 {base_resolution} 分辨率的视频。"
|
||||
f"可用分辨率: {', '.join(sorted(available_resolutions))}"
|
||||
)
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ def format_duration(seconds: float) -> str:
|
||||
return f"{minutes:02d}:{remaining_seconds:02d}"
|
||||
|
||||
|
||||
def cut_video(video_path: str, start_time: str, end_time: str) -> None:
|
||||
def cut_video(video_path: str, start_time: str, end_time: str, output_path: str) -> None:
|
||||
"""
|
||||
剪辑视频
|
||||
参数:
|
||||
@ -53,11 +53,13 @@ def cut_video(video_path: str, start_time: str, end_time: str) -> None:
|
||||
|
||||
# 剪辑视频
|
||||
video = video.subclip(start_seconds, end_seconds)
|
||||
video.write_videofile("../../resource/videos/cut_video2.mp4")
|
||||
video.write_videofile("../../resource/videos/cut_video3.mp4")
|
||||
|
||||
# 释放资源
|
||||
video.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cut_video("../../resource/videos/best.mp4", "00:40", "02:40")
|
||||
# cut_video("E:\\NarratoAI_v0.3.5_cuda\\NarratoAI\storage\\tasks\ca4fee22-350b-47f9-bb2f-802ad96774f7\\final-2.mp4", "00:00", "07:00", "E:\\NarratoAI_v0.3.5_cuda\\NarratoAI\storage\\tasks\\yyjx2-1")
|
||||
# cut_video("E:\\NarratoAI_v0.3.5_cuda\\NarratoAI\storage\\tasks\ca4fee22-350b-47f9-bb2f-802ad96774f7\\final-2.mp4", "07:00", "14:00", "E:\\NarratoAI_v0.3.5_cuda\\NarratoAI\storage\\tasks\\yyjx2-2")
|
||||
cut_video("E:\\NarratoAI_v0.3.5_cuda\\NarratoAI\storage\\tasks\ca4fee22-350b-47f9-bb2f-802ad96774f7\\final-2.mp4", "14:00", "22:00", "E:\\NarratoAI_v0.3.5_cuda\\NarratoAI\storage\\tasks\\yyjx2-3")
|
||||
|
||||
3
main.py
3
main.py
@ -1,3 +1,4 @@
|
||||
import os
|
||||
import uvicorn
|
||||
from loguru import logger
|
||||
|
||||
@ -7,6 +8,8 @@ if __name__ == "__main__":
|
||||
logger.info(
|
||||
"start server, docs: http://127.0.0.1:" + str(config.listen_port) + "/docs"
|
||||
)
|
||||
os.environ["HTTP_PROXY"] = config.proxy.get("http")
|
||||
os.environ["HTTPS_PROXY"] = config.proxy.get("https")
|
||||
uvicorn.run(
|
||||
app="app.asgi:app",
|
||||
host=config.listen_host,
|
||||
|
||||
323
webui.txt
323
webui.txt
@ -47,3 +47,326 @@ 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
|
||||
|
||||
请求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
|
||||
最后完成工作流
|
||||
@ -20,7 +20,7 @@ def render_audio_panel(tr):
|
||||
def render_tts_settings(tr):
|
||||
"""渲染TTS(文本转语音)设置"""
|
||||
# 获取支持的语音列表
|
||||
support_locales = ["zh-CN", "zh-HK", "zh-TW", "en-US"]
|
||||
support_locales = ["zh-CN"]
|
||||
voices = voice.get_all_azure_voices(filter_locals=support_locales)
|
||||
|
||||
# 创建友好的显示名称
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user