repaire sentences

This commit is contained in:
2026-10-21 19:39:04 +08:00
parent 176d722c77
commit 532d9900ba
13 changed files with 1233 additions and 60 deletions

View File

@@ -1029,3 +1029,139 @@ def test_admin_can_update_course_title(tmp_path):
json={"title": "Missing course"},
)
assert missing_response.status_code == 404
def test_admin_can_repair_sentence_gaps(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="Repair gaps lesson",
filename="lesson.mp4",
stored_filename=f"{VIDEO_HASH}.mp4",
content_type="video/mp4",
size_bytes=5,
language="en",
)
(settings.videos_dir / f"{VIDEO_HASH}.mp4").write_bytes(b"fake-video")
repository.save_processing_result(
SentenceBoundaryDocument(
video_hash=VIDEO_HASH,
duration_ms=18000,
algorithm_version="test",
sentences=[
SentenceBoundary(
index=0,
start_ms=0,
end_ms=2000,
text="Hello.",
language="en",
reference_speech_duration_ms=1800,
),
SentenceBoundary(
index=1,
start_ms=15000,
end_ms=18000,
text="Goodbye.",
language="en",
reference_speech_duration_ms=2500,
),
],
),
"Hello. Goodbye.",
)
repository.save_sentence_translations(VIDEO_HASH, {0: "你好。", 1: "再见。"})
from sentence_api.transcription import Transcript, TranscriptionSegment, _wav_bytes
import numpy as np
class GapTranscriber:
available = True
def transcribe(self, audio_path, language=None):
return Transcript(
text="The missing sentence.",
segments=[
TranscriptionSegment(
start_seconds=1.0,
end_seconds=5.0,
text="The missing sentence.",
)
],
)
def fake_extract(media_path, output_path, *, start_ms, duration_ms):
sample_rate = 16000
t = np.arange(int(max(1.0, duration_ms / 1000) * sample_rate)) / sample_rate
samples = (0.25 * np.sin(2 * np.pi * 220 * t)).astype(np.float32)
output_path.write_bytes(_wav_bytes(samples, sample_rate))
monkeypatch.setattr("sentence_api.processing.extract_audio_segment", fake_extract)
client = TestClient(
create_app(
BoundaryStore(tmp_path / "boundaries.json"),
settings=settings,
repository=repository,
transcriber=GapTranscriber(),
)
)
response = client.post(f"/api/v1/admin/videos/{VIDEO_HASH}/repair-gaps")
assert response.status_code == 200, response.text
payload = response.json()
assert payload["gap_count"] == 1
assert payload["inserted_count"] == 1
assert payload["sentence_count"] == 3
document = repository.get_document(VIDEO_HASH)
assert [item.text for item in document.sentences] == [
"Hello.",
"The missing sentence.",
"Goodbye.",
]
assert [item.zh_text for item in document.sentences] == ["你好。", None, "再见。"]
def test_admin_gap_repair_requires_transcriber(tmp_path):
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="Repair gaps lesson",
filename="lesson.mp4",
stored_filename=f"{VIDEO_HASH}.mp4",
content_type="video/mp4",
size_bytes=5,
language="en",
)
(settings.videos_dir / f"{VIDEO_HASH}.mp4").write_bytes(b"fake-video")
repository.save_processing_result(
SentenceBoundaryDocument(
video_hash=VIDEO_HASH,
duration_ms=18000,
algorithm_version="test",
sentences=[
SentenceBoundary(index=0, start_ms=0, end_ms=2000, text="Hello."),
SentenceBoundary(index=1, start_ms=15000, end_ms=18000, text="Goodbye."),
],
),
None,
)
class UnavailableTranscriber:
available = False
client = TestClient(
create_app(
BoundaryStore(tmp_path / "boundaries.json"),
settings=settings,
repository=repository,
transcriber=UnavailableTranscriber(),
)
)
response = client.post(f"/api/v1/admin/videos/{VIDEO_HASH}/repair-gaps")
assert response.status_code == 503