Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,48 @@ Title Case / sentence case of parameter labels across schemas — now more visib
side by side under headings, but a cosmetic sweep of several hundred strings that
should be its own change.

### Every pass says which VapourSynth calls it makes

Added 2026-08-19 on request: experts coming from Hybrid, StaxRip or AviSynth
recognise `FixChromaBleedingMod` or `MSRCP` instantly and could not tell what a
VapourBox pass was doing from its labels. Each pass now prints its calls in
**advanced mode**, and only there — a plugin name is not actionable for someone
who has not asked for that level, and advanced mode is the lever for exactly
that judgement. Field reference: **[docs/FILTER_SCHEMA.md](docs/FILTER_SCHEMA.md)**.

Two shapes, because filters come in two shapes:

- **One call per method** — the readout prints the selected method's own
`function`. That data already existed on every method and had **never been
rendered anywhere**; this was mostly a display gap, not a data one.
- **Composite passes** — Colour Correction, Chroma Fixes and Crop & Resize are
not one call. They declare an `implementation` list of everything they can
invoke, each entry optionally gated by an `activeWhen` that reuses the
`visibleWhen` matcher, and the readout shows the **whole repertoire with the
running calls emphasised**. Seeing the inactive ones is the point: it says
what the pass could do, not only what it is doing.

> **Colour Correction was the reported case and is the sharpest one.** It has no
> method dropdown at all (the inert one was removed in the panel audit), so
> before this there was nothing anywhere in the UI naming any of the six things
> it can run — `adjust.Tweak`, `std.Levels`, `haf.SmoothLevels`,
> `retinex.MSRCP`, and the two `PlaneStats`-driven automatic passes.

> **`activeWhen` takes more than one key, and that is load-bearing.**
> `applyLevels` chooses *between* `std.Levels` and `haf.SmoothLevels`, so each is
> gated on the pair `{applyLevels, smoothLevels}` and precisely one is ever
> emphasised. Gating both on `applyLevels` alone would claim the pass runs two
> levels operations. The same shape covers Chroma Fixes' automatic-supersedes-
> manual alignment, where both entries are `core.resize.Spline36` and only the
> `role` text tells them apart.

Three rules linted by `filter_schema_curation_test.dart`: no method leaves
`function` blank; a method declaring `"function": "custom"` must be explained by
an `implementation` list (`custom` is bookkeeping and is never displayed); and
every `activeWhen` key must name a real parameter, or the call reads as
permanently inactive — the same silent failure as a `visibleWhen` naming a
missing parameter.

### Presets are the other way a hidden setting arrives

A preset is the main route by which settings appear without anyone touching a
Expand Down
71 changes: 65 additions & 6 deletions app/assets/filters/core/chroma_fixes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.1.0",
"name": "Chroma Fixes",
"description": "Fix colour alignment, bleeding, rainbows and dot crawl",
"longDescription": "Repairs colour-specific damage from analog and composite video. It covers five separate problems, each with its own switch, in the order they appear below:\n\n**Alignment** \u2014 the colour sits sideways or up/down from the picture it belongs to. Correct it automatically or by hand; these are alternatives, not a pair.\n\n**Bleeding** \u2014 colour smears past the edge it belongs to, most obviously on strong reds.\n\n**Dot crawl** \u2014 dots crawling along sharp colour edges.\n\n**Rainbowing** \u2014 shimmering colour bands over fine detail.\n\n**Chroma combing** \u2014 leftover comb teeth in the colour planes.\n\nDot crawl and rainbowing each offer a second removal that compares neighbouring frames rather than working inside one. They reach different patterns, so they are worth combining rather than choosing between.\n\nTurn on only what you can actually see in the preview \u2014 every one of these costs some colour detail.",
"longDescription": "Repairs colour-specific damage from analog and composite video. It covers five separate problems, each with its own switch, in the order they appear below:\n\n**Alignment** the colour sits sideways or up/down from the picture it belongs to. Correct it automatically or by hand; these are alternatives, not a pair.\n\n**Bleeding** colour smears past the edge it belongs to, most obviously on strong reds.\n\n**Dot crawl** dots crawling along sharp colour edges.\n\n**Rainbowing** shimmering colour bands over fine detail.\n\n**Chroma combing** leftover comb teeth in the colour planes.\n\nDot crawl and rainbowing each offer a second removal that compares neighbouring frames rather than working inside one. They reach different patterns, so they are worth combining rather than choosing between.\n\nTurn on only what you can actually see in the preview every one of these costs some colour detail.",
"category": "cleanup",
"icon": "palette",
"order": 8,
Expand Down Expand Up @@ -69,7 +69,7 @@
"default": false,
"ui": {
"label": "Correct colour alignment automatically",
"description": "Measures how far the colour has slipped from the picture and shifts it back, so you do not have to guess it on a slider. If it cannot measure the source reliably it leaves the picture alone rather than guessing. This replaces the manual sliders \u2014 with it on, they are not used.",
"description": "Measures how far the colour has slipped from the picture and shifts it back, so you do not have to guess it on a slider. If it cannot measure the source reliably it leaves the picture alone rather than guessing. This replaces the manual sliders with it on, they are not used.",
"widget": "checkbox"
}
},
Expand Down Expand Up @@ -124,7 +124,7 @@
"default": false,
"ui": {
"label": "Correct colour alignment by hand (Y/C delay)",
"description": "Shift the colour back into place yourself. Use this when the automatic measurement finds nothing \u2014 very soft VHS colour is sometimes not measurable.",
"description": "Shift the colour back into place yourself. Use this when the automatic measurement finds nothing very soft VHS colour is sometimes not measurable.",
"widget": "checkbox",
"visibleWhen": {
"applyAutoChroma": false
Expand Down Expand Up @@ -229,7 +229,7 @@
},
"ui": {
"label": "Horizontal offset",
"description": "Whole-pixel colour offset the fix works from. Leave it alone unless the colour is misaligned as well as bleeding \u2014 the alignment controls above are the better tool for that.",
"description": "Whole-pixel colour offset the fix works from. Leave it alone unless the colour is misaligned as well as bleeding the alignment controls above are the better tool for that.",
"widget": "slider",
"visibleWhen": {
"applyChromaBleedingFix": true
Expand Down Expand Up @@ -391,7 +391,7 @@
"default": false,
"ui": {
"label": "Remove rainbow shimmer",
"description": "Shimmering colour bands over fine detail on composite captures \u2014 the companion to dot crawl, which sits along edges instead. Decided within each frame.",
"description": "Shimmering colour bands over fine detail on composite captures the companion to dot crawl, which sits along edges instead. Decided within each frame.",
"widget": "checkbox"
}
},
Expand Down Expand Up @@ -630,5 +630,64 @@
"import havsfunc as haf"
],
"generate": "custom"
}
},
"implementation": [
{
"function": "core.resize.Spline36",
"role": "chroma alignment, by a measured shift",
"activeWhen": {
"applyAutoChroma": true
}
},
{
"function": "core.resize.Spline36",
"role": "chroma alignment, by hand",
"activeWhen": {
"applyChromaShift": true,
"applyAutoChroma": false
}
},
{
"function": "haf.FixChromaBleedingMod",
"role": "colour bleeding past edges",
"activeWhen": {
"applyChromaBleedingFix": true
}
},
{
"function": "haf.LUTDeCrawl",
"role": "dot crawl",
"activeWhen": {
"applyDeCrawl": true
}
},
{
"function": "core.dedot.Dedot",
"role": "dot crawl, temporal",
"activeWhen": {
"applyDedot": true
}
},
{
"function": "haf.LUTDeRainbow",
"role": "rainbowing",
"activeWhen": {
"applyDeRainbow": true
}
},
{
"function": "core.bifrost.Bifrost",
"role": "rainbowing that only shows in motion",
"activeWhen": {
"applyBifrost": true
}
},
{
"function": "haf.Vinverse",
"role": "residual chroma combing",
"activeWhen": {
"applyVinverse": true
}
}
]
}
55 changes: 49 additions & 6 deletions app/assets/filters/core/color_correction.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.1.0",
"name": "Color Correction",
"description": "Adjust brightness, contrast, saturation, levels and white balance",
"longDescription": "Adjusts brightness, contrast, saturation, the black and white levels, and the colour cast.\n\nUse it to rescue washed-out or crushed transfers, to fix a capture made at the wrong levels (limited 16-235 read as full 0-255, or the reverse), or to lift colour from faded film. Small moves go a long way \u2014 check the before/after preview rather than judging by numbers.\n\nLevels and white balance can each be set **automatically** or **by hand**, and the two sit together in the same group. The automatic pass measures the picture and runs first; anything you then set by hand is applied on top of the corrected picture, not the original.",
"longDescription": "Adjusts brightness, contrast, saturation, the black and white levels, and the colour cast.\n\nUse it to rescue washed-out or crushed transfers, to fix a capture made at the wrong levels (limited 16-235 read as full 0-255, or the reverse), or to lift colour from faded film. Small moves go a long way check the before/after preview rather than judging by numbers.\n\nLevels and white balance can each be set **automatically** or **by hand**, and the two sit together in the same group. The automatic pass measures the picture and runs first; anything you then set by hand is applied on top of the corrected picture, not the original.",
"category": "color",
"icon": "tune",
"order": 7,
Expand Down Expand Up @@ -191,7 +191,7 @@
"default": false,
"ui": {
"label": "Set levels by hand",
"description": "Map the input range onto the output range yourself, and adjust gamma. With automatic levels on, only gamma is left to set \u2014 the automatic pass has already placed black and white."
"description": "Map the input range onto the output range yourself, and adjust gamma. With automatic levels on, only gamma is left to set the automatic pass has already placed black and white."
}
},
"gamma": {
Expand Down Expand Up @@ -317,7 +317,7 @@
"step": 0.05,
"ui": {
"label": "Strength",
"description": "Lower this to keep some of the original cast \u2014 useful when the cast is meant to be there, like firelight.",
"description": "Lower this to keep some of the original cast useful when the cast is meant to be there, like firelight.",
"widget": "slider",
"precision": 2,
"visibleWhen": {
Expand Down Expand Up @@ -352,7 +352,7 @@
},
"ui": {
"label": "Tint",
"description": "Green/magenta. Negative shifts toward green, positive toward magenta \u2014 the axis VHS and telecine casts usually sit on.",
"description": "Green/magenta. Negative shifts toward green, positive toward magenta the axis VHS and telecine casts usually sit on.",
"widget": "slider",
"precision": 0
}
Expand All @@ -362,7 +362,7 @@
"default": false,
"ui": {
"label": "Lift shadow detail",
"description": "Opens up detail hidden in dark areas of underexposed footage, by comparing each part of the picture to its surroundings rather than raising the black level. Brightness only \u2014 colour is untouched."
"description": "Opens up detail hidden in dark areas of underexposed footage, by comparing each part of the picture to its surroundings rather than raising the black level. Brightness only colour is untouched."
}
},
"shadowSigma": {
Expand Down Expand Up @@ -451,5 +451,48 @@
"import havsfunc as haf"
],
"generate": "custom"
}
},
"implementation": [
{
"function": "core.std.Levels",
"role": "automatic levels, measured per frame with core.std.PlaneStats",
"activeWhen": {
"applyAutoLevels": true
}
},
{
"function": "core.std.Expr",
"role": "automatic white balance, measured per frame with core.std.PlaneStats",
"activeWhen": {
"applyAutoWhiteBalance": true
}
},
{
"function": "adjust.Tweak",
"role": "brightness, contrast, saturation, hue"
},
{
"function": "core.std.Levels",
"role": "levels",
"activeWhen": {
"applyLevels": true,
"smoothLevels": false
}
},
{
"function": "haf.SmoothLevels",
"role": "levels, dithered and limited as it maps",
"activeWhen": {
"applyLevels": true,
"smoothLevels": true
}
},
{
"function": "core.retinex.MSRCP",
"role": "shadow detail, on the luma plane only",
"activeWhen": {
"applyShadowDetail": true
}
}
]
}
44 changes: 42 additions & 2 deletions app/assets/filters/core/crop_resize.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.2.0",
"name": "Crop & Resize",
"description": "Crop borders and resize or upscale video",
"longDescription": "Trims unwanted borders and changes the output resolution. Cropping happens first, then resizing.\n\nUse crop to cut the head-switching noise along the bottom of VHS captures and the black overscan edges of broadcast material \u2014 otherwise the encoder spends bitrate on them. Use resize for a target resolution, or the NNEDI3 upscaler for a much better 2x/4x enlargement than a plain kernel. Keep crop values even so they stay aligned with chroma subsampling.",
"longDescription": "Trims unwanted borders and changes the output resolution. Cropping happens first, then resizing.\n\nUse crop to cut the head-switching noise along the bottom of VHS captures and the black overscan edges of broadcast material otherwise the encoder spends bitrate on them. Use resize for a target resolution, or the NNEDI3 upscaler for a much better 2x/4x enlargement than a plain kernel. Keep crop values even so they stay aligned with chroma subsampling.",
"category": "transform",
"icon": "crop",
"order": 9,
Expand Down Expand Up @@ -752,5 +752,45 @@
"codeTemplate": {
"imports": [],
"generate": "custom"
}
},
"implementation": [
{
"function": "core.std.Crop",
"role": "cropping",
"activeWhen": {
"cropEnabled": true
}
},
{
"function": "core.resize.*",
"role": "scaling, with the kernel selected above",
"activeWhen": {
"resizeEnabled": true
}
},
{
"function": "core.znedi3.nnedi3",
"role": "edge-directed doubling (core.nnedi3.nnedi3 on ARM, which has NEON kernels)",
"activeWhen": {
"useIntegerUpscale": true,
"upscaleMethod": "nnedi3Rpow2"
}
},
{
"function": "core.eedi3m.EEDI3",
"role": "edge-directed doubling",
"activeWhen": {
"useIntegerUpscale": true,
"upscaleMethod": "eedi3Rpow2"
}
},
{
"function": "core.resize.Spline36",
"role": "integer doubling, without edge direction",
"activeWhen": {
"useIntegerUpscale": true,
"upscaleMethod": "spline36"
}
}
]
}
4 changes: 2 additions & 2 deletions app/assets/filters/core/frame_rate.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"name": "Frame Rate",
"description": "Convert between PAL and NTSC frame rates",
"longDescription": "Changes how many frames per second the output runs at. This is for standards conversion \u2014 a tape that was converted from NTSC to PAL (or the reverse) at some point in its life, and now needs to play at the right speed on your equipment.\n\nIt is deliberately not a \"make motion smooth\" feature. Interpolating a master to a higher rate invents frames that were never photographed, which makes the file a worse record of what was shot. Converting an already-converted tape is the opposite case: the damage is already in the source, and leaving it alone means either judder or a 4% speed error.\n\nMotion interpolation gives the smoothest result and is usually invisible on a pan, but it can warp edges where something passes in front of something else. Repeat frames invents nothing at all and judders instead \u2014 the honest choice for an archival master.\n\nThis pass runs last, after everything else.",
"longDescription": "Changes how many frames per second the output runs at. This is for standards conversion a tape that was converted from NTSC to PAL (or the reverse) at some point in its life, and now needs to play at the right speed on your equipment.\n\nIt is deliberately not a \"make motion smooth\" feature. Interpolating a master to a higher rate invents frames that were never photographed, which makes the file a worse record of what was shot. Converting an already-converted tape is the opposite case: the damage is already in the source, and leaving it alone means either judder or a 4% speed error.\n\nMotion interpolation gives the smoothest result and is usually invisible on a pan, but it can warp edges where something passes in front of something else. Repeat frames invents nothing at all and judders instead the honest choice for an archival master.\n\nThis pass runs last, after everything else.",
"category": "enhancement",
"methods": [
{
Expand All @@ -21,7 +21,7 @@
"id": "duplicate",
"name": "Repeat frames",
"description": "Repeats or drops whole frames. Invents nothing, but motion judders.",
"function": "havsfunc.ChangeFPS",
"function": "haf.ChangeFPS",
"parameters": []
}
],
Expand Down
Loading
Loading