+
+ {html.escape(tr('LLM stream window title'))}
+
+
{escaped_preview}
+
+ """,
+ unsafe_allow_html=True,
+ )
try:
with st.spinner(tr("Generating script...")):
@@ -414,9 +531,14 @@ def generate_script_short_sunmmary(
st.error(tr("Subtitle file is empty or unreadable"))
return
+ narration_copy = str(narration_copy or "").strip()
+ if not narration_copy:
+ st.error(tr("Please generate and review narration copy first"))
+ return
+
analyzer = SubtitleAnalyzerAdapter(text_api_key, text_model, text_base_url, text_provider)
if plot_analysis and str(plot_analysis).strip():
- logger.info("使用用户编辑后的剧情理解结果生成解说文案")
+ logger.info("使用用户编辑后的剧情理解结果匹配剪辑脚本")
analysis_result = {
"status": "success",
"analysis": str(plot_analysis).strip(),
@@ -424,7 +546,7 @@ def generate_script_short_sunmmary(
else:
plot_analysis_input = subtitle_content
if enable_web_search:
- update_progress(40, tr("Searching short drama with Tavily..."))
+ update_waiting(tr("Searching short drama with Tavily..."))
plot_analysis_input = _build_plot_analysis_input(
subtitle_content,
short_name=video_theme,
@@ -436,11 +558,13 @@ def generate_script_short_sunmmary(
try:
# 优先使用新的LLM服务架构
logger.info("使用新的LLM服务架构进行字幕分析")
+ update_waiting(tr("Analyzing subtitles with model..."))
analysis_result = analyzer.analyze_subtitle(plot_analysis_input)
except Exception as e:
logger.warning(f"使用新LLM服务失败,回退到旧实现: {str(e)}")
# 回退到旧的实现
+ update_waiting(tr("Analyzing subtitles with model..."))
analysis_result = analyze_subtitle(
subtitle_content=plot_analysis_input,
api_key=text_api_key,
@@ -451,42 +575,50 @@ def generate_script_short_sunmmary(
provider=text_provider
)
"""
- 3. 根据剧情生成解说文案
+ 3. 根据用户审核后的文案匹配画面与时间戳
"""
if analysis_result["status"] == "success":
logger.info("字幕分析成功!")
- update_progress(60, tr("Generating narration copy..."))
+ update_waiting()
- # 根据剧情生成解说文案 - 使用新的LLM服务架构
try:
- # 优先使用新的LLM服务架构
- logger.info("使用新的LLM服务架构生成解说文案")
- narration_result = analyzer.generate_narration_script(
+ logger.info("使用新的LLM服务架构将审核文案匹配到字幕画面")
+ update_waiting(tr("Matching narration copy to footage..."))
+ stream_text.info(tr("Waiting for model stream..."))
+ narration_result = analyzer.match_narration_copy_to_script(
short_name=video_theme,
plot_analysis=analysis_result["analysis"],
- subtitle_content=subtitle_content, # 传递原始字幕内容
- temperature=temperature
+ subtitle_content=subtitle_content,
+ narration_copy=narration_copy,
+ temperature=temperature,
+ narration_language=narration_language,
+ drama_genre=drama_genre,
+ original_sound_ratio=original_sound_ratio,
+ stream_callback=update_stream_window,
)
except Exception as e:
- logger.warning(f"使用新LLM服务失败,回退到旧实现: {str(e)}")
- # 回退到旧的实现
- narration_result = generate_narration_script(
+ logger.warning(f"使用新LLM服务匹配画面失败,回退到旧实现: {str(e)}")
+ stream_text.info(tr("Streaming unavailable fallback waiting..."))
+ narration_result = match_narration_copy_to_script_legacy(
short_name=video_theme,
plot_analysis=analysis_result["analysis"],
- subtitle_content=subtitle_content, # 传递原始字幕内容
+ subtitle_content=subtitle_content,
+ narration_copy=narration_copy,
api_key=text_api_key,
model=text_model,
base_url=text_base_url,
- save_result=True,
temperature=temperature,
- provider=text_provider
+ provider=text_provider,
+ narration_language=narration_language,
+ drama_genre=drama_genre,
+ original_sound_ratio=original_sound_ratio,
)
if narration_result["status"] == "success":
- logger.info("\n解说文案生成成功!")
+ logger.info("\n剪辑脚本匹配成功!")
logger.info(narration_result["narration_script"])
else:
- logger.info(f"\n解说文案生成失败: {narration_result['message']}")
+ logger.info(f"\n剪辑脚本匹配失败: {narration_result['message']}")
st.error(tr("Script generation failed check logs"))
st.stop()
else:
@@ -519,6 +651,7 @@ def generate_script_short_sunmmary(
narration_dict['items'],
selected_video_paths,
)
+ narration_items = _strip_planner_only_fields(narration_items)
script = json.dumps(narration_items, ensure_ascii=False, indent=2)
if script is None:
@@ -543,3 +676,4 @@ def generate_script_short_sunmmary(
time.sleep(2)
progress_bar.empty()
status_text.empty()
+ stream_text.empty()
diff --git a/webui/tools/test_generate_short_summary_unittest.py b/webui/tools/test_generate_short_summary_unittest.py
new file mode 100644
index 0000000..30122d6
--- /dev/null
+++ b/webui/tools/test_generate_short_summary_unittest.py
@@ -0,0 +1,27 @@
+import unittest
+
+from webui.tools.generate_short_summary import _format_progress_status, parse_and_fix_json
+
+
+class GenerateShortSummaryJsonTests(unittest.TestCase):
+ def test_progress_message_does_not_prefix_fake_percentage(self):
+ status = _format_progress_status(60, "正在生成文案...")
+
+ self.assertEqual("正在生成文案...", status)
+ self.assertNotIn("60%", status)
+
+ def test_invalid_json_does_not_create_default_fake_script(self):
+ self.assertIsNone(parse_and_fix_json("not a json response"))
+
+ def test_json_code_block_is_parsed(self):
+ parsed = parse_and_fix_json(
+ """```json
+{"items": [{"_id": 1, "timestamp": "00:00:01,000-00:00:02,000"}]}
+```"""
+ )
+
+ self.assertEqual(1, parsed["items"][0]["_id"])
+
+
+if __name__ == "__main__":
+ unittest.main()