PySceneDetect 0.7.3 adds scene-length based image export to save-images, a close() method and context manager support for VideoStream, and fixes several timing bugs: PyAV frame timing for videos without presentation timestamps, save-edl timecode drift at NTSC frame rates, OTIO source durations, and the last scene of variable framerate videos. Windows distributions ship updated dependencies.
CLI Changes
- [feature] Add
-i/--intervaltosave-imagesto save images based on the length of each scene: one image perDURATION, with--min-images/--max-imagesto limit the number of images per scene, e.g.save-images -i 2s --max-images 10(interval/min-images/max-imagesin the config file, andinterval/min_images/max_imagesinsave_images()) #280 - [cli]
--helpnow prints[default: ...]and[setting: ...]values as they are written in a config file (merge,fcpx,yes/noinstead ofMode.MERGE,FcpFormat.FCPX,True/on), the[backend-pyav]threading-modeconfig value is now validated, and thescenedetect.cfgtemplate matches the real config keys and defaults (verbosity,downscale,default-detector,fade-bias,start-timecode, OTIOname, and removed stalefilter-mode/luma-onlyentries) #589 - [bugfix]
$VIDEO_NAMEis now derived from the path component of URL inputs, ignoring the query string and fragment (e.g.https://host/clip.mp4?token=1.2->clip). URLs with no path use the host. Previously the query string leaked into output filenames, which fails on Windows. #197 - [bugfix] OTIO exports use the full source duration when provided, including from the CLI, instead of declaring a fixed 1,980-frame duration. The available range remains unspecified when the duration is unknown. Scene source ranges are unchanged. #580
- [bugfix] The OpenCV backend keeps
positionon the last frame after reaching the end of a VFR video, so the last scene no longer ends early (or before it starts) #561 - [bugfix]
save-html --no-imagesnow writes to the global-o/--outputdirectory instead of the working directory #583 - [bugfix]
split-videowith-onow logs the output folder instead of repeating the filename template #595 - [bugfix] Allow
-d/--downscaleon the CLI anddownscalein the config file to be set to0for automatic downscaling - [bugfix]
save-edltimecodes now count frames at the nominal rate (24, 30, or 60 per timecode second for 23.976, 29.97, or 59.94 fps) as required for non-drop-frame timecode, instead of being derived from wall-clock seconds. Previously the EDL drifted by roughly 3.6 seconds per hour at these rates.--start-timecodeis parsed the same way, so-s 01:00:00:00now starts the first event at01:00:00:00. Non-standard rates (e.g. the average rate of a VFR video) are unchanged. Thanks to @Cassian433 for the detailed report #593
API Changes
- [feature]
VideoStreamhas a newclose()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. Callingclose()more than once has no effect. Once an OpenCV, PyAV, MoviePy or concatenated stream is closed,read(),seek()andreset()raiseValueError, while properties describing the video (path,name,frame_rate,duration,frame_size,aspect_ratio) remain available. The default implementation does nothing, so existingVideoStreamsubclasses are unaffected, andVideoCaptureAdapterleaves the caller'sVideoCaptureopen #568 - [api]
VideoStream.seek()now documents that the stream position is undefined after aSeekErroris raised, andreset()must be called before reading further - [bugfix]
VideoStreamConcatno 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 - [bugfix]
write_scene_list()now starts the cut list row withTimecode List:when nocut_listis passed, as it does when one is passed and aswrite_scene_list_html()does - [bugfix] Fix
pyavbackend timing for videos that lack presentation timestamps (e.g. AVI files, elementary H.264 streams), which could lead to frame positions being out of order or stuck at zero and shifting cuts by one or more frames (#590, PyAV#933, PyAV#2478)- Videos are now opened with FFmpeg's
genptsflag disabled. - Each frame will use the exact presentation timestamp, but if it is unavailable we now fall back to the decode timestamp to be consistent with the
opencvbackend. - A frame with neither timestamp is placed one frame period after the previous frame (as the
moviepybackend does for every frame). - A one-time warning is emitted if timestamps are still detected going backwards.
VideoStreamAv.seek()now raisesSeekErroron streams that cannot be seeked (e.g. elementary streams without timestamps) instead of a backend-specific exception.VideoStreamAv.seek()now falls back to reading from the start when the demuxer lands on a frame without a timestamp past the target, andseek(0)always re-opens the video, fixing wrong positions after seeking in some MPEG-TS files.
- Videos are now opened with FFmpeg's
Packaging
- [general] Updates to Windows distributions:
- av 19.0.1 -> 19.0.2
- click 8.5.0 -> 8.5.1
- imageio-ffmpeg 0.6.0 -> 0.8.1
- pillow 12.3.0 -> 13.0.0
- platformdirs 4.13.0 -> 4.14.5
Downloads
- Windows:
PySceneDetect-0.7.3-win64.msi(installer) orPySceneDetect-0.7.3-win64.zip(portable); both signed, checksums inSHA256SUMSandPySceneDetect-0.7.3-win64.manifest.json - Python:
pip install scenedetect==0.7.3(orscenedetect-headless); wheels and sdists attached - Full changelog: https://www.scenedetect.com/changelog/
Thanks to @nikhiljangid120, @sungbin1015, @qwist1233-cpu, @vishal-tambi, @Rober1208, @ChandraChaan and @MuhammadBilalKhan267 for their contributions to this release.