ffmpeg-skill 1.4.1 → 1.4.2
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.
- package/package.json +1 -1
- package/references/process-pitfalls.md +10 -0
- package/scripts/color.py +24 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ffmpeg-skill",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "Agent Skill that gives coding agents (Claude Code, Cursor, Codex) a local video editor: 42 FFmpeg tools with a machine-readable contract, contract-derived MCP server, FFmpeg capability detection, probe-first / verify-last workflow. Cut, join, silence removal, fit, captions and karaoke, overlays, motion graphics, HDR to SDR, LUTs, audio clean-up and typed dynamics, sync with drift correction, multicam, loudness, delivery checks, project rendering, batch. No API keys, no cloud, no dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ffmpeg",
|
|
@@ -180,3 +180,13 @@ admin with Contents: read/write on this repo -- whose "Repository admin" bypass
|
|
|
180
180
|
push triggers workflows (GITHUB_TOKEN's do not), so the bump commit carries `[skip ci]`; the
|
|
181
181
|
tag, Release and npm publish all happen in the originating run. Rotate the PAT before it
|
|
182
182
|
expires or the next release fails at the same step, cleanly, before anything is published.
|
|
183
|
+
|
|
184
|
+
### A literal `[skip ci]` anywhere in a PR body skips every workflow on the squash merge
|
|
185
|
+
|
|
186
|
+
Found on the merge of #170 (2026-09-11). The PR body quoted the new bump-commit message
|
|
187
|
+
verbatim, including `[skip ci]`; a squash merge copies the PR body into the merge commit, and
|
|
188
|
+
GitHub honours the marker wherever it appears in the commit message. Nothing ran on `main` for
|
|
189
|
+
that merge -- no tests, no CodeQL, no release -- and the release only happened when the next PR
|
|
190
|
+
merged. Describe the marker in words in PR bodies and commit messages ("the skip-CI marker"),
|
|
191
|
+
or wrap it so it does not match, and after any merge that touches CI check that the push
|
|
192
|
+
actually triggered the expected runs.
|
package/scripts/color.py
CHANGED
|
@@ -112,26 +112,46 @@ def correction_chain(args: argparse.Namespace) -> str:
|
|
|
112
112
|
die(f"--levels-out-black {out_black} must be less than --levels-out-white {out_white}")
|
|
113
113
|
|
|
114
114
|
gm, rm, bm = -tint, tint / 2.0, tint / 2.0
|
|
115
|
-
|
|
115
|
+
rgb = [
|
|
116
116
|
f"exposure=exposure={exposure:g}",
|
|
117
117
|
f"colortemperature=temperature={temperature:g}",
|
|
118
118
|
f"colorbalance=rs={lift:g}:gs={lift:g}:bs={lift:g}:rm={rm:g}:gm={gm:g}:bm={bm:g}:rh={gain:g}:gh={gain:g}:bh={gain:g}",
|
|
119
|
-
f"eq=contrast={contrast:g}:saturation={saturation:g}:gamma={gamma:g}",
|
|
120
119
|
]
|
|
120
|
+
terms = [_rgb_stage(rgb), f"eq=contrast={contrast:g}:saturation={saturation:g}:gamma={gamma:g}"]
|
|
121
|
+
rgb2 = []
|
|
121
122
|
if (in_black, in_white, out_black, out_white) != (0, 255, 0, 255):
|
|
122
123
|
rimin, rimax = in_black / 255.0, in_white / 255.0
|
|
123
124
|
romin, romax = out_black / 255.0, out_white / 255.0
|
|
124
|
-
|
|
125
|
+
rgb2.append(
|
|
125
126
|
f"colorlevels=rimin={rimin:g}:gimin={rimin:g}:bimin={rimin:g}:"
|
|
126
127
|
f"rimax={rimax:g}:gimax={rimax:g}:bimax={rimax:g}:"
|
|
127
128
|
f"romin={romin:g}:gomin={romin:g}:bomin={romin:g}:"
|
|
128
129
|
f"romax={romax:g}:gomax={romax:g}:bomax={romax:g}"
|
|
129
130
|
)
|
|
130
131
|
if args.curves:
|
|
131
|
-
|
|
132
|
+
rgb2.append(f"curves=preset={args.curves}")
|
|
133
|
+
if rgb2:
|
|
134
|
+
terms.append(_rgb_stage(rgb2))
|
|
132
135
|
return ",".join(terms)
|
|
133
136
|
|
|
134
137
|
|
|
138
|
+
def _rgb_stage(filters: list) -> str:
|
|
139
|
+
"""Wrap a run of RGB-only filters in explicit, matching YUV<->RGB conversions.
|
|
140
|
+
|
|
141
|
+
exposure/colortemperature/colorbalance/colorlevels/curves take RGB, so libavfilter inserts a
|
|
142
|
+
swscale conversion on each side. Left to itself, the way in honours the frame's colour tag
|
|
143
|
+
(bt709 on any camera or export.py file) while the way back uses swscale's default matrix
|
|
144
|
+
(bt601): on a bt709-tagged source an all-defaults --correct came out 26 dB PSNR from its
|
|
145
|
+
input and ~8 % less saturated (#159). Untagged sources never showed it because both legs
|
|
146
|
+
then fall back to bt601 and cancel. Pinning both legs to the same matrix restores the
|
|
147
|
+
identity on every source (39 dB, the same as an untagged one always got); bt601 on both
|
|
148
|
+
sides measured better than bt709 on both (34 dB) because swscale's 601 path round-trips
|
|
149
|
+
8-bit 4:2:0 more exactly. The matrix here is only the working space of the conversion pair,
|
|
150
|
+
never a tag: the output carries the encoder's BT.709 tags as before."""
|
|
151
|
+
return ("scale=in_color_matrix=bt601,format=gbrpf32le," + ",".join(filters)
|
|
152
|
+
+ ",scale=out_color_matrix=bt601,format=yuv420p")
|
|
153
|
+
|
|
154
|
+
|
|
135
155
|
def hdr_to_sdr_chain(meta: dict, tonemap: str, peak: float, desat: float) -> str:
|
|
136
156
|
v = meta["video"]
|
|
137
157
|
trc = v.get("color_transfer") or "smpte2084"
|