优化交互文案

This commit is contained in:
puke
2025-10-28 14:58:55 +08:00
committed by puke
parent 2c3b2ab19c
commit a81d0d303d
3 changed files with 27 additions and 129 deletions

View File

@@ -14,7 +14,7 @@
"mode.generate": "💡 Generate Mode",
"mode.fixed": "📄 Fixed Mode",
"mode.help": "Generate: AI creates narrations from topic. Fixed: Use existing script without modification.",
"mode.help": "Generate: AI creates narrations from topic. Fixed: Use your script directly without modification, one narration per line.",
"input.book_name": "Book Name",
"input.book_name_placeholder": "e.g., Atomic Habits, How to Win Friends",
@@ -26,10 +26,10 @@
"input.text": "Text Input",
"input.text_help_generate": "Enter topic or theme (AI will create narrations)",
"input.text_help_fixed": "Enter complete narration script (will be split into scenes)",
"input.text_help_fixed": "Enter complete narration script (used directly without modification, one narration per line)",
"input.content": "Content",
"input.content_placeholder": "Enter your custom content here...",
"input.content_placeholder": "Used directly without modification, one narration per line\nExample:\nHello everyone, today I'll share three study tips\nThe first tip is focus training, meditate for 10 minutes daily\nThe second tip is active recall, review immediately after learning",
"input.content_help": "Provide your own content for video generation",
"input.title": "Title (Optional)",

View File

@@ -14,7 +14,7 @@
"mode.generate": "💡 生成模式",
"mode.fixed": "📄 固定模式",
"mode.help": "生成模式AI 从主题创作旁白。固定模式:使用现成脚本,不做修改。",
"mode.help": "生成模式AI 从主题创作旁白。固定模式:直接使用您的脚本,不做任何改写,每行一个旁白。",
"input.book_name": "书名",
"input.book_name_placeholder": "例如原子习惯、人性的弱点、Atomic Habits",
@@ -26,10 +26,10 @@
"input.text": "文本输入",
"input.text_help_generate": "输入主题或话题AI 将创作旁白)",
"input.text_help_fixed": "输入完整的旁白脚本(将被切分成分镜",
"input.text_help_fixed": "输入完整的旁白脚本(直接使用,不做改写,每行一个旁白",
"input.content": "内容",
"input.content_placeholder": "在此输入您的自定义内容...",
"input.content_placeholder": "直接使用,不做改写,每行一个旁白\n例如\n大家好今天跟你分享三个学习技巧\n第一个技巧是专注力训练每天冥想10分钟\n第二个技巧是主动回忆学完立即复述",
"input.content_help": "提供您自己的内容用于视频生成",
"input.title": "标题(可选)",

View File

@@ -95,13 +95,13 @@ class VideoGeneratorService:
Args:
text: Text input (required)
- For generate mode: topic/theme (e.g., "如何提高学习效率")
- For fixed mode: complete narration script (will be split into frames)
- For fixed mode: complete narration script (each line is a narration)
mode: Processing mode (default "generate")
- "generate": LLM generates narrations from topic/theme, creates n_scenes
- "fixed": Split existing script into frames, preserves original text
- "fixed": Use existing script as-is, each line becomes a narration
Note: In fixed mode, n_scenes is ignored (uses actual split count)
Note: In fixed mode, n_scenes is ignored (uses actual line count)
title: Video title (optional)
- If provided, use it as the video title
@@ -153,23 +153,16 @@ class VideoGeneratorService:
... bgm_path="default"
... )
# Fixed mode: Use existing script (split by paragraphs)
# Fixed mode: Use existing script (each line is a narration)
>>> script = '''大家好,今天跟你分享三个学习技巧
...
... 第一个技巧是专注力训练每天冥想10分钟
...
... 第个技巧是主动回忆,学完立即复述'''
... 第二个技巧是主动回忆,学完立即复述
... 第个技巧是间隔重复,学习后定期复习'''
>>> result = await reelforge.generate_video(
... text=script,
... mode="fixed",
... title="三个学习技巧"
... )
# Fixed mode: Use existing script (split by sentences)
>>> result = await reelforge.generate_video(
... text="第一点是专注。第二点是复述。第三点是重复。",
... mode="fixed"
... )
>>> print(result.video_path)
"""
# ========== Step 0: Process text and determine title ==========
@@ -249,10 +242,10 @@ class VideoGeneratorService:
)
logger.info(f"✅ Generated {len(narrations)} narrations")
else: # fixed
# Split fixed script using LLM (preserves original text)
# Split fixed script by lines (trust user input completely)
self._report_progress(progress_callback, "splitting_script", 0.05)
narrations = await self._split_narration_script(text, config)
logger.info(f"✅ Split script into {len(narrations)} segments")
logger.info(f"✅ Split script into {len(narrations)} segments (by lines)")
logger.info(f" Note: n_scenes={n_scenes} is ignored in fixed mode")
# Step 2: Generate image prompts
@@ -452,124 +445,29 @@ class VideoGeneratorService:
async def _split_narration_script(self, script: str, config: StoryboardConfig) -> list[str]:
"""
Split user-provided narration script into segments (programmatic splitting).
Split user-provided narration script into segments (trust user input completely).
Priority:
1. Split by major punctuation (newline, 。!?;)
2. If segment > max_len, split by comma ()
3. If still > max_len, keep original (no force split)
4. Merge segments < min_len with next segment
Simply split by newline, each line becomes a narration segment.
Empty lines are filtered out.
Args:
script: Fixed narration script
config: Storyboard configuration (for length guidelines)
script: Fixed narration script (each line is a narration)
config: Storyboard configuration (unused, kept for interface compatibility)
Returns:
List of narration segments
"""
import re
logger.info(f"Splitting script by lines (length: {len(script)} chars)")
min_len = config.min_narration_words
max_len = config.max_narration_words
# Split by newline, filter empty lines
narrations = [line.strip() for line in script.split('\n') if line.strip()]
logger.info(f"Splitting script (length: {len(script)} chars) with target: {min_len}-{max_len} chars")
# Step 1: Split by major punctuation (newline, period, exclamation, question mark, semicolon)
major_delimiters = r'[\n。]'
parts = re.split(f'({major_delimiters})', script)
# Reconstruct sentences (text only, remove trailing punctuation)
sentences = []
for i in range(0, len(parts)-1, 2):
text = parts[i].strip()
if text:
sentences.append(text)
# Handle last part if no delimiter
if len(parts) % 2 == 1 and parts[-1].strip():
sentences.append(parts[-1].strip())
logger.debug(f"After major split: {len(sentences)} sentences")
# Step 2: For segments > max_len, try splitting by comma
final_segments = []
for sentence in sentences:
sent_len = len(sentence)
# If within range or short, keep as is
if sent_len <= max_len:
final_segments.append(sentence)
continue
# Too long: try splitting by comma
comma_parts = re.split(r'()', sentence)
sub_segments = []
current = ""
for part in comma_parts:
if part == '':
continue
if not current:
current = part
elif len(current + part) <= max_len:
current += part
else:
# Current segment is ready
if current:
sub_segments.append(current.strip())
current = part
# Add last segment
if current:
sub_segments.append(current.strip())
# If comma splitting worked (resulted in multiple segments), use it
if sub_segments and len(sub_segments) > 1:
final_segments.extend(sub_segments)
else:
# Keep original sentence even if > max_len
logger.debug(f"Keeping long segment ({sent_len} chars): {sentence[:30]}...")
final_segments.append(sentence)
# Step 3: Merge segments that are too short
merged_segments = []
i = 0
while i < len(final_segments):
segment = final_segments[i]
# If too short and not the last one, try merging with next
if len(segment) < min_len and i < len(final_segments) - 1:
next_segment = final_segments[i + 1]
merged = segment + "" + next_segment
# If merged result is within max_len, use it
if len(merged) <= max_len:
merged_segments.append(merged)
i += 2 # Skip next segment
continue
# Otherwise keep as is
merged_segments.append(segment)
i += 1
# Clean up
result = [s.strip() for s in merged_segments if s.strip()]
logger.info(f"Split script into {len(narrations)} segments (by lines)")
# Log statistics
lengths = [len(s) for s in result]
logger.info(f"Script split into {len(result)} segments")
if lengths:
logger.info(f" Min: {min(lengths)} chars, Max: {max(lengths)} chars, Avg: {sum(lengths)//len(lengths)} chars")
in_range = sum(1 for l in lengths if min_len <= l <= max_len)
too_short = sum(1 for l in lengths if l < min_len)
too_long = sum(1 for l in lengths if l > max_len)
logger.info(f" In range ({min_len}-{max_len}): {in_range}/{len(result)} ({in_range*100//len(result)}%)")
if too_short:
logger.info(f" Too short (< {min_len}): {too_short}/{len(result)} ({too_short*100//len(result)}%)")
if too_long:
logger.info(f" Too long (> {max_len}): {too_long}/{len(result)} ({too_long*100//len(result)}%)")
if narrations:
lengths = [len(s) for s in narrations]
logger.info(f" Min: {min(lengths)} chars, Max: {max(lengths)} chars, Avg: {sum(lengths)//len(lengths)} chars")
return result
return narrations