NarratoAI/app/services/test_script_subtitle_unittest.py
viccy dc12f390bb feat: 新增原片字幕支持并优化视频合并流程
- 为VideoClipParams新增原字幕路径配置字段,支持单条/多条字幕路径
- 完善webui参数获取逻辑,处理字幕路径兼容性并对接前端选择
- 重构后端字幕处理流程,支持自动匹配视频对应原字幕,合并原声字幕
- 优化视频合并逻辑,新增ffmpeg无损copy合并判断,自动回退重编码提升效率
- 新增ffmpeg快速素材合并路径,支持自定义字幕样式与多音轨混合
- 新增多个单元测试覆盖字幕匹配、合并及视频合并场景
2026-06-08 13:05:30 +08:00

193 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import tempfile
import unittest
from pathlib import Path
from app.services import script_subtitle
class ScriptSubtitleTests(unittest.TestCase):
def test_split_narration_prefers_punctuation_boundaries(self):
chunks = script_subtitle.split_narration(
"她终于意识到,这场婚姻不是爱情,而是一场交易。",
max_chars=12,
)
self.assertEqual(
["她终于意识到", "这场婚姻不是爱情", "而是一场交易"],
chunks,
)
def test_time_range_parsing_supports_milliseconds(self):
start, end = script_subtitle.parse_time_range("00:00:01,500-00:00:03,250")
self.assertAlmostEqual(1.5, start)
self.assertAlmostEqual(3.25, end)
def test_create_script_subtitle_file_skips_original_audio_segments(self):
list_script = [
{
"_id": 1,
"OST": 0,
"narration": "第一句解说。第二句解说。",
"editedTimeRange": "00:00:00-00:00:04",
"duration": 4,
},
{
"_id": 2,
"OST": 1,
"narration": "这句是原声,不应该默认生成。",
"editedTimeRange": "00:00:04-00:00:08",
"duration": 4,
},
{
"_id": 3,
"OST": 2,
"narration": "混合片段也保留解说字幕。",
"editedTimeRange": "00:00:08-00:00:12",
"duration": 4,
},
]
with tempfile.TemporaryDirectory() as temp_dir:
output_file = Path(temp_dir) / "script_subtitles.srt"
result = script_subtitle.create_script_subtitle_file(
task_id="test",
list_script=list_script,
output_file=str(output_file),
max_chars=16,
)
self.assertEqual(str(output_file), result)
content = output_file.read_text(encoding="utf-8")
self.assertIn("00:00:00,000 -->", content)
self.assertIn("第一句解说", content)
self.assertIn("混合片段也保留解说字幕", content)
self.assertNotIn("这句是原声", content)
self.assertNotIn("", content)
self.assertNotIn("", content)
def test_create_script_subtitle_file_uses_duration_when_edited_range_missing(self):
list_script = [
{
"_id": 1,
"OST": 0,
"narration": "没有 editedTimeRange 时使用 duration。",
"duration": 3,
}
]
with tempfile.TemporaryDirectory() as temp_dir:
output_file = Path(temp_dir) / "script_subtitles.srt"
script_subtitle.create_script_subtitle_file(
task_id="test",
list_script=list_script,
output_file=str(output_file),
)
content = output_file.read_text(encoding="utf-8")
self.assertIn("00:00:00,000 -->", content)
self.assertIn("--> 00:00:03,000", content)
def test_create_script_subtitle_file_includes_original_audio_subtitles(self):
list_script = [
{
"_id": 1,
"OST": 0,
"narration": "前情解说。",
"editedTimeRange": "00:00:00-00:00:02",
"duration": 2,
},
{
"_id": 2,
"video_id": 1,
"video_name": "source.mp4",
"OST": 1,
"narration": "播放原片2",
"timestamp": "00:00:10,000-00:00:14,000",
"sourceTimeRange": "00:00:10,000-00:00:14,000",
"editedTimeRange": "00:00:02-00:00:06",
"duration": 4,
},
]
original_srt = """1
00:00:09,000 --> 00:00:11,000
开头会被裁掉一秒。
2
00:00:11,500 --> 00:00:13,000
这句原声对白应该出现!
3
00:00:13,500 --> 00:00:15,000
结尾只保留半秒。
"""
with tempfile.TemporaryDirectory() as temp_dir:
subtitle_file = Path(temp_dir) / "source.srt"
subtitle_file.write_text(original_srt, encoding="utf-8")
output_file = Path(temp_dir) / "script_subtitles.srt"
script_subtitle.create_script_subtitle_file(
task_id="test",
list_script=list_script,
output_file=str(output_file),
original_subtitle_paths=[str(subtitle_file)],
video_origin_paths=["source.mp4"],
max_chars=16,
)
content = output_file.read_text(encoding="utf-8")
self.assertIn("前情解说", content)
self.assertIn("开头会被裁掉一秒", content)
self.assertIn("这句原声对白应该出现", content)
self.assertIn("结尾只保留半秒", content)
self.assertIn("00:00:02,000 --> 00:00:03,000", content)
self.assertIn("00:00:03,500 --> 00:00:05,000", content)
self.assertIn("00:00:05,500 --> 00:00:06,000", content)
self.assertNotIn("播放原片2", content)
def test_create_script_subtitle_file_uses_matching_video_id_for_original_subtitles(self):
list_script = [
{
"_id": 1,
"video_id": 2,
"video_name": "second.mp4",
"OST": 1,
"narration": "播放原片1",
"timestamp": "00:00:01,000-00:00:03,000",
"sourceTimeRange": "00:00:01,000-00:00:03,000",
"editedTimeRange": "00:00:00-00:00:02",
"duration": 2,
},
]
first_srt = """1
00:00:01,000 --> 00:00:03,000
第一个视频的字幕不应该出现。
"""
second_srt = """1
00:00:01,000 --> 00:00:03,000
第二个视频的字幕应该出现。
"""
with tempfile.TemporaryDirectory() as temp_dir:
first_file = Path(temp_dir) / "first.srt"
second_file = Path(temp_dir) / "second.srt"
output_file = Path(temp_dir) / "script_subtitles.srt"
first_file.write_text(first_srt, encoding="utf-8")
second_file.write_text(second_srt, encoding="utf-8")
script_subtitle.create_script_subtitle_file(
task_id="test",
list_script=list_script,
output_file=str(output_file),
original_subtitle_paths=[str(first_file), str(second_file)],
video_origin_paths=["first.mp4", "second.mp4"],
)
content = output_file.read_text(encoding="utf-8")
self.assertIn("第二个视频的字幕应该出现", content)
self.assertNotIn("第一个视频的字幕不应该出现", content)
if __name__ == "__main__":
unittest.main()