Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions scenedetect/output/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def write_scene_list(
Raises:
TypeError: "delimiter" must be a 1-character string
"""
# `_open_output_file` replaces filesystem paths with writable text streams.
assert not isinstance(output_csv_file, (str, bytes, os.PathLike))
output_file = output_csv_file
csv_writer = csv.writer(output_file, delimiter=col_separator, lineterminator=row_separator)
Expand Down Expand Up @@ -374,6 +375,7 @@ def write_scene_list_edl(
every event so the EDL aligns with the source media's on-screen timecode. Applied to
both source and record columns.
"""
# `_open_output_file` replaces filesystem paths with writable text streams.
assert not isinstance(output_path, (str, bytes, os.PathLike))
output_file = output_path
offset_frames = 0
Expand Down Expand Up @@ -436,6 +438,7 @@ def write_scene_list_fcpx(
of `video_path`.
"""
assert scene_list
# `_open_output_file` replaces filesystem paths with writable text streams.
assert not isinstance(output_path, (str, bytes, os.PathLike))
output_file = output_path
video_path = Path(video_path)
Expand Down Expand Up @@ -540,6 +543,7 @@ def write_scene_list_fcp7(
frozen. If None, falls back to the last scene's end time.
"""
assert scene_list
# `_open_output_file` replaces filesystem paths with writable text streams.
assert not isinstance(output_path, (str, bytes, os.PathLike))
output_file = output_path
video_path = Path(video_path)
Expand Down Expand Up @@ -654,6 +658,7 @@ def write_scene_list_otio(
name: Timeline name. Defaults to the stem of `video_path`.
audio: If True (default), include an audio track alongside the video track.
"""
# `_open_output_file` replaces filesystem paths with writable text streams.
assert not isinstance(output_path, (str, bytes, os.PathLike))
output_file = output_path
video_path = Path(video_path)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from scenedetect.output import (
SceneMetadata,
VideoMetadata,
_open_output_file,
is_ffmpeg_available,
split_video_ffmpeg,
write_scene_list,
Expand Down Expand Up @@ -273,6 +274,20 @@ def _make_output_target(
return output_target, lambda: output_path.read_text(encoding="utf-8")


def test_open_output_file_does_not_create_file_on_error(tmp_path: Path):
"""The decorator does not create a destination when output generation fails."""

@_open_output_file("TEST")
def failing_writer(output_file: ty.Any):
output_file.write("partial output")
raise RuntimeError("generation failed")

output_path = tmp_path / "output.txt"
with pytest.raises(RuntimeError, match="generation failed"):
failing_writer(output_path)
assert not output_path.exists()


@pytest.mark.parametrize("target_kind", _OUTPUT_TARGET_KINDS)
def test_write_scene_list_output_target(tmp_path: Path, target_kind: str):
"""CSV output accepts a file handle, string, Path, or bytes path."""
Expand Down
Loading