You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
70 lines
3.6 KiB
70 lines
3.6 KiB
import json |
|
from pathlib import Path |
|
|
|
import pytest |
|
|
|
from voice_transcriptor.models import MediaInfo |
|
from voice_transcriptor.services.preprocessing import CancellationToken, PreprocessingCancelled, PreprocessingOptions, PreprocessingService |
|
|
|
|
|
class FakeProbe: |
|
def __init__(self, media: MediaInfo): self.media = media |
|
def probe(self, path: Path) -> MediaInfo: return self.media |
|
|
|
|
|
class FakeProcess: |
|
def __init__(self, command): |
|
self.stdout = iter(["out_time_us=450000000\n", "progress=continue\n", "progress=end\n"]) |
|
self.returncode = 0 |
|
self.terminated = False |
|
def wait(self, timeout=None): return self.returncode |
|
def poll(self): return self.returncode |
|
def terminate(self): self.terminated = True |
|
def kill(self): self.terminated = True |
|
|
|
|
|
def make_media(path: Path, duration="1800") -> MediaInfo: |
|
path.write_bytes(b"source") |
|
return MediaInfo(path, 6, float(duration), "aac", duration, True, path.suffix in {".mp4", ".mov", ".mkv", ".webm"}) |
|
|
|
|
|
def test_preprocess_builds_streaming_commands_and_exact_manifest(tmp_path: Path) -> None: |
|
source = tmp_path / "recording.mp4" |
|
commands = [] |
|
def factory(command, **kwargs): |
|
commands.append(command); Path(command[-1]).write_bytes(b"chunk"); return FakeProcess(command) |
|
progress = [] |
|
service = PreprocessingService(Path("ffmpeg"), FakeProbe(make_media(source)), tmp_path / "jobs", factory) |
|
result = service.preprocess(source, PreprocessingOptions(), progress=progress.append) |
|
manifest = json.loads(result.manifest_path.read_text(encoding="utf-8")) |
|
assert len(commands) == 3 |
|
assert [commands[0][commands[0].index(f) + 1] for f in ("-ss", "-t", "-ac", "-ar", "-c:a", "-b:a")] == ["0", "900", "1", "24000", "aac", "64k"] |
|
assert all("0:a:0" in command and "-progress" in command for command in commands) |
|
assert manifest["state"] == "completed" |
|
assert manifest["chunks"][1]["source_start_seconds"] == "885" |
|
assert manifest["chunks"][-1]["source_end_seconds"] == "1800" |
|
assert manifest["completed_chunks"] == 3 |
|
assert progress[-1].percent == 100 |
|
assert [p.percent for p in progress] == sorted(p.percent for p in progress) |
|
|
|
|
|
def test_cleanup_and_retention_are_job_scoped(tmp_path: Path) -> None: |
|
source = tmp_path / "recording.wav" |
|
def factory(command, **kwargs): Path(command[-1]).write_bytes(b"x"); return FakeProcess(command) |
|
service = PreprocessingService(Path("ffmpeg"), FakeProbe(make_media(source, "10")), tmp_path / "jobs", factory) |
|
result = service.preprocess(source, PreprocessingOptions()); job = result.job_directory; result.cleanup(); assert not job.exists() |
|
retained = service.preprocess(source, PreprocessingOptions(retain_temporary_files=True)); retained.cleanup(); assert retained.job_directory.exists() |
|
|
|
|
|
def test_cancelled_job_stops_before_process(tmp_path: Path) -> None: |
|
source = tmp_path / "recording.m4a"; token = CancellationToken(); token.cancel() |
|
service = PreprocessingService(Path("ffmpeg"), FakeProbe(make_media(source, "10")), tmp_path / "jobs", lambda *a, **k: pytest.fail("started")) |
|
with pytest.raises(PreprocessingCancelled): service.preprocess(source, PreprocessingOptions(), token) |
|
|
|
|
|
@pytest.mark.parametrize("suffix", [".m4a", ".mp3", ".wav", ".mp4", ".mov", ".webm", ".mkv"]) |
|
def test_supported_extensions(tmp_path: Path, suffix: str) -> None: |
|
source = tmp_path / f"recording{suffix}" |
|
def factory(command, **kwargs): Path(command[-1]).write_bytes(b"x"); return FakeProcess(command) |
|
result = PreprocessingService(Path("ffmpeg"), FakeProbe(make_media(source, "1")), tmp_path / "jobs", factory).preprocess(source, PreprocessingOptions()) |
|
assert result.manifest_path.exists()
|
|
|