A Python tool that scans video files for commercial break points (where the video goes to black and audio goes silent) and embeds chapter markers into a single output file. It can process one video or an entire directory of videos. A modern CustomTkinter desktop GUI is included, and the app can be packaged into a standalone executable with PyInstaller.
- Detects intervals of black frames using FFmpeg's
blackdetectfilter. - Detects silent audio intervals using FFmpeg's
silencedetectfilter. - Identifies commercial breaks where black video and silent audio overlap.
- Writes a single output file for each input with embedded chapter metadata (using FFmpeg's
ffmetadataformat). - Optionally removes the detected break intervals and concatenates the remaining content into one file.
- Python 3.8+
- FFmpeg (including
ffprobe) installed and on yourPATH - PyYAML
- CustomTkinter (for the GUI)
- PyInstaller (for building the standalone executable)
git clone https://github.com/kraftmatic/tv-video-chapter-tool.git
cd tv-video-chapter-tool
pip install -r requirements.txtA single-window CustomTkinter GUI is available in gui.py. It wraps the same processing engine and streams all output to a scrollable log window while running in a background thread.
python3 gui.py- Browse for a single input video or a directory of videos.
- Optionally choose an output folder; if left blank, the
/processedsubfolder is used automatically. - Toggle
remove_breaksand selectchapter_boundaryplacement. - Edit black-frame, silence, and break-detection tolerances in a tabbed settings panel.
- Live log output and progress bar.
- Open the output folder after processing.
On some macOS versions the system Python ships a tkinter build that aborts with macOS 15 (1507) or later required, have instead 15 (1506) !. The GUI now probes the interpreter in a child process and prints a clear error message if this happens. To actually run the GUI, use Python 3.11+ from python.org or Homebrew (brew install python@3.11).
The project includes a PyInstaller build script that produces a single --onefile --noconsole executable.
pip install -r requirements.txt
python3 build.pyAfter the build completes, the executable is available at:
- Linux:
dist/TVVideoChapterTool - Windows:
dist\\TVVideoChapterTool.exe - macOS:
dist/TVVideoChapterTool
You can also run PyInstaller directly:
python3 -m PyInstaller --onefile --noconsole --name TVVideoChapterTool --collect-data customtkinter gui.pyFFmpeg and ffprobe are not bundled in the executable; they must still be installed and available on the system's PATH.
Create a YAML configuration file (see config.yaml for an example):
# Path to a single video file or a directory of video files.
input: /path/to/video.mp4
# Optional output file or directory. If omitted, output goes to a "processed"
# subdirectory next to the input file(s): <input_dir>/processed/<name>_chapters.<ext>
# output: /path/to/output.mp4
# Where to place chapter boundaries relative to the detected break interval.
# - "end": new chapter starts right after the break (default).
# - "start": new chapter starts at the beginning of the break.
# - "midpoint": new chapter starts at the middle of the break.
# - "separate": commercial breaks are exposed as their own chapters.
chapter_boundary: end
# If true, the detected break intervals are removed and the remaining content is
# concatenated into a single file. Chapter markers are placed at each segment.
remove_breaks: false
# If true, output files keep the original source file name instead of appending _chapters.
preserve_original_filename: false
# If true, files that already contain chapter metadata are reprocessed (overwritten).
overwrite_existing_chapters: false
black:
min_duration: 0.5 # seconds; black frames must last at least this long
picture_threshold: 0.98 # fraction of pixels that must be black (0.0-1.0)
pixel_threshold: 0.15 # pixel luminance below which is considered black (0.0-1.0)
silence:
min_duration: 0.5 # seconds; audio must be silent at least this long
noise_threshold: -60 # dB; anything at or below this is treated as silence
break:
min_duration: 1.0 # overlap of black + silence must last this long
merge_gap_seconds: 0.5 # merge adjacent breaks if separated by less than this
max_duration: 300 # ignore breaks longer than this (e.g. end credits)
edge_buffer_seconds: 5.0 # ignore breaks that start in the first N seconds or end in the last N secondsblack.pixel_thresholdis relative to the pixel luminance range (0.0 = completely black, 1.0 = maximum). Lower values are more sensitive to dark video.black.picture_thresholdis the fraction of the frame that must be belowpixel_thresholdfor the frame to count as black.silence.noise_thresholdis in dB; anything at or below this value is considered silence.break.min_durationcontrols how long the black+silent overlap must be to be considered a real commercial break.break.edge_buffer_secondsprevents false positives at the very beginning and end of the video (e.g. intros, credits, or fade-ins/outs).
python3 video_chapter_tool.py -c config.yamlIf output is omitted in config.yaml, the result is written to processed/<input_name>_chapters.<ext>. Set preserve_original_filename: true to keep the original name.
Set input to a directory instead of a file:
input: /path/to/videosAll supported video files (.mp4, .m4v, .mkv, .mov, .avi, .webm, .ts, .m2ts, .mpg, .mpeg) in the top-level of the directory are processed. Outputs are written to <input_dir>/processed/. Set preserve_original_filename: true to keep each source file's name.
# override input file
python3 video_chapter_tool.py -c config.yaml -i /path/to/video.mp4
# override output file
python3 video_chapter_tool.py -c config.yaml -i /path/to/video.mp4 -o /path/to/output.mp4
# override input directory and output directory
python3 video_chapter_tool.py -c config.yaml -i /path/to/videos -o /path/to/output_dir
# verbose logging
python3 video_chapter_tool.py -c config.yaml -v
# keep original file name
python3 video_chapter_tool.py -c config.yaml --preserve-original-filename
# overwrite files that already have chapters
python3 video_chapter_tool.py -c config.yaml --overwrite-existing-chaptersSet remove_breaks: true in config.yaml. The tool will cut out the detected break intervals, concatenate the remaining segments, and embed chapter markers at each segment boundary.
Set preserve_original_filename: true in config.yaml (or check the box in the GUI). Output files will keep the original source file name instead of appending _chapters.
By default, files that already contain chapter metadata are skipped and copied unchanged. Set overwrite_existing_chapters: true in config.yaml (or check the box in the GUI) to reprocess them instead. When enabled, existing chapters are stripped and new chapters are generated from the detected breaks.
| Input | Output config | preserve_original_filename |
Result |
|---|---|---|---|
| Single file | omitted | false | <input_dir>/processed/<name>_chapters.<ext> |
| Single file | omitted | true | <input_dir>/processed/<name>.<ext> |
| Single file | file path | - | exactly that file path |
| Single file | directory path | false | <output_dir>/<name>_chapters.<ext> |
| Single file | directory path | true | <output_dir>/<name>.<ext> |
| Directory | omitted | false | <input_dir>/processed/<name>_chapters.<ext> for each file |
| Directory | omitted | true | <input_dir>/processed/<name>.<ext> for each file |
| Directory | directory path | false | <output_dir>/<name>_chapters.<ext> for each file |
| Directory | directory path | true | <output_dir>/<name>.<ext> for each file |
| Directory | file path | - | error: a directory is required for batch input |
- The tool copies video and audio streams by default (
-c copy), so processing is fast and lossless. - Chapter metadata is embedded via FFmpeg's
ffmetadatamuxer. It works well with MP4, MKV, and MOV containers. - When
remove_breaksis true, segment cuts are made with-c copy, which may land on the nearest keyframe. If you need frame-accurate cuts, re-encode with a different ffmpeg command instead. - Make sure FFmpeg and ffprobe are installed and available on your
PATH.
This project is provided as-is for personal video-library management.