scenedetect 0.7.1.dev0__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/PKG-INFO +2 -2
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/README.md +29 -14
- scenedetect-0.7.2/docs/LATEST_VERSION +1 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api.rst +3 -3
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/cli/backends.rst +1 -1
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/cli.rst +45 -7
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/index.rst +1 -1
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/packaging/package-info.rst +1 -1
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/__init__.py +46 -38
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_cli/__init__.py +68 -18
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_cli/commands.py +23 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_cli/config.py +9 -4
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_cli/context.py +4 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_cli/controller.py +0 -1
- scenedetect-0.7.2/scenedetect/_fan_out.py +244 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/backends/__init__.py +6 -4
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/backends/moviepy.py +8 -2
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/backends/opencv.py +43 -7
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/backends/pyav.py +75 -38
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/common.py +38 -11
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detector.py +19 -13
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/__init__.py +5 -5
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/adaptive_detector.py +2 -1
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/hash_detector.py +2 -2
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/histogram_detector.py +8 -6
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/threshold_detector.py +24 -6
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/output/__init__.py +139 -41
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/platform.py +10 -6
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/scene_manager.py +37 -24
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/video_stream.py +1 -1
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.cfg +18 -4
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.egg-info/PKG-INFO +2 -2
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.egg-info/SOURCES.txt +3 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_api.py +7 -3
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_backend_opencv.py +44 -1
- scenedetect-0.7.2/tests/test_backend_pyav.py +222 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_cli.py +90 -26
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_detectors.py +50 -4
- scenedetect-0.7.2/tests/test_fan_out.py +236 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_output.py +145 -6
- scenedetect-0.7.2/tests/test_threshold_detector.py +165 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_timecode.py +136 -7
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_vfr.py +29 -29
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_video_stream.py +48 -32
- scenedetect-0.7.1.dev0/docs/LATEST_VERSION +0 -1
- scenedetect-0.7.1.dev0/tests/test_backend_pyav.py +0 -93
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/LICENSE +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/Makefile +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/_static/favicon.ico +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/_static/pyscenedetect.css +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/_static/pyscenedetect_logo.png +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/_static/pyscenedetect_logo_small.png +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/_templates/navigation.html +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/backends.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/common.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/detector.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/detectors.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/migration_guide.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/output.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/platform.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/scene_manager.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/stats_manager.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/api/video_stream.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/cli/config_file.rst +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/conf.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/generate_cli_docs.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/docs/make.bat +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/pyproject.toml +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/__main__.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_thirdparty/__init__.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/_thirdparty/simpletable.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/backends/concat.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/content_detector.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/detectors/transnet_v2.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/frame_timecode.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/output/image.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/output/video.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/scene_detector.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/stats_manager.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect/video_splitter.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.egg-info/dependency_links.txt +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.egg-info/entry_points.txt +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.egg-info/requires.txt +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/scenedetect.egg-info/top_level.txt +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/setup.cfg +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_benchmark_evaluator.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_concat.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_platform.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_scene_manager.py +0 -0
- {scenedetect-0.7.1.dev0 → scenedetect-0.7.2}/tests/test_stats_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scenedetect
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Video scene cut/shot detection program and Python library (bundles the opencv-python OpenCV variant and the scenedetect CLI).
|
|
5
5
|
Author-email: Brandon Castellano <brandon248@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -66,7 +66,7 @@ Github Repo: https://github.com/Breakthrough/PySceneDetect/
|
|
|
66
66
|
|
|
67
67
|
Install: ``pip install --upgrade scenedetect`` (or ``scenedetect-headless`` for servers)
|
|
68
68
|
|
|
69
|
-
Packages: `scenedetect <https://pypi.org/project/scenedetect/>`_ (CLI + ``opencv-python``)
|
|
69
|
+
Packages: `scenedetect <https://pypi.org/project/scenedetect/>`_ (CLI + ``opencv-python``) and `scenedetect-headless <https://pypi.org/project/scenedetect-headless/>`_ (CLI + ``opencv-python-headless``). Both provide the same ``scenedetect`` module -- install only one.
|
|
70
70
|
|
|
71
71
|
----------------------------------------------------------
|
|
72
72
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
----------------------------------------------------------
|
|
15
15
|
|
|
16
|
-
### Latest Release: v0.7 (
|
|
16
|
+
### Latest Release: v0.7.2 (October 11, 2026)
|
|
17
17
|
|
|
18
18
|
**Website**: [scenedetect.com](https://www.scenedetect.com)
|
|
19
19
|
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
pip install scenedetect --upgrade
|
|
31
31
|
|
|
32
|
-
Requires ffmpeg/mkvmerge for video splitting support. Windows builds (MSI installer/portable ZIP) can be found on [the download page](https://scenedetect.com/download/).
|
|
32
|
+
Requires ffmpeg/mkvmerge for video splitting support. Windows builds (MSI installer/portable ZIP) can be found on [the download page](https://scenedetect.com/download/). A Docker image with all dependencies included is available as [`ghcr.io/breakthrough/pyscenedetect`](https://github.com/Breakthrough/PySceneDetect/pkgs/container/pyscenedetect).
|
|
33
33
|
|
|
34
34
|
----------------------------------------------------------
|
|
35
35
|
|
|
@@ -49,13 +49,20 @@ Skip the first 10 seconds of the input video:
|
|
|
49
49
|
|
|
50
50
|
More examples can be found throughout [the documentation](https://www.scenedetect.com/docs/latest/cli.html).
|
|
51
51
|
|
|
52
|
+
**Quick Start (Docker)**:
|
|
53
|
+
|
|
54
|
+
The same commands work without installing anything using [the official Docker image](https://github.com/Breakthrough/PySceneDetect/pkgs/container/pyscenedetect), which includes all dependencies (`ffmpeg`/`mkvmerge` included). Mount the folder containing your videos and use it for input/output paths:
|
|
55
|
+
|
|
56
|
+
docker run --rm -v "$(pwd):/files" ghcr.io/breakthrough/pyscenedetect -i /files/video.mp4 split-video -o /files
|
|
57
|
+
|
|
52
58
|
**Quick Start (Python API)**:
|
|
53
59
|
|
|
54
60
|
To get started, there is a high level function in the library that performs content-aware scene detection on a video (try it from a Python prompt):
|
|
55
61
|
|
|
56
62
|
```python
|
|
57
63
|
from scenedetect import detect, ContentDetector
|
|
58
|
-
|
|
64
|
+
|
|
65
|
+
scene_list = detect("my_video.mp4", ContentDetector())
|
|
59
66
|
```
|
|
60
67
|
|
|
61
68
|
`scene_list` will now be a list containing the start/end times of all scenes found in the video. There also exists a two-pass version `AdaptiveDetector` which handles fast camera movement better, and `ThresholdDetector` for handling fade out/fade in events.
|
|
@@ -64,20 +71,28 @@ Try calling `print(scene_list)`, or iterating over each scene:
|
|
|
64
71
|
|
|
65
72
|
```python
|
|
66
73
|
from scenedetect import detect, ContentDetector
|
|
67
|
-
|
|
74
|
+
|
|
75
|
+
scene_list = detect("my_video.mp4", ContentDetector())
|
|
68
76
|
for i, scene in enumerate(scene_list):
|
|
69
|
-
print(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
print(
|
|
78
|
+
" Scene %2d: Start %s / Frame %d, End %s / Frame %d"
|
|
79
|
+
% (
|
|
80
|
+
i + 1,
|
|
81
|
+
scene[0].get_timecode(),
|
|
82
|
+
scene[0].frame_num,
|
|
83
|
+
scene[1].get_timecode(),
|
|
84
|
+
scene[1].frame_num,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
73
87
|
```
|
|
74
88
|
|
|
75
89
|
We can also split the video into each scene if `ffmpeg` is installed (`mkvmerge` is also supported):
|
|
76
90
|
|
|
77
91
|
```python
|
|
78
92
|
from scenedetect import detect, ContentDetector, split_video_ffmpeg
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
|
|
94
|
+
scene_list = detect("my_video.mp4", ContentDetector())
|
|
95
|
+
split_video_ffmpeg("my_video.mp4", scene_list)
|
|
81
96
|
```
|
|
82
97
|
|
|
83
98
|
For more advanced usage, the API is highly configurable, and can easily integrate with any pipeline. This includes using different detection algorithms, splitting the input video, and much more. The following example shows how to implement a function similar to the above, but using [the `scenedetect` API](https://www.scenedetect.com/docs/latest/api.html):
|
|
@@ -87,12 +102,12 @@ from scenedetect import open_video, SceneManager, split_video_ffmpeg
|
|
|
87
102
|
from scenedetect.detectors import ContentDetector
|
|
88
103
|
from scenedetect.video_splitter import split_video_ffmpeg
|
|
89
104
|
|
|
105
|
+
|
|
90
106
|
def split_video_into_scenes(video_path, threshold=27.0):
|
|
91
107
|
# Open our video, create a scene manager, and add a detector.
|
|
92
108
|
video = open_video(video_path)
|
|
93
109
|
scene_manager = SceneManager()
|
|
94
|
-
scene_manager.add_detector(
|
|
95
|
-
ContentDetector(threshold=threshold))
|
|
110
|
+
scene_manager.add_detector(ContentDetector(threshold=threshold))
|
|
96
111
|
scene_manager.detect_scenes(video, show_progress=True)
|
|
97
112
|
scene_list = scene_manager.get_scene_list()
|
|
98
113
|
split_video_ffmpeg(video_path, scene_list, show_progress=True)
|
|
@@ -102,13 +117,13 @@ See [the documentation](https://www.scenedetect.com/docs/latest/api.html) for mo
|
|
|
102
117
|
|
|
103
118
|
**Benchmark**:
|
|
104
119
|
|
|
105
|
-
We evaluate the performance of different detectors in terms of accuracy and processing speed. See the [benchmark report](benchmark/README.md) for details.
|
|
120
|
+
We evaluate the performance of different detectors in terms of accuracy and processing speed. See [www.scenedetect.com/benchmarks](https://www.scenedetect.com/benchmarks/) for results, or the [benchmark report](benchmark/README.md) for details on the datasets and methodology.
|
|
106
121
|
|
|
107
122
|
## Reference
|
|
108
123
|
|
|
109
124
|
- [Documentation](https://www.scenedetect.com/docs/) (covers application and Python API)
|
|
110
125
|
- [CLI Example](https://www.scenedetect.com/cli/)
|
|
111
|
-
- [Config File](https://www.scenedetect.com/docs/
|
|
126
|
+
- [Config File](https://www.scenedetect.com/docs/latest/cli/config_file.html)
|
|
112
127
|
|
|
113
128
|
## Help & Contributing
|
|
114
129
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.7.2
|
|
@@ -23,9 +23,9 @@ The `scenedetect` API is easy to integrate with most application workflows, whil
|
|
|
23
23
|
|
|
24
24
|
* :func:`split_video_ffmpeg <scenedetect.output.split_video_ffmpeg>` and :func:`split_video_mkvmerge <scenedetect.output.split_video_mkvmerge>` split a video based on the detected scenes
|
|
25
25
|
|
|
26
|
-
* :func:`save_images <scenedetect.
|
|
26
|
+
* :func:`save_images <scenedetect.output.save_images>` can save an arbitrary number of images from each scene
|
|
27
27
|
|
|
28
|
-
* :func:`write_scene_list <scenedetect.
|
|
28
|
+
* :func:`write_scene_list <scenedetect.output.write_scene_list>` can be used to save scene/cut info as CSV, :func:`write_scene_list_html <scenedetect.output.write_scene_list_html>` for HTML
|
|
29
29
|
|
|
30
30
|
* :ref:`scenedetect.backends 🎥 <scenedetect-backends>`: PySceneDetect supports multiple libraries as an input backend:
|
|
31
31
|
|
|
@@ -58,7 +58,7 @@ Most types/functions are also available directly from the `scenedetect` package
|
|
|
58
58
|
|
|
59
59
|
.. code:: python
|
|
60
60
|
|
|
61
|
-
scenedetect
|
|
61
|
+
scenedetect~=0.7
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
.. _scenedetect-quickstart:
|
|
@@ -19,7 +19,7 @@ The `OpenCV <https://opencv.org/>`_ backend (usually `opencv-python <https://pyp
|
|
|
19
19
|
|
|
20
20
|
It is mostly reliable and fast, although can occasionally run into issues processing videos with multiple audio tracks or small amounts of frame corruption. You can use a custom version of the ``cv2`` package, or install either the `opencv-python` or `opencv-python-headless` packages from `pip`.
|
|
21
21
|
|
|
22
|
-
The OpenCV backend also supports image sequences as inputs (e.g. ``frame%02d.jpg`` if you want to load frame001.jpg, frame002.jpg, frame003.jpg...). Make sure to specify the framerate manually (``-f``/``--
|
|
22
|
+
The OpenCV backend also supports image sequences as inputs (e.g. ``frame%02d.jpg`` if you want to load frame001.jpg, frame002.jpg, frame003.jpg...). Make sure to specify the framerate manually (``-f``/``--frame-rate``) to ensure accurate timing calculations.
|
|
23
23
|
|
|
24
24
|
Variable framerate (VFR) video is supported. Scene detection uses PTS-derived timestamps from ``CAP_PROP_POS_MSEC`` for accurate timecodes. Seeking compensates for OpenCV's average-fps-based internal seek approximation, so output timecodes remain accurate across the full video.
|
|
25
25
|
|
|
@@ -61,9 +61,9 @@ Options
|
|
|
61
61
|
|
|
62
62
|
Stats file (.csv) to write frame metrics. Existing files will be overwritten. Used for tuning detection parameters and data analysis.
|
|
63
63
|
|
|
64
|
-
.. option:: -f FPS, --framerate FPS
|
|
64
|
+
.. option:: -f FPS, --framerate FPS, --frame-rate FPS
|
|
65
65
|
|
|
66
|
-
Override
|
|
66
|
+
Override frame rate with value as frames/sec.
|
|
67
67
|
|
|
68
68
|
.. option:: -m TIMECODE, --min-scene-len TIMECODE
|
|
69
69
|
|
|
@@ -308,13 +308,13 @@ Options
|
|
|
308
308
|
|
|
309
309
|
Max distance between hash values (0.0 to 1.0) of adjacent frames. Lower values are more sensitive to changes.
|
|
310
310
|
|
|
311
|
-
Default: ``0.
|
|
311
|
+
Default: ``0.35``
|
|
312
312
|
|
|
313
313
|
.. option:: -s SIZE, --size SIZE
|
|
314
314
|
|
|
315
315
|
Size of square of low frequency data to include from the discrete cosine transform.
|
|
316
316
|
|
|
317
|
-
Default: ``
|
|
317
|
+
Default: ``8``
|
|
318
318
|
|
|
319
319
|
.. option:: -l FRAC, --lowpass FRAC
|
|
320
320
|
|
|
@@ -359,13 +359,13 @@ Options
|
|
|
359
359
|
|
|
360
360
|
Max difference (0.0 to 1.0) between histograms of adjacent frames. Lower values are more sensitive to changes.
|
|
361
361
|
|
|
362
|
-
Default: ``0.
|
|
362
|
+
Default: ``0.2``
|
|
363
363
|
|
|
364
364
|
.. option:: -b NUM, --bins NUM
|
|
365
365
|
|
|
366
366
|
The number of bins to use for the histogram calculation.
|
|
367
367
|
|
|
368
|
-
Default: ``
|
|
368
|
+
Default: ``128``
|
|
369
369
|
|
|
370
370
|
.. option:: -m TIMECODE, --min-scene-len TIMECODE
|
|
371
371
|
|
|
@@ -416,6 +416,12 @@ Options
|
|
|
416
416
|
|
|
417
417
|
Default: ``True``
|
|
418
418
|
|
|
419
|
+
.. option:: --min-out-length TIMECODE
|
|
420
|
+
|
|
421
|
+
Minimum time spent faded out before a cut is allowed. Ignores shorter fades even when the minimum scene length is met. Also applies to a final fade-out, including the last processed frame. Zero allows fades of any duration. TIMECODE can be specified in frames (4), seconds with an `s` suffix (0.4s), or timecode (00:00:00.400).
|
|
422
|
+
|
|
423
|
+
Default: ``0.1s``. The Python API default remains ``0``.
|
|
424
|
+
|
|
419
425
|
.. option:: -m TIMECODE, --min-scene-len TIMECODE
|
|
420
426
|
|
|
421
427
|
Minimum length of any scene. Overrides global option :option:`-m/--min-scene-len <scenedetect -m>`. TIMECODE can be specified in frames (-m 100), in seconds with `s` suffix (-m 3.5s), or timecode (-m 00:01:52.778).
|
|
@@ -549,6 +555,10 @@ Options
|
|
|
549
555
|
|
|
550
556
|
Output directory to save EDL file to. Overrides global option :option:`-o/--output <scenedetect -o>`.
|
|
551
557
|
|
|
558
|
+
.. option:: -s TIMECODE, --start-timecode TIMECODE
|
|
559
|
+
|
|
560
|
+
Start timecode added to every event so the EDL aligns with the source media's on-screen timecode. Accepts SMPTE HH:MM:SS:FF or 8 digits (HHMMSSFF, e.g. 01000000).
|
|
561
|
+
|
|
552
562
|
|
|
553
563
|
.. _command-save-fcp:
|
|
554
564
|
|
|
@@ -707,6 +717,34 @@ Options
|
|
|
707
717
|
Width (pixels) of images.
|
|
708
718
|
|
|
709
719
|
|
|
720
|
+
.. _command-save-keyframes:
|
|
721
|
+
|
|
722
|
+
.. program:: scenedetect save-keyframes
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
``save-keyframes``
|
|
726
|
+
========================================================================
|
|
727
|
+
|
|
728
|
+
Save detected cuts using keyframe format v1.
|
|
729
|
+
|
|
730
|
+
Frame numbers are currently approximate for variable framerate (VFR) video.
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
Options
|
|
734
|
+
------------------------------------------------------------------------
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
.. option:: -f NAME, --filename NAME
|
|
738
|
+
|
|
739
|
+
Filename format to use.
|
|
740
|
+
|
|
741
|
+
Default: ``$VIDEO_NAME-keyframes.txt``
|
|
742
|
+
|
|
743
|
+
.. option:: -o DIR, --output DIR
|
|
744
|
+
|
|
745
|
+
Output directory to save keyframes to. Overrides global option :option:`-o/--output <scenedetect -o>`.
|
|
746
|
+
|
|
747
|
+
|
|
710
748
|
.. _command-save-otio:
|
|
711
749
|
|
|
712
750
|
.. program:: scenedetect save-otio
|
|
@@ -859,7 +897,7 @@ Options
|
|
|
859
897
|
|
|
860
898
|
.. option:: --expand
|
|
861
899
|
|
|
862
|
-
Extend the first/last output clips to cover the full input video, even if
|
|
900
|
+
Extend the first/last output clips to cover the full input video, even if `time -s/-e` limited the analysis window. Useful for keeping content outside the analyzed region attached to the adjacent split.
|
|
863
901
|
|
|
864
902
|
|
|
865
903
|
.. _command-time:
|
|
@@ -8,7 +8,7 @@ PySceneDetect Documentation
|
|
|
8
8
|
|
|
9
9
|
Welcome to the PySceneDetect docs. The docs are split into two separate parts: one for the command-line interface (the `scenedetect` command) and another for the Python API (the `scenedetect` module).
|
|
10
10
|
|
|
11
|
-
You can install the latest release of PySceneDetect by running `pip install scenedetect` (or `pip install scenedetect-headless` on servers without GUI libraries), or by downloading the Windows build from `scenedetect.com/download <http://www.scenedetect.com/download/>`_.
|
|
11
|
+
You can install the latest release of PySceneDetect by running `pip install scenedetect` (or `pip install scenedetect-headless` on servers without GUI libraries), or by downloading the Windows build from `scenedetect.com/download <http://www.scenedetect.com/download/>`_. PySceneDetect requires `ffmpeg` or `mkvmerge` for video splitting support.
|
|
12
12
|
|
|
13
13
|
.. note::
|
|
14
14
|
|
|
@@ -25,7 +25,7 @@ Github Repo: https://github.com/Breakthrough/PySceneDetect/
|
|
|
25
25
|
|
|
26
26
|
Install: ``pip install --upgrade scenedetect`` (or ``scenedetect-headless`` for servers)
|
|
27
27
|
|
|
28
|
-
Packages: `scenedetect <https://pypi.org/project/scenedetect/>`_ (CLI + ``opencv-python``)
|
|
28
|
+
Packages: `scenedetect <https://pypi.org/project/scenedetect/>`_ (CLI + ``opencv-python``) and `scenedetect-headless <https://pypi.org/project/scenedetect-headless/>`_ (CLI + ``opencv-python-headless``). Both provide the same ``scenedetect`` module -- install only one.
|
|
29
29
|
|
|
30
30
|
----------------------------------------------------------
|
|
31
31
|
|
|
@@ -15,6 +15,7 @@ can be used to open a video for a
|
|
|
15
15
|
:class:`SceneManager <scenedetect.scene_manager.SceneManager>`.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
+
import warnings
|
|
18
19
|
from logging import getLogger
|
|
19
20
|
|
|
20
21
|
# OpenCV is a required package, but we don't have it as an explicit dependency since we
|
|
@@ -30,54 +31,56 @@ except ModuleNotFoundError as ex:
|
|
|
30
31
|
|
|
31
32
|
# Commonly used classes/functions exported under the `scenedetect` namespace for brevity.
|
|
32
33
|
# Note that order of importants is important!
|
|
33
|
-
from scenedetect.platform import init_logger # noqa: I001
|
|
34
|
+
from scenedetect.platform import init_logger as init_logger # noqa: I001
|
|
34
35
|
from scenedetect.common import (
|
|
35
|
-
FrameTimecode,
|
|
36
|
-
FrameRate,
|
|
37
|
-
SceneList,
|
|
38
|
-
CutList,
|
|
39
|
-
CropRegion,
|
|
40
|
-
TimecodePair,
|
|
41
|
-
TimecodeLike,
|
|
42
|
-
Interpolation,
|
|
36
|
+
FrameTimecode as FrameTimecode,
|
|
37
|
+
FrameRate as FrameRate,
|
|
38
|
+
SceneList as SceneList,
|
|
39
|
+
CutList as CutList,
|
|
40
|
+
CropRegion as CropRegion,
|
|
41
|
+
TimecodePair as TimecodePair,
|
|
42
|
+
TimecodeLike as TimecodeLike,
|
|
43
|
+
Interpolation as Interpolation,
|
|
43
44
|
)
|
|
44
|
-
from scenedetect.platform import StrPath
|
|
45
|
-
from scenedetect.video_stream import VideoStream
|
|
45
|
+
from scenedetect.platform import StrPath as StrPath
|
|
46
|
+
from scenedetect.video_stream import VideoStream as VideoStream
|
|
47
|
+
from scenedetect.video_stream import VideoOpenFailure as VideoOpenFailure
|
|
46
48
|
from scenedetect.output import (
|
|
47
|
-
save_images,
|
|
48
|
-
split_video_ffmpeg,
|
|
49
|
-
split_video_mkvmerge,
|
|
50
|
-
is_ffmpeg_available,
|
|
51
|
-
is_mkvmerge_available,
|
|
52
|
-
write_scene_list,
|
|
53
|
-
write_scene_list_html,
|
|
54
|
-
PathFormatter,
|
|
55
|
-
VideoMetadata,
|
|
56
|
-
SceneMetadata,
|
|
49
|
+
save_images as save_images,
|
|
50
|
+
split_video_ffmpeg as split_video_ffmpeg,
|
|
51
|
+
split_video_mkvmerge as split_video_mkvmerge,
|
|
52
|
+
is_ffmpeg_available as is_ffmpeg_available,
|
|
53
|
+
is_mkvmerge_available as is_mkvmerge_available,
|
|
54
|
+
write_scene_list as write_scene_list,
|
|
55
|
+
write_scene_list_html as write_scene_list_html,
|
|
56
|
+
PathFormatter as PathFormatter,
|
|
57
|
+
VideoMetadata as VideoMetadata,
|
|
58
|
+
SceneMetadata as SceneMetadata,
|
|
57
59
|
)
|
|
58
|
-
from scenedetect.detector import SceneDetector
|
|
60
|
+
from scenedetect.detector import SceneDetector as SceneDetector
|
|
59
61
|
from scenedetect.detectors import (
|
|
60
|
-
ContentDetector,
|
|
61
|
-
AdaptiveDetector,
|
|
62
|
-
ThresholdDetector,
|
|
63
|
-
HistogramDetector,
|
|
64
|
-
HashDetector,
|
|
62
|
+
ContentDetector as ContentDetector,
|
|
63
|
+
AdaptiveDetector as AdaptiveDetector,
|
|
64
|
+
ThresholdDetector as ThresholdDetector,
|
|
65
|
+
HistogramDetector as HistogramDetector,
|
|
66
|
+
HashDetector as HashDetector,
|
|
65
67
|
)
|
|
66
68
|
from scenedetect.backends import (
|
|
67
|
-
AVAILABLE_BACKENDS,
|
|
68
|
-
VideoStreamCv2,
|
|
69
|
-
VideoStreamAv,
|
|
70
|
-
VideoStreamMoviePy,
|
|
71
|
-
VideoCaptureAdapter,
|
|
72
|
-
VideoStreamConcat,
|
|
73
|
-
SourceSpan,
|
|
69
|
+
AVAILABLE_BACKENDS as AVAILABLE_BACKENDS,
|
|
70
|
+
VideoStreamCv2 as VideoStreamCv2,
|
|
71
|
+
VideoStreamAv as VideoStreamAv,
|
|
72
|
+
VideoStreamMoviePy as VideoStreamMoviePy,
|
|
73
|
+
VideoCaptureAdapter as VideoCaptureAdapter,
|
|
74
|
+
VideoStreamConcat as VideoStreamConcat,
|
|
75
|
+
SourceSpan as SourceSpan,
|
|
74
76
|
)
|
|
75
|
-
from scenedetect.stats_manager import StatsManager
|
|
77
|
+
from scenedetect.stats_manager import StatsManager as StatsManager
|
|
78
|
+
from scenedetect.stats_manager import StatsFileCorrupt as StatsFileCorrupt
|
|
76
79
|
from scenedetect.scene_manager import SceneManager
|
|
77
80
|
|
|
78
81
|
# Used for module identification and when printing version & about info
|
|
79
82
|
# (e.g. calling `scenedetect version` or `scenedetect about`).
|
|
80
|
-
__version__ = "0.7.
|
|
83
|
+
__version__ = "0.7.2"
|
|
81
84
|
|
|
82
85
|
init_logger()
|
|
83
86
|
logger = getLogger("pyscenedetect")
|
|
@@ -113,8 +116,13 @@ def open_video(
|
|
|
113
116
|
:class:`VideoOpenFailure`: Constructing the VideoStream fails. If multiple backends have
|
|
114
117
|
been attempted, the error from the first backend will be returned.
|
|
115
118
|
"""
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
if framerate is not None:
|
|
120
|
+
warnings.warn(
|
|
121
|
+
"`framerate` is deprecated and scheduled for removal in v0.9; "
|
|
122
|
+
"use `frame_rate` instead.",
|
|
123
|
+
DeprecationWarning,
|
|
124
|
+
stacklevel=2,
|
|
125
|
+
)
|
|
118
126
|
if frame_rate is None:
|
|
119
127
|
frame_rate = framerate
|
|
120
128
|
# A list of paths is opened as a single concatenated stream. VideoStreamConcat handles
|
|
@@ -22,7 +22,6 @@ import inspect
|
|
|
22
22
|
import logging
|
|
23
23
|
import os
|
|
24
24
|
import os.path
|
|
25
|
-
import typing as ty
|
|
26
25
|
from copy import copy
|
|
27
26
|
|
|
28
27
|
import click
|
|
@@ -231,6 +230,7 @@ Global options (e.g. -i/--input, -c/--config) must be specified before any comma
|
|
|
231
230
|
)
|
|
232
231
|
@click.option(
|
|
233
232
|
"--frame-rate",
|
|
233
|
+
"--framerate",
|
|
234
234
|
"-f",
|
|
235
235
|
"frame_rate",
|
|
236
236
|
metavar="FPS",
|
|
@@ -238,15 +238,6 @@ Global options (e.g. -i/--input, -c/--config) must be specified before any comma
|
|
|
238
238
|
default=None,
|
|
239
239
|
help="Override frame rate with value as frames/sec.",
|
|
240
240
|
)
|
|
241
|
-
@click.option(
|
|
242
|
-
"--framerate",
|
|
243
|
-
"framerate_legacy",
|
|
244
|
-
metavar="FPS",
|
|
245
|
-
type=click.FLOAT,
|
|
246
|
-
default=None,
|
|
247
|
-
hidden=True,
|
|
248
|
-
help="[DEPRECATED] Use -f/--frame-rate instead.",
|
|
249
|
-
)
|
|
250
241
|
@click.option(
|
|
251
242
|
"--min-scene-len",
|
|
252
243
|
"-m",
|
|
@@ -347,7 +338,6 @@ def scenedetect(
|
|
|
347
338
|
stats: str | None,
|
|
348
339
|
config: str | None,
|
|
349
340
|
frame_rate: float | None,
|
|
350
|
-
framerate_legacy: float | None,
|
|
351
341
|
min_scene_len: str | None,
|
|
352
342
|
drop_short_scenes: bool | None,
|
|
353
343
|
merge_last_scene: bool | None,
|
|
@@ -362,13 +352,6 @@ def scenedetect(
|
|
|
362
352
|
ctx = ctx.obj
|
|
363
353
|
assert isinstance(ctx, CliContext)
|
|
364
354
|
|
|
365
|
-
# TODO(https://scenedetect.com/issue/548): emit DeprecationWarning when `--framerate`
|
|
366
|
-
# is used, once downstream users have had a release to migrate to `--frame-rate`.
|
|
367
|
-
if frame_rate is None:
|
|
368
|
-
frame_rate = framerate_legacy
|
|
369
|
-
elif framerate_legacy is not None:
|
|
370
|
-
logger.warning("Both --frame-rate and --framerate were specified; using --frame-rate.")
|
|
371
|
-
|
|
372
355
|
ctx.handle_options(
|
|
373
356
|
input_path=input,
|
|
374
357
|
output=output,
|
|
@@ -516,6 +499,19 @@ def time_command(
|
|
|
516
499
|
ctx.duration = ctx.parse_timecode(duration)
|
|
517
500
|
if ctx.start_time and ctx.end_time and (ctx.start_time + 1) > ctx.end_time:
|
|
518
501
|
raise click.BadParameter("-e/--end time must be greater than -s/--start")
|
|
502
|
+
# Reject start times past EOF before any backend seeks. OpenCV/PyAV previously
|
|
503
|
+
# continued and produced inconsistent errors; MoviePy raised SeekError (#380).
|
|
504
|
+
video_duration = ctx.video_stream.duration if ctx.video_stream is not None else None
|
|
505
|
+
if (
|
|
506
|
+
ctx.start_time is not None
|
|
507
|
+
and video_duration is not None
|
|
508
|
+
and video_duration > 0
|
|
509
|
+
and ctx.start_time >= video_duration
|
|
510
|
+
):
|
|
511
|
+
raise click.BadParameter(
|
|
512
|
+
"start time is beyond the end of the video",
|
|
513
|
+
param_hint="-s/--start",
|
|
514
|
+
)
|
|
519
515
|
|
|
520
516
|
|
|
521
517
|
DETECT_CONTENT_HELP = """Find fast cuts using differences in HSL (filtered).
|
|
@@ -780,6 +776,15 @@ Examples:
|
|
|
780
776
|
USER_CONFIG.get_help_string("detect-threshold", "add-last-scene")
|
|
781
777
|
),
|
|
782
778
|
)
|
|
779
|
+
@click.option(
|
|
780
|
+
"--min-out-length",
|
|
781
|
+
metavar="TIMECODE",
|
|
782
|
+
type=click.STRING,
|
|
783
|
+
default=None,
|
|
784
|
+
help="Minimum time spent faded out before a cut is allowed. Ignores shorter fades even when the minimum scene length is met. Also applies to a final fade-out, including the last processed frame. Zero allows fades of any duration. TIMECODE can be specified in frames (4), seconds with an `s` suffix (0.4s), or timecode (00:00:00.400).{}".format(
|
|
785
|
+
USER_CONFIG.get_help_string("detect-threshold", "min-out-length")
|
|
786
|
+
),
|
|
787
|
+
)
|
|
783
788
|
@click.option(
|
|
784
789
|
"--min-scene-len",
|
|
785
790
|
"-m",
|
|
@@ -800,6 +805,7 @@ def detect_threshold_command(
|
|
|
800
805
|
fade_bias: float | None,
|
|
801
806
|
add_last_scene: bool,
|
|
802
807
|
min_scene_len: str | None,
|
|
808
|
+
min_out_length: str | None,
|
|
803
809
|
):
|
|
804
810
|
ctx = ctx.obj
|
|
805
811
|
assert isinstance(ctx, CliContext)
|
|
@@ -808,6 +814,7 @@ def detect_threshold_command(
|
|
|
808
814
|
fade_bias=fade_bias,
|
|
809
815
|
add_last_scene=add_last_scene,
|
|
810
816
|
min_scene_len=min_scene_len,
|
|
817
|
+
min_out_length=min_out_length,
|
|
811
818
|
)
|
|
812
819
|
ctx.add_detector(ThresholdDetector, detector_args)
|
|
813
820
|
|
|
@@ -1714,6 +1721,48 @@ def save_qp_command(
|
|
|
1714
1721
|
ctx.add_command(cli_commands.save_qp, save_qp_args)
|
|
1715
1722
|
|
|
1716
1723
|
|
|
1724
|
+
SAVE_KEYFRAMES_HELP = """Save detected cuts using keyframe format v1.
|
|
1725
|
+
|
|
1726
|
+
Frame numbers are currently approximate for variable framerate (VFR) video.
|
|
1727
|
+
"""
|
|
1728
|
+
|
|
1729
|
+
|
|
1730
|
+
@click.command("save-keyframes", cls=Command, help=SAVE_KEYFRAMES_HELP)
|
|
1731
|
+
@click.option(
|
|
1732
|
+
"--filename",
|
|
1733
|
+
"-f",
|
|
1734
|
+
metavar="NAME",
|
|
1735
|
+
default=None,
|
|
1736
|
+
type=click.STRING,
|
|
1737
|
+
help="Filename format to use.{}".format(
|
|
1738
|
+
USER_CONFIG.get_help_string("save-keyframes", "filename")
|
|
1739
|
+
),
|
|
1740
|
+
)
|
|
1741
|
+
@click.option(
|
|
1742
|
+
"--output",
|
|
1743
|
+
"-o",
|
|
1744
|
+
metavar="DIR",
|
|
1745
|
+
type=click.Path(exists=False, dir_okay=True, writable=True, resolve_path=False),
|
|
1746
|
+
help="Output directory to save keyframes to. Overrides global option -o/--output.{}".format(
|
|
1747
|
+
USER_CONFIG.get_help_string("save-keyframes", "output", show_default=False)
|
|
1748
|
+
),
|
|
1749
|
+
)
|
|
1750
|
+
@click.pass_context
|
|
1751
|
+
def save_keyframes_command(
|
|
1752
|
+
ctx: click.Context,
|
|
1753
|
+
filename: str | None,
|
|
1754
|
+
output: str | None,
|
|
1755
|
+
):
|
|
1756
|
+
ctx = ctx.obj
|
|
1757
|
+
assert isinstance(ctx, CliContext)
|
|
1758
|
+
|
|
1759
|
+
save_keyframes_args = {
|
|
1760
|
+
"filename": ctx.config.get_value("save-keyframes", "filename", filename),
|
|
1761
|
+
"output": ctx.config.get_value("save-keyframes", "output", output),
|
|
1762
|
+
}
|
|
1763
|
+
ctx.add_command(cli_commands.save_keyframes, save_keyframes_args)
|
|
1764
|
+
|
|
1765
|
+
|
|
1717
1766
|
SAVE_FCP_HELP = """Save cuts in Final Cut Pro XML format (FCP7 xmeml or FCPX)."""
|
|
1718
1767
|
|
|
1719
1768
|
|
|
@@ -1858,6 +1907,7 @@ scenedetect.add_command(save_edl_command)
|
|
|
1858
1907
|
scenedetect.add_command(save_html_command)
|
|
1859
1908
|
scenedetect.add_command(save_images_command)
|
|
1860
1909
|
scenedetect.add_command(save_qp_command)
|
|
1910
|
+
scenedetect.add_command(save_keyframes_command)
|
|
1861
1911
|
scenedetect.add_command(save_fcp_command)
|
|
1862
1912
|
scenedetect.add_command(save_otio_command)
|
|
1863
1913
|
scenedetect.add_command(split_video_command)
|
|
@@ -102,6 +102,29 @@ def save_qp(
|
|
|
102
102
|
logger.info(f"QP file written to: {qp_path}")
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
def save_keyframes(
|
|
106
|
+
context: CliContext, scenes: SceneList, cuts: CutList, output: str, filename: str
|
|
107
|
+
):
|
|
108
|
+
"""Handler for the `save-keyframes` command."""
|
|
109
|
+
del scenes # We only use cuts for this handler.
|
|
110
|
+
assert context.video_stream is not None
|
|
111
|
+
keyframes_path = get_and_create_path(
|
|
112
|
+
Template(filename).safe_substitute(VIDEO_NAME=context.video_stream.name),
|
|
113
|
+
output,
|
|
114
|
+
)
|
|
115
|
+
with open(keyframes_path, "w") as keyframes_file:
|
|
116
|
+
keyframes_file.write("# keyframe format v1\n")
|
|
117
|
+
# The keyframe format v1 specification includes an FPS field, but Aegisub does not
|
|
118
|
+
# use it and documents `0` as the conventional value:
|
|
119
|
+
# https://aegisub.org/docs/latest/video/#keyframe-file-specification
|
|
120
|
+
keyframes_file.write("fps 0\n")
|
|
121
|
+
keyframes_file.write("0\n")
|
|
122
|
+
# TODO(https://scenedetect.com/issues/569): Frame numbers are approximate for VFR
|
|
123
|
+
# input until exact presentation-frame ordinals are tracked.
|
|
124
|
+
keyframes_file.writelines(f"{cut.frame_num}\n" for cut in cuts)
|
|
125
|
+
logger.info(f"Keyframes written to: {keyframes_path}")
|
|
126
|
+
|
|
127
|
+
|
|
105
128
|
def list_scenes(
|
|
106
129
|
context: CliContext,
|
|
107
130
|
scenes: SceneList,
|
|
@@ -379,17 +379,18 @@ CONFIG_MAP: ConfigDict = {
|
|
|
379
379
|
"detect-hash": {
|
|
380
380
|
"min-scene-len": TimecodeValue(0),
|
|
381
381
|
"lowpass": RangeValue(2, min_val=1, max_val=256),
|
|
382
|
-
"size": RangeValue(
|
|
383
|
-
"threshold": RangeValue(0.
|
|
382
|
+
"size": RangeValue(8, min_val=1, max_val=256),
|
|
383
|
+
"threshold": RangeValue(0.35, min_val=0.0, max_val=1.0),
|
|
384
384
|
},
|
|
385
385
|
"detect-hist": {
|
|
386
386
|
"min-scene-len": TimecodeValue(0),
|
|
387
|
-
"threshold": RangeValue(0.
|
|
388
|
-
"bins": RangeValue(
|
|
387
|
+
"threshold": RangeValue(0.20, min_val=0.0, max_val=1.0),
|
|
388
|
+
"bins": RangeValue(128, min_val=1, max_val=256),
|
|
389
389
|
},
|
|
390
390
|
"detect-threshold": {
|
|
391
391
|
"add-last-scene": True,
|
|
392
392
|
"fade-bias": RangeValue(0, min_val=-100.0, max_val=100.0),
|
|
393
|
+
"min-out-length": TimecodeValue("0.1s"),
|
|
393
394
|
"min-scene-len": TimecodeValue(0),
|
|
394
395
|
"threshold": RangeValue(12.0, min_val=0.0, max_val=255.0),
|
|
395
396
|
},
|
|
@@ -460,6 +461,10 @@ CONFIG_MAP: ConfigDict = {
|
|
|
460
461
|
"filename": "$VIDEO_NAME.qp",
|
|
461
462
|
"output": None,
|
|
462
463
|
},
|
|
464
|
+
"save-keyframes": {
|
|
465
|
+
"filename": "$VIDEO_NAME-keyframes.txt",
|
|
466
|
+
"output": None,
|
|
467
|
+
},
|
|
463
468
|
"save-fcp": {
|
|
464
469
|
"format": FcpFormat.FCPX,
|
|
465
470
|
"filename": "$VIDEO_NAME.xml",
|
|
@@ -405,6 +405,7 @@ class CliContext:
|
|
|
405
405
|
fade_bias: float | None = None,
|
|
406
406
|
add_last_scene: bool | None = None,
|
|
407
407
|
min_scene_len: str | None = None,
|
|
408
|
+
min_out_length: str | None = None,
|
|
408
409
|
) -> dict[str, ty.Any]:
|
|
409
410
|
"""Handle detect-threshold command options and return args to construct one with."""
|
|
410
411
|
|
|
@@ -415,6 +416,9 @@ class CliContext:
|
|
|
415
416
|
or self.config.get_value("detect-threshold", "add-last-scene"),
|
|
416
417
|
"fade_bias": self.config.get_value("detect-threshold", "fade-bias", fade_bias),
|
|
417
418
|
"min_scene_len": min_scene_len_frames,
|
|
419
|
+
"min_out_length": self.parse_timecode(
|
|
420
|
+
self.config.get_value("detect-threshold", "min-out-length", min_out_length)
|
|
421
|
+
),
|
|
418
422
|
"threshold": self.config.get_value("detect-threshold", "threshold", threshold),
|
|
419
423
|
}
|
|
420
424
|
|
|
@@ -131,7 +131,6 @@ def _detect(context: CliContext) -> tuple[SceneList, CutList] | None:
|
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
# Handle case where video failure is most likely due to multiple audio tracks (#179).
|
|
134
|
-
# TODO(https://scenedetect.com/issues/380): Ensure this does not erroneusly fire.
|
|
135
134
|
if num_frames <= 0 and isinstance(context.video_stream, VideoStreamCv2):
|
|
136
135
|
logger.critical(
|
|
137
136
|
"Failed to read any frames from video file. This could be caused by the video"
|