mirror of
https://github.com/linyqh/NarratoAI.git
synced 2026-07-29 17:35:53 +00:00
feat(webui): 新增短剧剧情分析、可视化脚本编辑器与通用生成参数设置
- 抽离通用生成参数设置组件,统一管理temperature等LLM生成参数 - 新增短剧字幕剧情分析功能,支持一键分析与手动编辑分析结果 - 重构短剧脚本生成逻辑,支持传入预先生成的剧情分析内容 - 新增可视化视频脚本表格编辑器,支持增删编辑行与原始JSON预览 - 优化多语言翻译、UI交互细节与字幕相关提示文案
This commit is contained in:
parent
e744960ac1
commit
e6d15fe246
@ -167,6 +167,17 @@ def render_basic_settings(tr):
|
||||
with right_config_panel:
|
||||
render_text_llm_settings(tr) # 文案生成模型设置
|
||||
|
||||
render_generation_settings(tr)
|
||||
|
||||
|
||||
def render_generation_settings(tr):
|
||||
"""渲染通用生成参数。"""
|
||||
st.divider()
|
||||
st.subheader(tr("Generation Settings"))
|
||||
if 'temperature' not in st.session_state:
|
||||
st.session_state['temperature'] = 0.7
|
||||
st.slider("temperature", 0.0, 2.0, key="temperature")
|
||||
|
||||
|
||||
def render_language_settings(tr):
|
||||
st.subheader(tr("Proxy Settings"))
|
||||
|
||||
@ -1,18 +1,23 @@
|
||||
import os
|
||||
import glob
|
||||
import json
|
||||
import math
|
||||
import time
|
||||
import traceback
|
||||
import pandas as pd
|
||||
import streamlit as st
|
||||
from loguru import logger
|
||||
|
||||
from app.config import config
|
||||
from app.models.schema import VideoClipParams
|
||||
from app.services.subtitle_text import decode_subtitle_bytes
|
||||
from app.services.subtitle_text import decode_subtitle_bytes, read_subtitle_text
|
||||
from app.utils import utils, check_script
|
||||
from webui.tools.generate_script_docu import generate_script_docu
|
||||
from webui.tools.generate_script_short import generate_script_short
|
||||
from webui.tools.generate_short_summary import generate_script_short_sunmmary
|
||||
from webui.tools.generate_short_summary import analyze_short_drama_plot, generate_script_short_sunmmary
|
||||
|
||||
|
||||
SCRIPT_TABLE_BASE_COLUMNS = ["_id", "timestamp", "picture", "narration", "OST"]
|
||||
|
||||
|
||||
def render_script_panel(tr):
|
||||
@ -77,8 +82,10 @@ def render_script_file(tr, params):
|
||||
default_index = mode_keys.index(tr("Short Generate"))
|
||||
elif current_path == "summary":
|
||||
default_index = mode_keys.index(tr("Short Drama Summary"))
|
||||
else:
|
||||
elif current_path:
|
||||
default_index = mode_keys.index(tr("Select/Upload Script"))
|
||||
else:
|
||||
default_index = 0
|
||||
|
||||
# 1. 渲染功能选择组件
|
||||
default_mode_label = mode_keys[default_index]
|
||||
@ -230,16 +237,21 @@ def render_script_file(tr, params):
|
||||
def render_video_file(tr, params):
|
||||
"""渲染视频文件选择"""
|
||||
source_options = {
|
||||
tr("Select from resource directory"): "resource",
|
||||
tr("Upload Local Files"): "upload",
|
||||
tr("Select from resource directory"): "resource",
|
||||
}
|
||||
source_labels = list(source_options.keys())
|
||||
default_source_label = source_labels[0]
|
||||
source_default_version = "upload_first_v1"
|
||||
|
||||
if (
|
||||
'video_source_selection' not in st.session_state
|
||||
or st.session_state['video_source_selection'] not in source_options
|
||||
):
|
||||
if st.session_state.get('_video_source_default_version') != source_default_version:
|
||||
if (
|
||||
st.session_state.get('video_source_selection') not in source_options
|
||||
or not st.session_state.get('video_origin_path')
|
||||
):
|
||||
st.session_state['video_source_selection'] = default_source_label
|
||||
st.session_state['_video_source_default_version'] = source_default_version
|
||||
elif st.session_state.get('video_source_selection') not in source_options:
|
||||
st.session_state['video_source_selection'] = default_source_label
|
||||
|
||||
current_source = st.session_state['video_source_selection']
|
||||
@ -250,12 +262,13 @@ def render_video_file(tr, params):
|
||||
)
|
||||
st.markdown(f"**{tr('Video Source')}** :gray[{source_caption}]")
|
||||
|
||||
source = st.selectbox(
|
||||
source = st.pills(
|
||||
tr("Video Source"),
|
||||
options=source_labels,
|
||||
index=None,
|
||||
selection_mode="single",
|
||||
key="video_source_selection",
|
||||
label_visibility="collapsed",
|
||||
width="stretch",
|
||||
)
|
||||
if not source:
|
||||
source = default_source_label
|
||||
@ -399,23 +412,84 @@ def short_drama_summary(tr):
|
||||
st.session_state['subtitle_file_processed'] = False
|
||||
|
||||
render_fun_asr_transcription(tr)
|
||||
render_subtitle_preview(tr)
|
||||
|
||||
# 名称输入框
|
||||
video_theme = st.text_input(tr("短剧名称"))
|
||||
current_subtitle_path = st.session_state.get('subtitle_path', '')
|
||||
plot_analysis_source = st.session_state.get('short_drama_plot_analysis_subtitle_path')
|
||||
if plot_analysis_source and plot_analysis_source != current_subtitle_path:
|
||||
st.session_state['short_drama_plot_analysis'] = ""
|
||||
st.session_state['short_drama_plot_analysis_subtitle_path'] = ""
|
||||
|
||||
name_cols = st.columns([4, 1.2], vertical_alignment="bottom")
|
||||
with name_cols[0]:
|
||||
video_theme = st.text_input(tr("短剧名称"))
|
||||
with name_cols[1]:
|
||||
analyze_plot_clicked = st.button(
|
||||
tr("剧情理解"),
|
||||
key="short_drama_plot_analysis_button",
|
||||
disabled=not current_subtitle_path,
|
||||
use_container_width=True,
|
||||
)
|
||||
st.session_state['video_theme'] = video_theme
|
||||
# 数字输入框
|
||||
temperature = st.slider("temperature", 0.0, 2.0, 0.7)
|
||||
st.session_state['temperature'] = temperature
|
||||
|
||||
if analyze_plot_clicked:
|
||||
with st.spinner(tr("Analyzing plot...")):
|
||||
plot_analysis = analyze_short_drama_plot(
|
||||
current_subtitle_path,
|
||||
st.session_state.get('temperature', 0.7),
|
||||
tr,
|
||||
subtitle_content=st.session_state.get('subtitle_content', ''),
|
||||
)
|
||||
if plot_analysis:
|
||||
st.session_state['short_drama_plot_analysis'] = plot_analysis
|
||||
st.session_state['short_drama_plot_analysis_subtitle_path'] = current_subtitle_path
|
||||
st.success(tr("Plot analysis completed"))
|
||||
|
||||
if st.session_state.get('short_drama_plot_analysis'):
|
||||
st.text_area(
|
||||
tr("剧情理解结果"),
|
||||
key="short_drama_plot_analysis",
|
||||
height=240,
|
||||
)
|
||||
|
||||
return video_theme
|
||||
|
||||
|
||||
def render_subtitle_preview(tr):
|
||||
"""渲染可折叠的当前字幕预览;没有字幕时提示用户先转写或上传。"""
|
||||
subtitle_path = st.session_state.get('subtitle_path', '')
|
||||
subtitle_content = st.session_state.get('subtitle_content', '')
|
||||
|
||||
if subtitle_path and not subtitle_content and os.path.exists(subtitle_path):
|
||||
subtitle_content = read_subtitle_text(subtitle_path).text
|
||||
st.session_state['subtitle_content'] = subtitle_content
|
||||
|
||||
with st.expander(tr("Subtitle Preview"), expanded=False):
|
||||
if not subtitle_path or not subtitle_content:
|
||||
st.info(tr("Please transcribe or upload subtitles first"))
|
||||
return
|
||||
|
||||
st.text_area(
|
||||
tr("Subtitle Preview"),
|
||||
key="subtitle_content",
|
||||
height=180,
|
||||
label_visibility="collapsed",
|
||||
)
|
||||
|
||||
|
||||
def render_subtitle_upload(tr):
|
||||
"""上传并保存用户提供的 SRT 字幕文件。"""
|
||||
subtitle_dir_label = utils.subtitle_dir().replace(config.root_dir, ".")
|
||||
st.markdown(
|
||||
f"**{tr('上传字幕文件')}** "
|
||||
f":gray[{tr('Transcribed subtitles storage hint').format(path=subtitle_dir_label)}]"
|
||||
)
|
||||
subtitle_file = st.file_uploader(
|
||||
tr("上传字幕文件"),
|
||||
type=["srt"],
|
||||
accept_multiple_files=False,
|
||||
key="subtitle_file_uploader" # 添加唯一key
|
||||
key="subtitle_file_uploader", # 添加唯一key
|
||||
label_visibility="collapsed",
|
||||
)
|
||||
|
||||
# 显示当前已上传的字幕文件路径
|
||||
@ -476,6 +550,141 @@ def render_subtitle_upload(tr):
|
||||
st.error(f"{tr('Upload failed')}: {str(e)}")
|
||||
|
||||
|
||||
def _is_blank_table_value(value):
|
||||
if value is None:
|
||||
return True
|
||||
if isinstance(value, float) and math.isnan(value):
|
||||
return True
|
||||
if isinstance(value, str) and not value.strip():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _ordered_script_columns(script_rows):
|
||||
columns = []
|
||||
for column in SCRIPT_TABLE_BASE_COLUMNS:
|
||||
columns.append(column)
|
||||
|
||||
for row in script_rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
for column in row.keys():
|
||||
if column not in columns:
|
||||
columns.append(column)
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def _script_json_to_table(script_data):
|
||||
if not isinstance(script_data, list):
|
||||
script_data = []
|
||||
|
||||
if not script_data:
|
||||
return pd.DataFrame(columns=SCRIPT_TABLE_BASE_COLUMNS)
|
||||
|
||||
if not all(isinstance(item, dict) for item in script_data):
|
||||
rows = [
|
||||
{"value": json.dumps(item, ensure_ascii=False)}
|
||||
for item in script_data
|
||||
]
|
||||
return pd.DataFrame(rows, columns=["value"])
|
||||
|
||||
columns = _ordered_script_columns(script_data)
|
||||
return pd.DataFrame(script_data, columns=columns)
|
||||
|
||||
|
||||
def _normalize_script_table_value(column, value):
|
||||
if _is_blank_table_value(value):
|
||||
return ""
|
||||
|
||||
if column in {"_id", "OST"}:
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return value
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _script_table_to_json(edited_data):
|
||||
if isinstance(edited_data, pd.DataFrame):
|
||||
records = edited_data.to_dict("records")
|
||||
elif isinstance(edited_data, list):
|
||||
records = edited_data
|
||||
else:
|
||||
records = pd.DataFrame(edited_data).to_dict("records")
|
||||
|
||||
script_data = []
|
||||
for row in records:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
if all(_is_blank_table_value(value) for value in row.values()):
|
||||
continue
|
||||
|
||||
cleaned_row = {}
|
||||
for column, value in row.items():
|
||||
if not column:
|
||||
continue
|
||||
normalized_value = _normalize_script_table_value(column, value)
|
||||
if _is_blank_table_value(normalized_value) and column not in SCRIPT_TABLE_BASE_COLUMNS:
|
||||
continue
|
||||
cleaned_row[column] = normalized_value
|
||||
|
||||
if cleaned_row:
|
||||
script_data.append(cleaned_row)
|
||||
|
||||
return json.dumps(script_data, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
def render_video_script_editor(tr):
|
||||
"""使用弹窗和表格编辑视频脚本 JSON。"""
|
||||
@st.dialog(tr("Video Script"), width="large")
|
||||
def video_script_dialog():
|
||||
script_data = st.session_state.get('video_clip_json', [])
|
||||
table_data = _script_json_to_table(script_data)
|
||||
column_order = list(table_data.columns)
|
||||
|
||||
st.caption(tr("Video script table help"))
|
||||
edited_table = st.data_editor(
|
||||
table_data,
|
||||
key="video_script_table_editor",
|
||||
hide_index=True,
|
||||
num_rows="dynamic",
|
||||
use_container_width=True,
|
||||
height=520,
|
||||
row_height=72,
|
||||
column_order=column_order,
|
||||
column_config={
|
||||
"_id": st.column_config.NumberColumn(tr("Script Column ID"), step=1, format="%d", width=52),
|
||||
"timestamp": st.column_config.TextColumn(tr("Script Column Timestamp"), width=200),
|
||||
"picture": st.column_config.TextColumn(tr("Script Column Picture"), width=320),
|
||||
"narration": st.column_config.TextColumn(tr("Script Column Narration"), width=480),
|
||||
"OST": st.column_config.NumberColumn(
|
||||
tr("Script Column OST"),
|
||||
min_value=0,
|
||||
max_value=2,
|
||||
step=1,
|
||||
format="%d",
|
||||
width=52,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
video_clip_json_details = _script_table_to_json(edited_table)
|
||||
with st.expander(tr("Raw JSON Preview"), expanded=False):
|
||||
st.code(video_clip_json_details, language="json")
|
||||
|
||||
if st.button(tr("Save Script"), key="save_script_from_dialog", use_container_width=True):
|
||||
save_script_with_validation(tr, video_clip_json_details)
|
||||
|
||||
script_data = st.session_state.get('video_clip_json', [])
|
||||
script_count = len(script_data) if isinstance(script_data, list) else 0
|
||||
st.markdown(f"**{tr('Video Script')}** :gray[{tr('Video script row count').format(count=script_count)}]")
|
||||
|
||||
if st.button(tr("Edit Video Script"), key="open_video_script_editor", use_container_width=True):
|
||||
video_script_dialog()
|
||||
|
||||
|
||||
def render_fun_asr_transcription(tr):
|
||||
"""使用 Fun-ASR 从本地音视频转写生成字幕。"""
|
||||
def clear_fun_asr_subtitle_state():
|
||||
@ -681,20 +890,22 @@ def render_script_buttons(tr, params):
|
||||
subtitle_path = st.session_state.get('subtitle_path')
|
||||
video_theme = st.session_state.get('video_theme')
|
||||
temperature = st.session_state.get('temperature')
|
||||
generate_script_short_sunmmary(params, subtitle_path, video_theme, temperature, tr)
|
||||
plot_analysis = ""
|
||||
if st.session_state.get('short_drama_plot_analysis_subtitle_path') == subtitle_path:
|
||||
plot_analysis = st.session_state.get('short_drama_plot_analysis', '')
|
||||
generate_script_short_sunmmary(
|
||||
params,
|
||||
subtitle_path,
|
||||
video_theme,
|
||||
temperature,
|
||||
tr,
|
||||
plot_analysis=plot_analysis,
|
||||
subtitle_content=st.session_state.get('subtitle_content', ''),
|
||||
)
|
||||
else:
|
||||
load_script(tr, script_path)
|
||||
|
||||
# 视频脚本编辑区
|
||||
video_clip_json_details = st.text_area(
|
||||
tr("Video Script"),
|
||||
value=json.dumps(st.session_state.get('video_clip_json', []), indent=2, ensure_ascii=False),
|
||||
height=500
|
||||
)
|
||||
|
||||
# 操作按钮行 - 合并格式检查和保存功能
|
||||
if st.button(tr("Save Script"), key="save_script", use_container_width=True):
|
||||
save_script_with_validation(tr, video_clip_json_details)
|
||||
render_video_script_editor(tr)
|
||||
|
||||
|
||||
def load_script(tr, script_path):
|
||||
|
||||
@ -9,7 +9,17 @@
|
||||
"Generate Video Script and Keywords": "Click to use AI to generate **Video Script** and **Video Keywords** based on the **subject**",
|
||||
"Auto Detect": "Auto Detect",
|
||||
"Auto Generate": "Frame Analysis",
|
||||
"Video Script": "Video Script (:blue[①Optional, use AI to generate ②Proper punctuation helps in generating subtitles])",
|
||||
"Video Script": "Video Script",
|
||||
"Edit Video Script": "View/Edit Video Script",
|
||||
"Video script row count": "{count} script rows",
|
||||
"Video script table help": "Edit the full script JSON as a table. You can add or delete rows; saving will validate and write the script file again.",
|
||||
"Raw JSON Preview": "Raw JSON Preview",
|
||||
"Script Column ID": "ID",
|
||||
"Script Column Timestamp": "Timestamp",
|
||||
"Script Column Picture": "Picture",
|
||||
"Script Column Narration": "Narration",
|
||||
"Script Column OST": "Mark",
|
||||
"Generation Settings": "Generation Settings",
|
||||
"Save Script": "Save Script",
|
||||
"Crop Video": "Crop Video",
|
||||
"Video File": "Video File",
|
||||
@ -324,6 +334,13 @@
|
||||
"Transcribing with Fun-ASR...": "Transcribing subtitles with Ali Bailian Fun-ASR, please wait...",
|
||||
"Fun-ASR failed without subtitle file": "Fun-ASR transcription failed: no subtitle file was generated",
|
||||
"Subtitle transcription succeeded": "Subtitle transcription succeeded: {file}",
|
||||
"Transcribed subtitles storage hint": "Previously transcribed subtitles are saved in {path}; drag a file from that folder to upload",
|
||||
"剧情理解": "Plot Analysis",
|
||||
"剧情理解结果": "Plot Analysis Result",
|
||||
"Analyzing plot...": "Analyzing plot...",
|
||||
"Plot analysis completed": "Plot analysis completed",
|
||||
"Please generate or upload subtitles first": "Please transcribe or upload subtitles first",
|
||||
"Please transcribe or upload subtitles first": "Please transcribe or upload subtitles first",
|
||||
"Fun-ASR transcription failed": "Fun-ASR transcription failed",
|
||||
"Validating script format...": "Validating script format...",
|
||||
"Script format validation failed": "Script format validation failed",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
"Auto Detect": "自动检测",
|
||||
"Video Theme": "视频主题",
|
||||
"Generation Prompt": "自定义提示词",
|
||||
"Generation Settings": "生成参数",
|
||||
"Save Script": "保存脚本",
|
||||
"Video File": "视频文件",
|
||||
"Plot Description": "剧情描述 (:blue[可从 https://www.tvmao.com/ 获取])",
|
||||
@ -104,6 +105,15 @@
|
||||
"Failed to Save Script": "保存脚本失败",
|
||||
"Script saved successfully": "脚本保存成功",
|
||||
"Video Script": "视频脚本",
|
||||
"Edit Video Script": "查看/编辑视频脚本",
|
||||
"Video script row count": "共 {count} 条脚本",
|
||||
"Video script table help": "在表格中编辑完整脚本 JSON。可新增、删除行;保存时会重新校验并写入脚本文件。",
|
||||
"Raw JSON Preview": "原始 JSON 预览",
|
||||
"Script Column ID": "序号",
|
||||
"Script Column Timestamp": "时间戳",
|
||||
"Script Column Picture": "画面描述",
|
||||
"Script Column Narration": "解说台词",
|
||||
"Script Column OST": "标记",
|
||||
"Video Quality": "视频质量",
|
||||
"Custom prompt for LLM, leave empty to use default prompt": "自定义提示词,留空则使用默认提示词",
|
||||
"Proxy Settings": "代理设置",
|
||||
@ -306,6 +316,13 @@
|
||||
"Transcribing with Fun-ASR...": "正在使用阿里百炼 Fun-ASR 转写字幕,请稍候...",
|
||||
"Fun-ASR failed without subtitle file": "Fun-ASR 转写失败:未生成字幕文件",
|
||||
"Subtitle transcription succeeded": "字幕转写成功: {file}",
|
||||
"Transcribed subtitles storage hint": "之前转录生成的字幕保存在 {path},可从该目录拖入上传",
|
||||
"剧情理解": "剧情理解",
|
||||
"剧情理解结果": "剧情理解结果",
|
||||
"Analyzing plot...": "正在理解剧情...",
|
||||
"Plot analysis completed": "剧情理解完成",
|
||||
"Please generate or upload subtitles first": "请先转写或上传字幕",
|
||||
"Please transcribe or upload subtitles first": "请先转写或上传字幕",
|
||||
"Fun-ASR transcription failed": "Fun-ASR 字幕转写失败",
|
||||
"Validating script format...": "正在验证脚本格式...",
|
||||
"Script format validation failed": "脚本格式验证失败",
|
||||
|
||||
@ -135,7 +135,58 @@ def parse_and_fix_json(json_string):
|
||||
return None
|
||||
|
||||
|
||||
def generate_script_short_sunmmary(params, subtitle_path, video_theme, temperature, tr=lambda key: key):
|
||||
def analyze_short_drama_plot(subtitle_path, temperature, tr=lambda key: key, subtitle_content=None):
|
||||
"""仅执行短剧字幕剧情理解,返回可编辑的剧情分析文本。"""
|
||||
if not subtitle_path:
|
||||
st.error(tr("Please generate or upload subtitles first"))
|
||||
return None
|
||||
if not os.path.exists(subtitle_path):
|
||||
st.error(tr("Subtitle file does not exist"))
|
||||
return None
|
||||
|
||||
text_provider = config.app.get('text_llm_provider', 'gemini').lower()
|
||||
text_api_key = config.app.get(f'text_{text_provider}_api_key')
|
||||
text_model = config.app.get(f'text_{text_provider}_model_name')
|
||||
text_base_url = config.app.get(f'text_{text_provider}_base_url')
|
||||
|
||||
subtitle_content = str(subtitle_content or "").strip() or read_subtitle_text(subtitle_path).text
|
||||
if not subtitle_content:
|
||||
st.error(tr("Subtitle file is empty or unreadable"))
|
||||
return None
|
||||
|
||||
try:
|
||||
logger.info("使用新的LLM服务架构进行字幕分析")
|
||||
analyzer = SubtitleAnalyzerAdapter(text_api_key, text_model, text_base_url, text_provider)
|
||||
analysis_result = analyzer.analyze_subtitle(subtitle_content)
|
||||
except Exception as e:
|
||||
logger.warning(f"使用新LLM服务失败,回退到旧实现: {str(e)}")
|
||||
analysis_result = analyze_subtitle(
|
||||
subtitle_content=subtitle_content,
|
||||
api_key=text_api_key,
|
||||
model=text_model,
|
||||
base_url=text_base_url,
|
||||
save_result=True,
|
||||
temperature=temperature,
|
||||
provider=text_provider
|
||||
)
|
||||
|
||||
if analysis_result["status"] != "success":
|
||||
logger.error(f"分析失败: {analysis_result['message']}")
|
||||
st.error(tr("Script generation failed check logs"))
|
||||
return None
|
||||
|
||||
return analysis_result["analysis"]
|
||||
|
||||
|
||||
def generate_script_short_sunmmary(
|
||||
params,
|
||||
subtitle_path,
|
||||
video_theme,
|
||||
temperature,
|
||||
tr=lambda key: key,
|
||||
plot_analysis=None,
|
||||
subtitle_content=None,
|
||||
):
|
||||
"""
|
||||
生成 短剧解说 视频脚本
|
||||
要求: 提供高质量短剧字幕
|
||||
@ -174,30 +225,36 @@ def generate_script_short_sunmmary(params, subtitle_path, video_theme, temperatu
|
||||
text_base_url = config.app.get(f'text_{text_provider}_base_url')
|
||||
|
||||
# 读取字幕文件内容(无论使用哪种实现都需要)
|
||||
subtitle_content = read_subtitle_text(subtitle_path).text
|
||||
subtitle_content = str(subtitle_content or "").strip() or read_subtitle_text(subtitle_path).text
|
||||
if not subtitle_content:
|
||||
st.error(tr("Subtitle file is empty or unreadable"))
|
||||
return
|
||||
|
||||
try:
|
||||
# 优先使用新的LLM服务架构
|
||||
logger.info("使用新的LLM服务架构进行字幕分析")
|
||||
analyzer = SubtitleAnalyzerAdapter(text_api_key, text_model, text_base_url, text_provider)
|
||||
analyzer = SubtitleAnalyzerAdapter(text_api_key, text_model, text_base_url, text_provider)
|
||||
if plot_analysis and str(plot_analysis).strip():
|
||||
logger.info("使用用户编辑后的剧情理解结果生成解说文案")
|
||||
analysis_result = {
|
||||
"status": "success",
|
||||
"analysis": str(plot_analysis).strip(),
|
||||
}
|
||||
else:
|
||||
try:
|
||||
# 优先使用新的LLM服务架构
|
||||
logger.info("使用新的LLM服务架构进行字幕分析")
|
||||
analysis_result = analyzer.analyze_subtitle(subtitle_content)
|
||||
|
||||
analysis_result = analyzer.analyze_subtitle(subtitle_content)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"使用新LLM服务失败,回退到旧实现: {str(e)}")
|
||||
# 回退到旧的实现
|
||||
analysis_result = analyze_subtitle(
|
||||
subtitle_file_path=subtitle_path,
|
||||
api_key=text_api_key,
|
||||
model=text_model,
|
||||
base_url=text_base_url,
|
||||
save_result=True,
|
||||
temperature=temperature,
|
||||
provider=text_provider
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"使用新LLM服务失败,回退到旧实现: {str(e)}")
|
||||
# 回退到旧的实现
|
||||
analysis_result = analyze_subtitle(
|
||||
subtitle_content=subtitle_content,
|
||||
api_key=text_api_key,
|
||||
model=text_model,
|
||||
base_url=text_base_url,
|
||||
save_result=True,
|
||||
temperature=temperature,
|
||||
provider=text_provider
|
||||
)
|
||||
"""
|
||||
3. 根据剧情生成解说文案
|
||||
"""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user