mirror of
https://github.com/linyqh/NarratoAI.git
synced 2025-12-16 15:12:57 +00:00
完成了 narrato-api 生成视频脚本的逻辑
This commit is contained in:
parent
8267a0b3eb
commit
49b52041ce
@ -2,8 +2,24 @@
|
|||||||
project_version="0.3.0"
|
project_version="0.3.0"
|
||||||
# 支持视频理解的大模型提供商
|
# 支持视频理解的大模型提供商
|
||||||
# gemini
|
# gemini
|
||||||
|
# NarratoAPI
|
||||||
# qwen2-vl (待增加)
|
# qwen2-vl (待增加)
|
||||||
video_llm_provider="gemini"
|
vision_llm_provider="gemini"
|
||||||
|
vision_batch_size = 5
|
||||||
|
vision_analysis_prompt = "你是资深视频内容分析专家,擅长分析视频画面信息,分析下面视频画面内容,只输出客观的画面描述不要给任何总结或评价"
|
||||||
|
|
||||||
|
########## Vision Gemini API Key
|
||||||
|
vision_gemini_api_key = ""
|
||||||
|
vision_gemini_model_name = "gemini-1.5-flash"
|
||||||
|
|
||||||
|
########### Vision NarratoAPI Key
|
||||||
|
# NarratoAPI 是为了便捷访问不了 Gemini API 的用户, 提供的代理服务
|
||||||
|
narrato_api_key = ""
|
||||||
|
narrato_api_url = ""
|
||||||
|
narrato_vision_model = "gemini-1.5-flash"
|
||||||
|
narrato_vision_key = ""
|
||||||
|
narrato_llm_model = "gpt-4o"
|
||||||
|
narrato_llm_key = ""
|
||||||
|
|
||||||
# 用于生成文案的大模型支持的提供商 (Supported providers):
|
# 用于生成文案的大模型支持的提供商 (Supported providers):
|
||||||
# openai (默认)
|
# openai (默认)
|
||||||
@ -13,63 +29,52 @@
|
|||||||
# azure
|
# azure
|
||||||
# qwen (通义千问)
|
# qwen (通义千问)
|
||||||
# gemini
|
# gemini
|
||||||
llm_provider="openai"
|
text_llm_provider="openai"
|
||||||
########## Ollama Settings
|
|
||||||
# No need to set it unless you want to use your own proxy
|
|
||||||
ollama_base_url = ""
|
|
||||||
# Check your available models at https://ollama.com/library
|
|
||||||
ollama_model_name = ""
|
|
||||||
|
|
||||||
########## OpenAI API Key
|
########## OpenAI API Key
|
||||||
# Get your API key at https://platform.openai.com/api-keys
|
# Get your API key at https://platform.openai.com/api-keys
|
||||||
openai_api_key = ""
|
text_openai_api_key = ""
|
||||||
# No need to set it unless you want to use your own proxy
|
# No need to set it unless you want to use your own proxy
|
||||||
openai_base_url = ""
|
text_openai_base_url = ""
|
||||||
# Check your available models at https://platform.openai.com/account/limits
|
# Check your available models at https://platform.openai.com/account/limits
|
||||||
openai_model_name = "gpt-4o"
|
text_openai_model_name = "gpt-4o-mini"
|
||||||
|
|
||||||
########## Moonshot API Key
|
########## Moonshot API Key
|
||||||
# Visit https://platform.moonshot.cn/console/api-keys to get your API key.
|
# Visit https://platform.moonshot.cn/console/api-keys to get your API key.
|
||||||
moonshot_api_key=""
|
text_moonshot_api_key=""
|
||||||
moonshot_base_url = "https://api.moonshot.cn/v1"
|
text_moonshot_base_url = "https://api.moonshot.cn/v1"
|
||||||
moonshot_model_name = "moonshot-v1-8k"
|
text_moonshot_model_name = "moonshot-v1-8k"
|
||||||
|
|
||||||
########## OneAPI API Key
|
|
||||||
# Visit https://github.com/songquanpeng/one-api to get your API key
|
|
||||||
oneapi_api_key=""
|
|
||||||
oneapi_base_url=""
|
|
||||||
oneapi_model_name=""
|
|
||||||
|
|
||||||
########## G4F
|
########## G4F
|
||||||
# Visit https://github.com/xtekky/gpt4free to get more details
|
# Visit https://github.com/xtekky/gpt4free to get more details
|
||||||
# Supported model list: https://github.com/xtekky/gpt4free/blob/main/g4f/models.py
|
# Supported model list: https://github.com/xtekky/gpt4free/blob/main/g4f/models.py
|
||||||
g4f_model_name = "gpt-3.5-turbo"
|
text_g4f_model_name = "gpt-3.5-turbo"
|
||||||
|
|
||||||
########## Azure API Key
|
########## Azure API Key
|
||||||
# Visit https://learn.microsoft.com/zh-cn/azure/ai-services/openai/ to get more details
|
# Visit https://learn.microsoft.com/zh-cn/azure/ai-services/openai/ to get more details
|
||||||
# API documentation: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference
|
# API documentation: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference
|
||||||
azure_api_key = ""
|
text_azure_api_key = ""
|
||||||
azure_base_url=""
|
text_azure_base_url=""
|
||||||
azure_model_name="gpt-35-turbo" # replace with your model deployment name
|
text_azure_model_name="gpt-35-turbo" # replace with your model deployment name
|
||||||
azure_api_version = "2024-02-15-preview"
|
text_azure_api_version = "2024-02-15-preview"
|
||||||
|
|
||||||
########## Gemini API Key
|
########## Gemini API Key
|
||||||
gemini_api_key=""
|
text_gemini_api_key=""
|
||||||
gemini_model_name = "gemini-1.5-pro"
|
text_gemini_model_name = "gemini-1.5-flash"
|
||||||
|
|
||||||
########## Qwen API Key
|
########## Qwen API Key
|
||||||
# Visit https://dashscope.console.aliyun.com/apiKey to get your API key
|
# Visit https://dashscope.console.aliyun.com/apiKey to get your API key
|
||||||
# Visit below links to get more details
|
# Visit below links to get more details
|
||||||
# https://tongyi.aliyun.com/qianwen/
|
# https://tongyi.aliyun.com/qianwen/
|
||||||
# https://help.aliyun.com/zh/dashscope/developer-reference/model-introduction
|
# https://help.aliyun.com/zh/dashscope/developer-reference/model-introduction
|
||||||
qwen_api_key = ""
|
text_qwen_api_key = ""
|
||||||
qwen_model_name = "qwen-max"
|
text_qwen_model_name = "qwen-max"
|
||||||
|
|
||||||
########## DeepSeek API Key
|
########## DeepSeek API Key
|
||||||
# Visit https://platform.deepseek.com/api_keys to get your API key
|
# Visit https://platform.deepseek.com/api_keys to get your API key
|
||||||
deepseek_api_key = ""
|
text_deepseek_api_key = ""
|
||||||
deepseek_base_url = "https://api.deepseek.com"
|
text_deepseek_base_url = "https://api.deepseek.com"
|
||||||
deepseek_model_name = "deepseek-chat"
|
text_deepseek_model_name = "deepseek-chat"
|
||||||
|
|
||||||
# 字幕提供商、可选,支持 whisper 和 faster-whisper-large-v2"whisper"
|
# 字幕提供商、可选,支持 whisper 和 faster-whisper-large-v2"whisper"
|
||||||
# 默认为 faster-whisper-large-v2 模型地址:https://huggingface.co/guillaumekln/faster-whisper-large-v2
|
# 默认为 faster-whisper-large-v2 模型地址:https://huggingface.co/guillaumekln/faster-whisper-large-v2
|
||||||
|
|||||||
@ -283,9 +283,10 @@ def generate_script(tr, params):
|
|||||||
raise Exception(f"关键帧提取失败: {str(e)}")
|
raise Exception(f"关键帧提取失败: {str(e)}")
|
||||||
|
|
||||||
# 根据不同的 LLM 提供商处理
|
# 根据不同的 LLM 提供商处理
|
||||||
video_llm_provider = st.session_state.get('video_llm_providers', 'Gemini').lower()
|
vision_llm_provider = st.session_state.get('vision_llm_providers').lower()
|
||||||
|
logger.debug(f"Vision LLM 提供商: {vision_llm_provider}")
|
||||||
|
|
||||||
if video_llm_provider == 'gemini':
|
if vision_llm_provider == 'gemini':
|
||||||
try:
|
try:
|
||||||
# ===================初始化视觉分析器===================
|
# ===================初始化视觉分析器===================
|
||||||
update_progress(30, "正在初始化视觉分析器...")
|
update_progress(30, "正在初始化视觉分析器...")
|
||||||
@ -443,7 +444,7 @@ def generate_script(tr, params):
|
|||||||
logger.exception(f"Gemini 处理过程中发生错误\n{traceback.format_exc()}")
|
logger.exception(f"Gemini 处理过程中发生错误\n{traceback.format_exc()}")
|
||||||
raise Exception(f"视觉分析失败: {str(e)}")
|
raise Exception(f"视觉分析失败: {str(e)}")
|
||||||
|
|
||||||
else: # NarratoAPI
|
elif vision_llm_provider == 'narratoapi': # NarratoAPI
|
||||||
try:
|
try:
|
||||||
# 创建临时目录
|
# 创建临时目录
|
||||||
temp_dir = utils.temp_dir("narrato")
|
temp_dir = utils.temp_dir("narrato")
|
||||||
@ -451,12 +452,11 @@ def generate_script(tr, params):
|
|||||||
# 打包关键帧
|
# 打包关键帧
|
||||||
update_progress(30, "正在打包关键帧...")
|
update_progress(30, "正在打包关键帧...")
|
||||||
zip_path = os.path.join(temp_dir, f"keyframes_{int(time.time())}.zip")
|
zip_path = os.path.join(temp_dir, f"keyframes_{int(time.time())}.zip")
|
||||||
|
|
||||||
if not file_utils.create_zip(keyframe_files, zip_path):
|
if not file_utils.create_zip(keyframe_files, zip_path):
|
||||||
raise Exception("打包关键帧失败")
|
raise Exception("打包关键帧失败")
|
||||||
|
|
||||||
# 获取API配置
|
# 获取API配置
|
||||||
api_url = st.session_state.get('narrato_api_url', 'http://127.0.0.1:8000/api/v1/video/analyze')
|
api_url = st.session_state.get('narrato_api_url')
|
||||||
api_key = st.session_state.get('narrato_api_key')
|
api_key = st.session_state.get('narrato_api_key')
|
||||||
|
|
||||||
if not api_key:
|
if not api_key:
|
||||||
@ -480,12 +480,13 @@ def generate_script(tr, params):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 发送API请求
|
# 发送API请求
|
||||||
|
logger.info(f"请求 NarratoAPI:{api_url}")
|
||||||
update_progress(40, "正在上传文件...")
|
update_progress(40, "正在上传文件...")
|
||||||
with open(zip_path, 'rb') as f:
|
with open(zip_path, 'rb') as f:
|
||||||
files = {'file': (os.path.basename(zip_path), f, 'application/x-zip-compressed')}
|
files = {'file': (os.path.basename(zip_path), f, 'application/x-zip-compressed')}
|
||||||
try:
|
try:
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
api_url,
|
f"{api_url}/video/analyze",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
params=api_params,
|
params=api_params,
|
||||||
files=files,
|
files=files,
|
||||||
@ -493,10 +494,11 @@ def generate_script(tr, params):
|
|||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
|
logger.error(f"Narrato API 请求失败:\n{traceback.format_exc()}")
|
||||||
raise Exception(f"API请求失败: {str(e)}")
|
raise Exception(f"API请求失败: {str(e)}")
|
||||||
|
|
||||||
task_data = response.json()
|
task_data = response.json()
|
||||||
task_id = task_data.get('task_id')
|
task_id = task_data["data"].get('task_id')
|
||||||
if not task_id:
|
if not task_id:
|
||||||
raise Exception(f"无效的API响应: {response.text}")
|
raise Exception(f"无效的API响应: {response.text}")
|
||||||
|
|
||||||
@ -508,7 +510,7 @@ def generate_script(tr, params):
|
|||||||
while retry_count < max_retries:
|
while retry_count < max_retries:
|
||||||
try:
|
try:
|
||||||
status_response = requests.get(
|
status_response = requests.get(
|
||||||
f"{api_url}/tasks/{task_id}",
|
f"{api_url}/video/tasks/{task_id}",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
timeout=10
|
timeout=10
|
||||||
)
|
)
|
||||||
@ -516,14 +518,12 @@ def generate_script(tr, params):
|
|||||||
task_status = status_response.json()['data']
|
task_status = status_response.json()['data']
|
||||||
|
|
||||||
if task_status['status'] == 'SUCCESS':
|
if task_status['status'] == 'SUCCESS':
|
||||||
script = task_status['result']
|
script = task_status['result']['data']
|
||||||
break
|
break
|
||||||
elif task_status['status'] in ['FAILURE', 'RETRY']:
|
elif task_status['status'] in ['FAILURE', 'RETRY']:
|
||||||
raise Exception(f"任务失败: {task_status.get('error')}")
|
raise Exception(f"任务失败: {task_status.get('error')}")
|
||||||
|
|
||||||
retry_count += 1
|
retry_count += 1
|
||||||
progress = min(70, 50 + (retry_count * 20 / max_retries))
|
|
||||||
update_progress(progress, "正在分析中...")
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
@ -536,7 +536,7 @@ def generate_script(tr, params):
|
|||||||
raise Exception("任务执行超时")
|
raise Exception("任务执行超时")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("NarratoAPI 处理过程中发生错误")
|
logger.exception(f"NarratoAPI 处理过程中发生错误\n{traceback.format_exc()}")
|
||||||
raise Exception(f"NarratoAPI 处理失败: {str(e)}")
|
raise Exception(f"NarratoAPI 处理失败: {str(e)}")
|
||||||
finally:
|
finally:
|
||||||
# 清理临时文件
|
# 清理临时文件
|
||||||
@ -546,12 +546,14 @@ def generate_script(tr, params):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"清理临时文件失败: {str(e)}")
|
logger.warning(f"清理临时文件失败: {str(e)}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.exception("Vision Model 未启用,请检查配置")
|
||||||
|
|
||||||
if script is None:
|
if script is None:
|
||||||
st.error("生成脚本失败,请检查日志")
|
st.error("生成脚本失败,请检查日志")
|
||||||
st.stop()
|
st.stop()
|
||||||
|
logger.info(f"脚本生成完成\n{script} \n{type(script)}")
|
||||||
script = utils.clean_model_output(script)
|
st.session_state['video_clip_json'] = script
|
||||||
st.session_state['video_clip_json'] = json.loads(script)
|
|
||||||
update_progress(90, "脚本生成完成")
|
update_progress(90, "脚本生成完成")
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
@ -561,7 +563,7 @@ def generate_script(tr, params):
|
|||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
st.error(f"生成过程中发生错误: {str(err)}")
|
st.error(f"生成过程中发生错误: {str(err)}")
|
||||||
logger.exception("生成脚本时发生错误")
|
logger.exception(f"生成脚本时发生错误\n{traceback.format_exc()}")
|
||||||
finally:
|
finally:
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
progress_bar.empty()
|
progress_bar.empty()
|
||||||
|
|||||||
@ -188,13 +188,14 @@ def ensure_directory(directory):
|
|||||||
logger.error(f"创建目录失败: {directory}, 错误: {e}")
|
logger.error(f"创建目录失败: {directory}, 错误: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def create_zip(files: list, zip_path: str, base_dir: str = None) -> bool:
|
def create_zip(files: list, zip_path: str, base_dir: str = None, folder_name: str = "demo") -> bool:
|
||||||
"""
|
"""
|
||||||
创建zip文件
|
创建zip文件
|
||||||
Args:
|
Args:
|
||||||
files: 要打包的文件列表
|
files: 要打包的文件列表
|
||||||
zip_path: zip文件保存路径
|
zip_path: zip文件保存路径
|
||||||
base_dir: 基础目录,用于保持目录结构
|
base_dir: 基础目录,用于保持目录结构
|
||||||
|
folder_name: zip解压后的文件夹名称,默认为frames
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否成功
|
bool: 是否成功
|
||||||
"""
|
"""
|
||||||
@ -210,19 +211,18 @@ def create_zip(files: list, zip_path: str, base_dir: str = None) -> bool:
|
|||||||
logger.warning(f"文件不存在,跳过: {file}")
|
logger.warning(f"文件不存在,跳过: {file}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 计算文件在zip中的路径
|
# 计算文件在zip中的路径,添加folder_name作为前缀目录
|
||||||
if base_dir:
|
if base_dir:
|
||||||
arcname = os.path.relpath(file, base_dir)
|
arcname = os.path.join(folder_name, os.path.relpath(file, base_dir))
|
||||||
else:
|
else:
|
||||||
arcname = os.path.basename(file)
|
arcname = os.path.join(folder_name, os.path.basename(file))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zipf.write(file, arcname)
|
zipf.write(file, arcname)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"添加文件到zip失败: {file}, 错误: {e}")
|
logger.error(f"添加文件到zip失败: {file}, 错误: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"创建zip文件成功: {zip_path}")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user