later process

This commit is contained in:
2026-10-29 08:48:49 +08:00
parent 03e7987c7c
commit 031f3c6f21
9 changed files with 541 additions and 85 deletions

View File

@@ -790,6 +790,7 @@ def test_admin_can_merge_adjacent_sentences_and_reindexes_following_sentences(tm
VIDEO_HASH,
{0: "之前。", 1: "第一段", 2: "第二段", 3: "下一句。"},
)
(settings.videos_dir / f"{VIDEO_HASH}.mp4").write_bytes(b"video")
class UnavailableTranscriber:
available = False
@@ -833,6 +834,108 @@ def test_admin_can_merge_adjacent_sentences_and_reindexes_following_sentences(tm
def test_admin_batch_applies_merge_and_split_once(tmp_path, monkeypatch):
settings = replace(Settings.from_env(), data_dir=tmp_path / "data")
settings.ensure_directories()
repository = VideoRepository(settings.database_path)
repository.upsert_upload(
video_hash=VIDEO_HASH,
title="Batch edit lesson",
filename="lesson.mp4",
stored_filename=f"{VIDEO_HASH}.mp4",
content_type="video/mp4",
size_bytes=5,
language="en",
)
repository.save_processing_result(
SentenceBoundaryDocument(
video_hash=VIDEO_HASH,
duration_ms=12000,
algorithm_version="test",
sentences=[
SentenceBoundary(index=0, start_ms=0, end_ms=2000, text="Before.", language="en", reference_speech_duration_ms=1800),
SentenceBoundary(index=1, start_ms=2000, end_ms=5000, text="First part", language="en", reference_speech_duration_ms=2800),
SentenceBoundary(index=2, start_ms=5000, end_ms=9000, text="second part", language="en", reference_speech_duration_ms=3600),
SentenceBoundary(index=3, start_ms=9000, end_ms=12000, text="Next sentence.", language="en", reference_speech_duration_ms=2800),
],
),
None,
)
repository.save_sentence_translations(
VIDEO_HASH,
{0: "之前。", 1: "第一段", 2: "第二段", 3: "下一句。"},
)
(settings.videos_dir / f"{VIDEO_HASH}.mp4").write_bytes(b"video")
calls = []
from sentence_api.transcription import Transcript, TranscriptionSegment, WordTimestamp
class OnceTranscriber:
available = True
def transcribe(self, audio_path, language=None):
calls.append(audio_path)
return Transcript(
text="Next sentence.",
segments=[
TranscriptionSegment(
start_seconds=0.0,
end_seconds=1.0,
text="Next sentence.",
words=[
WordTimestamp(0.0, 0.2, "Next"),
WordTimestamp(0.2, 1.0, "sentence."),
],
)
],
)
monkeypatch.setattr(
"sentence_api.processing.extract_audio_segment",
lambda media_path, output_path, *, start_ms, duration_ms: output_path.write_bytes(b"audio"),
)
client = TestClient(
create_app(
BoundaryStore(tmp_path / "boundaries.json"),
settings=settings,
repository=repository,
transcriber=OnceTranscriber(),
)
)
response = client.put(
f"/api/v1/admin/videos/{VIDEO_HASH}/sentences/batch",
json={
"sentences": [
{"start_ms": 0, "end_ms": 2000, "text": "Before.", "language": "en", "reference_speech_duration_ms": 1800},
{"start_ms": 2000, "end_ms": 9000, "text": "First part second part", "language": "en", "reference_speech_duration_ms": 6400},
{"start_ms": 9000, "end_ms": 9200, "text": "Next", "language": "en", "reference_speech_duration_ms": 1000, "source_start_ms": 9000, "source_end_ms": 12000},
{"start_ms": 9200, "end_ms": 12000, "text": "sentence.", "language": "en", "reference_speech_duration_ms": 1000, "source_start_ms": 9000, "source_end_ms": 12000},
]
},
)
assert response.status_code == 200, response.text
payload = response.json()
assert [item["text"] for item in payload] == [
"Before.",
"First part second part",
"Next",
"sentence.",
]
assert [item["start_ms"] for item in payload] == [0, 2000, 9000, 9600]
assert [item["end_ms"] for item in payload] == [2000, 9000, 9600, 12000]
assert [item["reference_speech_duration_ms"] for item in payload] == [1800, 6400, 862, 1938]
assert len(calls) == 1
document = repository.get_document(VIDEO_HASH)
assert [item.text for item in document.sentences] == [
"Before.",
"First part second part",
"Next",
"sentence.",
]
assert all(not (item.zh_text or "").strip() for item in document.sentences)
def test_admin_sentence_merge_restarts_subtitle_translation(tmp_path, monkeypatch):
client = make_client(tmp_path)
_ready_video(client.app.state.video_repository)