AdSlicer is an archival boundary-detection and segment-slicing application built for long-form VHS captures, analog transfers, and TV recordings. It analyzes candidate boundaries, identifies likely commercial blocks, and prepares a reviewable export plan that outputs:
- A clean show file (ads removed)
- A folder of isolated commercial clips
- Full diagnostic logs in JSON, CSV, and EDL
- ffmeta chapter markers for media player navigation
- A structured ML dataset (
dataset.jsonl) with 74 per-segment feature columns - A run manifest recording all parameters and detection statistics
The pipeline is self-contained (ffmpeg bundled), batch-friendly, and designed for noisy analog sources where black slugs vary in length and clarity.
Topics
| πΏ | Installation β Download a compiled application or build AdSlicer yourself |
| β | Tips & Tricks β Practical advice for faster review and safer exports |
| βοΈ | How It Works β Detection pipeline, confidence scoring, export modes |
| βΆ | Quick Start β Get from launch to a clean show master |
| π§ | All Parameters β Complete parameter reference |
| πΎ | Preset System β Built-in presets, saving, and file format |
| π | Tuning Guide β Fixing false positives, missed breaks, noisy tape |
| π | ML Dataset Output β The 74-column dataset.jsonl format |
| π‘ | Use Cases β Archiving, batch processing, ML, compilations |
Installation
There are two supported ways to install AdSlicer. Most users should download a compiled release. Developers, contributors, and users who need a custom build can compile the application from source.
Download the Compiled Application
Open the projectβs GitHub Releases page and download the newest package for your computer. The release build includes the AdSlicer application and its required FFmpeg/FFprobe sidecars.
- No Rust or Tauri installation required
- No separate FFmpeg setup required
- Best choice for archivists and everyday users
Build AdSlicer Yourself
Clone the repository, install the Rust/Tauri requirements, prepare the bundled media binaries, and build the target for your platform.
- Useful for development and custom modifications
- Produces platform-native application bundles
- Requires Rust, Cargo, Tauri CLI, and build tools
Compiled Release Workflow
- Open GitHub Releases.
- Choose the newest stable release.
- Download the package matching your operating system.
- Extract or mount the package, then move AdSlicer into your normal Applications or Programs location.
- Launch AdSlicer and choose an input video plus an output directory.
Build-from-Source Summary
git clone https://github.com/schwwaaa/AdSlicer.git cd AdSlicer chmod +x build.sh ./build.sh setup-bins ./build.sh mac-universal # macOS universal application # or ./build.sh windows # Windows x86_64
See Building from Source for prerequisites, development mode, sidecar setup, and target-specific commands.
Quick Start
1. Choose your input
Select a single video file, or switch to Batch Folder mode and select a directory. All matching video files inside will be queued.
2. Set your output folder
A subfolder is created per input file β you'll never lose track of which output came from which tape. Results are never overwritten; re-processing appends _1, _2, etc.
3. Pick a preset
Open the Presets menu and choose the one closest to your source material. See Preset System for descriptions.
4. Dry run first
Enable Dry Run before committing to a full export. This runs the full detection pipeline and writes all logs, but cuts no media. Review detect.json and check dataset.jsonl for cuts where only sig_black_boundary fired and confidence is below 0.90 β those are the weakest detections and worth inspecting first.
5. Final export
Disable Dry Run. Enable Re-encode for frame-accurate archival cuts (H.264/AAC). Run.
Recommended workflow: 1. Enable Dry Run 2. Review activity log and detect.json 3. Adjust parameters if needed, re-run dry 4. Disable Dry Run, enable Re-encode 5. Run final export
-c copy for speed. Stream copy snaps to the nearest keyframe β a few frames of error at each boundary. Enable reencode for surgical precision.Output Structure
Embedding chapter markers
Keep segments become Content N chapters; commercial blocks become Advertisement N. Embed into the show file:
ffmpeg -i show.mp4 -i logs/chapters.ffmeta \ -map_metadata 1 -c copy show_with_chapters.mp4
Recognized by VLC, mpv, Kodi, and any player that reads ffmpeg metadata.
How It Works
Each commercial candidate passes through four independent detection passes in sequence. Every signal that fires is recorded against the interval and reflected in its confidence score.
Detection Passes
Uses ffmpeg
blackdetect to locate near-black frames. Segments shorter than blackMinDur are discarded. Segments within mergeGap seconds are merged.Uses ffmpeg
silencedetect. Candidates overlapping a silence segment by β₯ 0.5 s receive silence_overlap and a +0.05 confidence boost. Set silenceNoiseDb β₯ 0 to disable.Uses ffmpeg
showinfo to compute per-frame luma stddev. Frames with stddev β€ uniformMaxStddev are classified as uniform slates. Candidates overlapping β₯ 0.3 s receive uniform_overlap and +0.04 boost. Set uniformMaxStddev to 0 to disable.Uses ffmpeg
select=scene. Blocks exceeding 1.3Γ the file average scene rate receive high_scene_rate and +0.04 boost. Set sceneThreshold to 0 to disable.Scoring Guards
| Guard | Parameter | Comskip equivalent |
|---|---|---|
| Uncorroborated penalty | automatic | punish_modifier |
| Minimum show segment | minShowSegment | min_show_segment_length |
| Edge protection | alwaysKeepFirst / alwaysKeepLast | always_keep_first/last_seconds |
| 30s boundary snapping | requireDiv5 | require_div5 |
| Asymmetric trim | removeBefore / removeAfter | remove_before / remove_after |
Confidence Score
Every CutInterval carries a confidence float (0.0β1.0) and a signals list. The activity log renders confidence as a star rating:
| Score | Display | Meaning |
|---|---|---|
| β₯ 1.0 | β β β | Multiple corroborating signals |
| β₯ 0.8 | β β β | At least one corroborating signal |
| < 0.8 | β ββ | Black boundary only β no corroboration |
All Parameters
Input / Output
| Parameter | Type | Description |
|---|---|---|
| inputMode | singleFile | batchDir | Process one file or a whole folder |
| inputPath | path | Input file or folder path |
| glob | pattern | Comma-separated globs for batch mode (e.g. *.mp4,*.mov,*.dv) |
| outdir | path | Base output directory β a subfolder is created per input file |
Black Frame Detection
| Parameter | Default | Description |
|---|---|---|
| blackMinDur | 0.10 s | Minimum black segment duration. Shorter flashes are discarded. |
| pixTh | 0.08 | Pixel luma threshold for blackdetect. Lower = stricter black definition. |
| picTh | 0.98 | Fraction of pixels per frame that must be below pixTh. |
| mergeGap | 1.5 s | Merge black segments separated by β€ this gap. Prevents flickering slugs from splitting boundaries. |
Cut Behaviour
| Parameter | Default | Description |
|---|---|---|
| edgePadPre | 0.20 s | Padding added before each cut boundary. |
| edgePadPost | 0.06 s | Padding added after each cut boundary. |
| minCommercial | 5 s | Minimum gap to classify as a commercial break. |
| maxCommercial | 240 s | Maximum gap to classify as a commercial break. |
| includeBlack | false | Include surrounding black frames inside exported commercial clips. |
| reencode | false | Re-encode output with H.264/AAC for frame-accurate cuts. |
| dryRun | false | Write logs only β no media files are created. |
Advanced Detection (Comskip-derived)
| Parameter | Default | Comskip equiv. | Description |
|---|---|---|---|
| silenceNoiseDb | -40 dB | max_silence | Audio noise floor. Set β₯ 0 to disable silence detection. |
| silenceMinDur | 0.5 s | min_silence | Minimum silence duration to register as a segment. |
| minShowSegment | 30 s | min_show_segment_length | Minimum keep-segment length. Cuts that would leave shorter keeps are demoted. |
| alwaysKeepFirst | 0 s | always_keep_first_seconds | Hard-protect first N seconds from being cut. |
| alwaysKeepLast | 0 s | always_keep_last_seconds | Hard-protect last N seconds from being cut. |
| uniformMaxStddev | 8.0 | non_uniformity | Luma stddev ceiling for uniform frame detection. Set to 0 to disable. |
| sceneThreshold | 0.4 | schange_threshold | Scene change sensitivity. Set to 0 to disable. |
| removeBefore | 0 s | remove_before | Trim from the content side of each cut. |
| removeAfter | 0 s | remove_after | Trim from the ad side of each cut. |
| requireDiv5 | false | require_div5 | Snap or drop candidates not within 3 s of a 30-second multiple. |
Verbosity
| Value | Output |
|---|---|
| 0 | Errors only |
| 1 | Milestones + raw blackdetect/silencedetect logs written to logs/ |
| 2 | Full step-by-step + all raw filter logs written to logs/ |
Preset System
AdSlicer ships with three built-in presets and a full save/load system.
Built-in Presets
| File | Purpose |
|---|---|
| default.json | Balanced starting point for typical VHS |
| vhs_noisy.json | Loose thresholds for degraded/worn tape |
| broadcast_strict.json | Strict thresholds with 30s snapping for clean off-air captures |
Preset Menu
Presets ββ BUILT-IN ββββββββββββββ Broadcast strict Default VHS noisy ββ MY PRESETS ββββββββββββ my_custom_settings ββββββββββββββββββββββββββ Save Current as Presetβ¦ ββββββββββββββββββββββββββ Open User Presets Folderβ¦ Reload Presets
User Preset Locations
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/net.schwwaaa.adslice/presets/ |
| Windows | %APPDATA%\net.schwwaaa.adslice\presets\ |
| Linux | ~/.config/net.schwwaaa.adslice/presets/ |
Use Presets β Open User Presets Folderβ¦ to open this location. Drop any .json file there and use Reload Presets to make it appear in the menu.
Preset File Format
Plain JSON. _preset sets the menu label; _description sets the tooltip. Unrecognized keys are silently ignored.
{
"_preset": "My custom VHS settings",
"_description": "Tuned for my specific deck and capture card.",
"blackMinDur": 0.10,
"pixTh": 0.08,
"picTh": 0.98,
"mergeGap": 1.5,
"minCommercial": 5,
"maxCommercial": 240,
"silenceNoiseDb": -40,
"requireDiv5": false
}
Adding a Built-in Preset to the Build
Drop a .json file into src-tauri/presets/ and rebuild. The tauri.conf.json resources glob picks it up β no code changes needed.
Tips & Tricks
AdSlicer works best as a review-assisted archival tool: analyze first, inspect the uncertain boundaries, then render only after the plan looks correct. These practices reduce accidental content loss and make large tape collections much faster to process.
Dry Run Before Every New Source Type
Run detection without exporting media when you change tape decks, capture hardware, channels, decades, or recording quality. One short dry run is cheaper than re-rendering a multi-hour tape.
Review Weak Boundaries First
Start with short intervals, low-confidence candidates, and cuts supported only by sig_black_boundary. These are the areas most likely to need manual inspection or parameter adjustment.
Tune One Representative Tape
Before processing a folder, choose one recording that represents the batch. Tune that file, save the settings as a preset, and then apply the preset to the remaining captures.
Keep the Original Capture Untouched
Export into a separate project folder. Preserve the full source recording as the archival master, then treat the clean show and isolated commercials as derived access files.
Fast, Safe Review Workflow
- Import the full recording.
- Run detection with Dry Run enabled.
- Inspect suspect short intervals and low-confidence boundaries first.
- Adjust only the settings needed for the source.
- Repeat the dry run until the detection plan is trustworthy.
- Enable Re-encode for the final frame-accurate archival export.
- Sort the rendered outputs into shows, commercials, promos, station IDs, and other archival categories.
Choose Speed or Precision Intentionally
| Goal | Recommended Mode | Why |
|---|---|---|
| Test a detection plan | Dry Run | Writes the plan and diagnostics without spending time rendering media. |
| Make a quick review copy | Stream copy | Very fast, but boundaries may snap to nearby keyframes. |
| Create the final archive output | Re-encode | Provides frame-accurate cuts and consistent H.264/AAC output. |
| Process many related tapes | Saved preset + batch mode | Reuses a tested configuration across a consistent collection. |
Source-Specific Tricks
Noisy or Near-Black VHS Transitions
- Raise
pixThslightly and lowerpicThso analog black does not have to be perfectly clean. - Increase
mergeGapwhen a black separator flickers or contains brief tracking noise. - Keep
requireDiv5disabled when tape timing has drifted away from exact broadcast durations.
Protect Openings and End Credits
- Use
alwaysKeepFirstto protect cold opens, station intros, and leading material. - Use
alwaysKeepLastto protect credits, post-credit tags, and tape-end material. - Increase
minShowSegmentwhen short dark scenes are being mistaken for breaks.
Organize the Output Immediately
- Keep the generated
logs/folder with every processed tape; it is the audit trail for how the result was produced. - Rename or catalog commercials only after the automatic segmentation is complete.
- Use
chapters.ffmeta, EDL, CSV, or JSON output when another archival or editorial tool needs the boundary data.
Tuning Guide
Thresholds may require tuning for darker or noisier analog captures. Always Dry Run first and review dataset.jsonl before committing to export.
Too many false positives (content being cut)
- Raise
blackMinDur(0.15β0.25) β require longer slugs - Raise
picTh(0.99) β require nearly pure black frames - Increase
minShowSegment(60β120 s) β prevent short content being consumed - Enable
requireDiv5for clean broadcast β non-multiples of 30 s are not real ad breaks - Raise
minCommercialβ filter breaks too short to be real commercials - Check
dataset.jsonlfor cuts where onlysig_black_boundaryfired β weakest detections
Missed commercials (breaks not detected)
- Lower
blackMinDur(0.06β0.08) β accept shorter slugs - Raise
pixTh(0.10β0.14) β more permissive black definition - Lower
picTh(0.90β0.95) β allow noisier black frames - Increase
mergeGapfor flickering VHS slug patterns - Lower
sceneThreshold(0.25β0.35) β catch more cuts within blocks
Noisy or degraded VHS
- Raise
pixTh+ lowerpicThβ the standard analog adjustment - Raise
uniformMaxStddev(12β18) β VHS black slugs are never truly uniform - Set
removeBefore 0.1β recovers content clipped by ambiguous slug entry points - Disable
requireDiv5β VHS timing is irregular - Lower
silenceNoiseDbto -35 dB β VHS audio floor is noisier
Clean off-air broadcast
- Enable
requireDiv5β US TV commercials are exact 15/30/60/90 s units - Set
alwaysKeepFirst 15andalwaysKeepLast 15β protect cold opens and credits - Lower
uniformMaxStddevto 5β6 β broadcast slates are near-perfect - Raise
sceneThresholdto 0.45 β hard cuts only; avoid dissolve false positives
Recommended workflow
- Run with
dryRunenabled β reviewdetect.jsonand the activity log - Check
dataset.jsonlβ confidence below 0.90 or onlysig_black_boundaryfiring is worth inspecting - Adjust parameters and re-run dry until the plan is correct
- Remove
dryRunand enablereencodefor final archival export
ML Dataset Output
Every run writes logs/dataset.jsonl β one JSON object per line, one line per segment, 74 columns. Load it directly:
import pandas as pd
df = pd.read_json("logs/dataset.jsonl", lines=True)
Column Groups
Identity (4 cols)
| Column | Type | Description |
|---|---|---|
| run_id | string | ISO-8601 UTC timestamp of the processing run |
| source_file | string | Input filename stem |
| segment_index | int | Index within this label type |
| timeline_position | int | Sequential position in the overall file timeline |
Timing (9 cols)
| Column | Description |
|---|---|
| start_s, end_s, dur_s | Absolute timestamps and duration in seconds |
| start_norm, end_norm, dur_norm | Position and duration as fraction of file length (0β1) |
| offset_from_start_s | Seconds from start of recording |
| offset_from_end_s | Seconds from end of recording |
Signal Indicators β all 0.0 or 1.0 (11 cols)
| Column | Fires when |
|---|---|
| sig_black_boundary | Interval is bracketed by a black slug |
| sig_within_commercial_range | Duration within [min_commercial, max_commercial] |
| sig_silence_overlap | Silence corroboration fired |
| sig_uniform_overlap | Uniform frame corroboration fired |
| sig_high_scene_rate | Scene rate exceeds 1.3Γ file average |
| sig_demoted_min_show_segment | Was a commercial candidate, demoted by show guard |
| sig_always_keep_first/last | Interval falls within the always-keep window |
| sig_content_between_commercials | Standard keep between two commercial blocks |
| sig_div5_snapped | Boundary was snapped to a 30 s multiple |
Classification (3 cols)
| Column | Values |
|---|---|
| label | "commercial" or "keep" |
| label_int | 1 = commercial, 0 = keep |
| confidence | Detection confidence score 0.0β1.0 |
Usage Examples
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
df = pd.read_json("logs/dataset.jsonl", lines=True)
# Feature matrix
X = df[[
"dur_s", "dur_norm", "start_norm",
"black_left_dur_s", "black_right_dur_s",
"silence_coverage", "has_silence_overlap",
"scene_change_rate", "scene_change_rate_vs_avg",
"sig_black_boundary", "sig_silence_overlap",
]]
y = df["label_int"]
# Compare across parameter tuning runs
runs = pd.concat([
pd.read_json("run1/logs/dataset.jsonl", lines=True),
pd.read_json("run2/logs/dataset.jsonl", lines=True),
])
runs.groupby("param_scene_threshold")["run_commercial_ratio"].mean()
# Inspect low-confidence cuts
df[(df["label"] == "commercial") & (df["confidence"] < 0.9)]
param_* columns are fully denormalised. Individual files can be concatenated across runs and remain independently queryable.Use Cases
AdSlicer is designed for recordings where the commercials, program material, transitions, and broadcast artifacts are all historically usefulβbut need to be separated into practical, reviewable outputs.
Preserving a Complete Broadcast and a Clean Viewing Copy
A collector digitizes an off-air movie, sports broadcast, or television block from VHS. AdSlicer preserves the original capture, creates a clean show master, isolates commercial blocks, and writes detection metadata so both versions remain traceable to the same source.
Processing Large VHS and Broadcast Collections
An archive, library, preservation group, or individual collector has hundreds of related tapes. A representative tape is tuned first, its settings are saved as a preset, and batch mode then produces consistent folder structures and diagnostics across the collection.
Recovering Vintage Commercials, Promos, and Station IDs
Commercial blocks can be exported as separate clips for historical research, brand studies, broadcast design reference, compilation editing, or cataloging. The program is not merely deleting advertisementsβit is separating two valuable classes of archival material.
Preparing Ad-Free Program Masters
Editors and collectors can create watchable copies of cartoons, movies, music programs, news, and episodic television while retaining the source capture and isolated advertisements elsewhere in the project.
Boundary Review and Quality Control
Detection logs identify short, weak, or unusual candidate intervals that deserve attention. Reviewers can focus on suspect boundaries instead of manually scrubbing through every hour of the recording.
Building Research and Machine-Learning Datasets
The structured dataset.jsonl, detect.json, CSV, EDL, and run manifest outputs provide labeled timing, confidence, signal, and parameter data for commercial-detection research, comparative testing, and future model development.
Creating Editorial Compilations
Once commercial blocks are extracted, an editor can sort clips by brand, year, network, product category, visual style, or campaign. The same workflow also supports promo reels, station-identification collections, and historical broadcast packages.
Integrating With Other Archive Tools
EDL, chapter metadata, CSV, and JSON outputs can be handed to media players, command-line processes, database ingest tools, or nonlinear editors without forcing every downstream system to repeat the detection pass.
Building from Source
This path is intended for developers, contributors, and users who need a custom application build. Users who only want to run AdSlicer should use the compiled package described under Installation.
Prerequisites
- Git
- Rust and Cargo
- Tauri CLI
- Platform build tools for macOS or Windows
curlorwget, plusunzip
Clone the repository
git clone https://github.com/schwwaaa/AdSlicer.git cd AdSlicer chmod +x build.sh
First-time sidecar setup
Download the static FFmpeg and FFprobe binaries that Tauri bundles with the application. The target-specific filenames are required before compilation.
./build.sh setup-bins
Dev mode
cd src-tauri cargo tauri dev
Release builds
| Command | Target |
|---|---|
| ./build.sh | Auto-detect current OS |
| ./build.sh mac-universal | macOS arm64 + x86_64 fat binary |
| ./build.sh mac-arm | macOS Apple Silicon only |
| ./build.sh mac-x86 | macOS Intel only |
| ./build.sh windows | Windows x86_64 |
ffmpeg and ffprobe are bundled automatically. Users need no external dependencies.
Adding a built-in preset
Drop a .json file into src-tauri/presets/ and run ./build.sh. The tauri.conf.json resources glob picks it up β no code changes needed.