From e3cb7c4633dba7cfd234b588afd36d5818a6fa56 Mon Sep 17 00:00:00 2001 From: ChandraChaan <53801679+ChandraChaan@users.noreply.github.com> Date: Mon, 2 Nov 2026 12:09:27 +0530 Subject: [PATCH 1/2] Test the CSV cut list row when no cut list is passed --- tests/test_output.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_output.py b/tests/test_output.py index d3769eb4..709a4310 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -399,6 +399,16 @@ def test_write_scene_list_rejects_invalid_output_target(): write_scene_list(ty.cast(ty.Any, object()), scenes) +def test_write_scene_list_cut_list_row_without_cut_list(): + """The cut list row is labelled whether the cut list is passed in or derived from the scenes.""" + scenes = _fake_scenes(_FPS_CFR, [(0, 30), (30, 60), (60, 90)]) + derived, explicit = StringIO(), StringIO() + write_scene_list(derived, scenes) + write_scene_list(explicit, scenes, cut_list=[scenes[1][0], scenes[2][0]]) + assert derived.getvalue().splitlines()[0] == "Timecode List:,00:00:01.000,00:00:02.000" + assert derived.getvalue() == explicit.getvalue() + + def test_write_scene_list_edl(tmp_path: Path): """EDL output has title header, FCM line, and one event per scene in CMX 3600 format.""" scenes = _fake_scenes(_FPS_CFR, [(0, 30), (30, 60)]) From 2a3888f42cff3abfa858beb358c8631ace7a3181 Mon Sep 17 00:00:00 2001 From: ChandraChaan <53801679+ChandraChaan@users.noreply.github.com> Date: Mon, 2 Nov 2026 12:09:38 +0530 Subject: [PATCH 2/2] [output] Label the CSV cut list row when no cut list is passed write_scene_list() only wrote the "Timecode List:" label when a cut_list was passed. Without one, the conditional expression covered the whole row, so the derived cut list was written with no label (and an empty row for a single scene). The CLI always passes a cut list, but API callers using the documented default got a different first row. The label now precedes the cut list in both cases, as in write_scene_list_html(). --- scenedetect/output/__init__.py | 7 ++++--- website/pages/changelog.md | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scenedetect/output/__init__.py b/scenedetect/output/__init__.py index b0d56b35..097f18de 100644 --- a/scenedetect/output/__init__.py +++ b/scenedetect/output/__init__.py @@ -172,9 +172,10 @@ def write_scene_list( # If required, output the cutting list as the first row (i.e. before the header row). if include_cut_list: csv_writer.writerow( - ["Timecode List:", *cut_list] - if cut_list - else [start.get_timecode() for start, _ in scene_list[1:]] + [ + "Timecode List:", + *(cut_list if cut_list else [start.get_timecode() for start, _ in scene_list[1:]]), + ] ) csv_writer.writerow( [ diff --git a/website/pages/changelog.md b/website/pages/changelog.md index 0a336e81..f674489a 100644 --- a/website/pages/changelog.md +++ b/website/pages/changelog.md @@ -825,4 +825,5 @@ Development - [feature] `VideoStream` has a new `close()` method to release the file handles and decoders a video holds, and can be used as a context manager (`with open_video(path) as video:`) to close it automatically. Calling `close()` more than once has no effect. Once an OpenCV, PyAV, MoviePy or concatenated stream is closed, `read()`, `seek()` and `reset()` raise `ValueError`, while properties describing the video (`path`, `name`, `frame_rate`, `duration`, `frame_size`, `aspect_ratio`) remain available. The default implementation does nothing, so existing `VideoStream` subclasses are unaffected, and `VideoCaptureAdapter` leaves the caller's `VideoCapture` open [#568](https://github.com/Breakthrough/PySceneDetect/issues/568) - [bugfix] `VideoStreamConcat` no longer keeps its input videos open when they can't be concatenated (e.g. mismatched resolutions), and now closes each input explicitly once it is no longer needed instead of relying on garbage collection [#568](https://github.com/Breakthrough/PySceneDetect/issues/568) - [bugfix] `split-video` with `-o` now logs the output folder instead of repeating the filename template [#595](https://github.com/Breakthrough/PySceneDetect/pull/595) + - [bugfix] `write_scene_list()` now starts the cut list row with `Timecode List:` when no `cut_list` is passed, as it does when one is passed and as `write_scene_list_html()` does - [bugfix] Allow `-d`/`--downscale` on the CLI and `downscale` in the config file to be set to `0` for automatic downscaling \ No newline at end of file