mirror of
https://github.com/linyqh/NarratoAI.git
synced 2025-12-12 11:22:51 +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(
|
async def start_subclip(
|
||||||
request: VideoClipParams,
|
request: VideoClipParams,
|
||||||
|
task_id: str,
|
||||||
|
subclip_videos: dict,
|
||||||
background_tasks: BackgroundTasks
|
background_tasks: BackgroundTasks
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -153,13 +155,13 @@ async def start_subclip(
|
|||||||
# 在后台任务中执行视频剪辑
|
# 在后台任务中执行视频剪辑
|
||||||
background_tasks.add_task(
|
background_tasks.add_task(
|
||||||
task_service.start_subclip,
|
task_service.start_subclip,
|
||||||
task_id=request.task_id,
|
task_id=task_id,
|
||||||
params=params,
|
params=params,
|
||||||
subclip_path_videos=request.subclip_videos
|
subclip_path_videos=subclip_videos
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"task_id": request.task_id,
|
"task_id": task_id,
|
||||||
"state": "PROCESSING" # 初始状态
|
"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 {}
|
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:
|
Args:
|
||||||
|
|||||||
@ -324,7 +324,7 @@ def start(task_id, params: VideoParams, stop_at: str = "video"):
|
|||||||
return kwargs
|
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
|
Name: zh-CN-XiaoxiaoMultilingualNeural-V2
|
||||||
Gender: Female
|
Gender: Female
|
||||||
|
|
||||||
|
Name: zh-CN-YunxiNeural-V2
|
||||||
|
Gender: Male
|
||||||
""".strip()
|
""".strip()
|
||||||
voices = []
|
voices = []
|
||||||
name = ""
|
name = ""
|
||||||
@ -1034,8 +1037,8 @@ def is_azure_v2_voice(voice_name: str):
|
|||||||
def tts(
|
def tts(
|
||||||
text: str, voice_name: str, voice_rate: float, voice_pitch: float, voice_file: str
|
text: str, voice_name: str, voice_rate: float, voice_pitch: float, voice_file: str
|
||||||
) -> [SubMaker, None]:
|
) -> [SubMaker, None]:
|
||||||
# if is_azure_v2_voice(voice_name):
|
if is_azure_v2_voice(voice_name):
|
||||||
# return azure_tts_v2(text, voice_name, voice_file)
|
return azure_tts_v2(text, voice_name, voice_file)
|
||||||
return azure_tts_v1(text, voice_name, voice_rate, voice_pitch, 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 uuid import uuid4
|
||||||
|
|
||||||
from app.utils import utils
|
from app.utils import utils
|
||||||
|
from app.services import video as VideoService
|
||||||
|
|
||||||
|
|
||||||
class YoutubeService:
|
class YoutubeService:
|
||||||
@ -61,6 +62,7 @@ class YoutubeService:
|
|||||||
Args:
|
Args:
|
||||||
url: YouTube视频URL
|
url: YouTube视频URL
|
||||||
resolution: 目标分辨率 ('2160p', '1440p', '1080p', '720p' etc.)
|
resolution: 目标分辨率 ('2160p', '1440p', '1080p', '720p' etc.)
|
||||||
|
注意:对于类似'1080p60'的输入会被处理为'1080p'
|
||||||
output_format: 输出视频格式
|
output_format: 输出视频格式
|
||||||
rename: 可选的重命名
|
rename: 可选的重命名
|
||||||
|
|
||||||
@ -71,23 +73,32 @@ class YoutubeService:
|
|||||||
task_id = str(uuid4())
|
task_id = str(uuid4())
|
||||||
self._validate_format(output_format)
|
self._validate_format(output_format)
|
||||||
|
|
||||||
|
# 标准化分辨率格式
|
||||||
|
base_resolution = resolution.split('p')[0] + 'p'
|
||||||
|
|
||||||
# 获取所有可用格式
|
# 获取所有可用格式
|
||||||
formats = self._get_video_formats(url)
|
formats = self._get_video_formats(url)
|
||||||
|
|
||||||
# 查找指定分辨率的最佳视频格式
|
# 查找指定分辨率的最佳视频格式
|
||||||
target_format = None
|
target_format = None
|
||||||
for fmt in formats:
|
for fmt in formats:
|
||||||
if fmt['resolution'] == resolution and fmt['vcodec'] != 'none':
|
fmt_resolution = fmt['resolution']
|
||||||
target_format = fmt
|
# 将格式的分辨率也标准化后进行比较
|
||||||
break
|
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:
|
if target_format is None:
|
||||||
|
# 收集可用分辨率时也进行标准化
|
||||||
available_resolutions = set(
|
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'
|
if fmt['resolution'] != 'N/A' and fmt['vcodec'] != 'none'
|
||||||
)
|
)
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"未找到 {resolution} 分辨率的视频。"
|
f"未找到 {base_resolution} 分辨率的视频。"
|
||||||
f"可用分辨率: {', '.join(sorted(available_resolutions))}"
|
f"可用分辨率: {', '.join(sorted(available_resolutions))}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ def format_duration(seconds: float) -> str:
|
|||||||
return f"{minutes:02d}:{remaining_seconds:02d}"
|
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 = video.subclip(start_seconds, end_seconds)
|
||||||
video.write_videofile("../../resource/videos/cut_video2.mp4")
|
video.write_videofile("../../resource/videos/cut_video3.mp4")
|
||||||
|
|
||||||
# 释放资源
|
# 释放资源
|
||||||
video.close()
|
video.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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
|
import uvicorn
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@ -7,6 +8,8 @@ if __name__ == "__main__":
|
|||||||
logger.info(
|
logger.info(
|
||||||
"start server, docs: http://127.0.0.1:" + str(config.listen_port) + "/docs"
|
"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(
|
uvicorn.run(
|
||||||
app="app.asgi:app",
|
app="app.asgi:app",
|
||||||
host=config.listen_host,
|
host=config.listen_host,
|
||||||
|
|||||||
323
webui.txt
323
webui.txt
@ -47,3 +47,326 @@ pause
|
|||||||
|
|
||||||
rem set HF_ENDPOINT=https://hf-mirror.com
|
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 --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):
|
def render_tts_settings(tr):
|
||||||
"""渲染TTS(文本转语音)设置"""
|
"""渲染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)
|
voices = voice.get_all_azure_voices(filter_locals=support_locales)
|
||||||
|
|
||||||
# 创建友好的显示名称
|
# 创建友好的显示名称
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user