From 6ca2c9f884df9f4541ac535b43d666bcd80aef25 Mon Sep 17 00:00:00 2001 From: Rue <343175018+thatsrue@users.noreply.github.com> Date: Fri, 30 Oct 2026 21:35:11 -0400 Subject: [PATCH] fix: name a linked track's output by the link, not its target --- tests/test_batch.py | 42 +++++++++++++++++++++++++++++ transcript_combobulator/pipeline.py | 16 ++++++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/tests/test_batch.py b/tests/test_batch.py index e89aaf4..4611ccb 100644 --- a/tests/test_batch.py +++ b/tests/test_batch.py @@ -739,6 +739,48 @@ def test_symlink_is_placed_by_the_link_not_its_target(self, tmp_path): assert out.call_args.args == (link, None) assert ts.call_args.args[1] == tmp_path / "out" + def test_link_named_unlike_its_target_writes_the_vtt_combine_reads( + self, tmp_path, monkeypatch + ): + from transcript_combobulator import config + from transcript_combobulator.pipeline import process_file + + monkeypatch.setattr(config, "OUTPUT_DIR", tmp_path / "output") + real = tmp_path / "archive" / "4-pikel_123.wav" + real.parent.mkdir() + real.write_bytes(b"audio") + link = tmp_path / "s11" / "4-pikel.wav" + link.parent.mkdir() + link.symlink_to(real) + + def fake_transcribe(audio_path, output_dir, **kwargs): + # Names its files from the audio it is handed, as the real one does. + vtt = output_dir / config.vtt_name_for_stem(audio_path.stem) + vtt.write_text("WEBVTT\n") + return { + "vtt_file": str(vtt), + "json_file": str(output_dir / f"{audio_path.stem}_transcription.json"), + "mapping_file": str(output_dir / f"{audio_path.stem}_mapping.json"), + "metrics": {"chunk_count": 0, "failed_chunk_count": 0, "chunks": []}, + } + + with ( + patch( + "transcript_combobulator.pipeline.needs_conversion", return_value=False + ), + patch( + "transcript_combobulator.pipeline.process_audio", + return_value=(tmp_path, []), + ), + patch( + "transcript_combobulator.pipeline.transcribe_segments", + side_effect=fake_transcribe, + ), + patch("transcript_combobulator.pipeline.write_pipeline_manifest"), + ): + process_file(str(link), session="s11") + assert config.vtt_path_for_input(link, "s11").exists() + def test_session_is_passed_through(self, tmp_path): track = tmp_path / "1-cadderly.wav" track.touch() diff --git a/transcript_combobulator/pipeline.py b/transcript_combobulator/pipeline.py index 957afd5..cb70931 100644 --- a/transcript_combobulator/pipeline.py +++ b/transcript_combobulator/pipeline.py @@ -57,8 +57,10 @@ def process_file( metrics: Optional[dict[str, Any]] = None, session: Optional[str] = None, ) -> dict[str, Any]: - # Read through symlinks, but place output by the path as given. + # Read through symlinks, but place and name output by the path as given, + # so the VTT lands where vtt_path_for_input looks for it. input_file = Path(input_path).resolve() + stem = Path(input_path).stem setup_logging() file_metrics = metrics if metrics is not None else {} file_metrics.clear() @@ -100,8 +102,8 @@ def _progress_callback(phase: str, current: int, total: int) -> None: with _timed_stage(file_metrics, "setup"): output_dir = get_output_path_for_input(Path(input_path), session) output_dir.mkdir(parents=True, exist_ok=True) - output_file = output_dir / f"{input_file.stem}.wav" - manifest_path = get_manifest_path(output_dir, input_file.stem) + output_file = output_dir / f"{stem}.wav" + manifest_path = get_manifest_path(output_dir, stem) file_metrics["output_dir"] = str(output_dir) with _timed_stage(file_metrics, "cache_check"): @@ -114,10 +116,10 @@ def _progress_callback(phase: str, current: int, total: int) -> None: return file_metrics fingerprints = build_stage_fingerprints(input_file) - progress_path = get_progress_path(output_dir, input_file.stem) - mapping_path = output_dir / f"{input_file.stem}_mapping.json" - json_path = output_dir / f"{input_file.stem}_transcription.json" - vtt_path = output_dir / vtt_name_for_stem(input_file.stem) + progress_path = get_progress_path(output_dir, stem) + mapping_path = output_dir / f"{stem}_mapping.json" + json_path = output_dir / f"{stem}_transcription.json" + vtt_path = output_dir / vtt_name_for_stem(stem) with _timed_stage(file_metrics, "cache_invalidation"): if force: # A forced run trusts nothing it finds.