ffmpeg-skill 2.12.0 → 2.12.1
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/docs/contract.md +2 -2
- package/package.json +2 -1
- package/scripts/look.py +10 -3
package/docs/contract.md
CHANGED
|
@@ -21,7 +21,7 @@ The contract is derived from the code that runs, not maintained beside it:
|
|
|
21
21
|
| Field | Meaning | Changes when |
|
|
22
22
|
|---|---|---|
|
|
23
23
|
| `contract_version` | shape of this document (`1.0`) | a key is renamed, removed or changes meaning |
|
|
24
|
-
| `skill.version` | the npm / package.json version (`2.12.
|
|
24
|
+
| `skill.version` | the npm / package.json version (`2.12.1`) | any release |
|
|
25
25
|
|
|
26
26
|
A release that adds a tool or a flag keeps `contract_version`; a breaking change to the
|
|
27
27
|
ToolSpec shape bumps it. Consumers pin on `contract_version` and read `skill.version`
|
|
@@ -125,7 +125,7 @@ JSON would have been rewritten for that. The flat keys, typed per tool in `outpu
|
|
|
125
125
|
"contract_version": "1.0",
|
|
126
126
|
"deprecated": [{"what": "running on Python 3.9 (the requirements.python floor)", "since": "2.10.9", "replacement": "Python 3.10 or later; ...", "removed_in": "3.0.0", "where": "behaviour"}],
|
|
127
127
|
"removed": [{"what": "...", "since": "1.10.0", "replacement": "...", "removed_in": "2.0.0", "where": "cli | json | mcp | behaviour"}],
|
|
128
|
-
"skill": {"id": "ffmpeg-skill", "version": "2.12.
|
|
128
|
+
"skill": {"id": "ffmpeg-skill", "version": "2.12.1", "execution_mode": "local", "kind": "execution",
|
|
129
129
|
"entrypoints": {"cli": "...", "mcp": "...", "contract": "...", "doctor": "..."},
|
|
130
130
|
"not_provided": ["AI reasoning", "decisions", "production plans", "project IR", "approvals", "network access", "transcription engine"]},
|
|
131
131
|
"requirements": {"python": ">=3.9 (standard library only; 3.9 deprecated since 2.10.9, removed in 3.0.0; 3.10+ recommended)", "ffmpeg": ">=5.0", "ffprobe": ">=5.0"},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ffmpeg-skill",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.1",
|
|
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",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
],
|
|
52
52
|
"scripts": {
|
|
53
53
|
"test": "python3 tests/test_all.py && python3 tests/test_contract.py",
|
|
54
|
+
"test:fast": "python3 tests/test_contract.py",
|
|
54
55
|
"release-check": "bash tests/release_check.sh",
|
|
55
56
|
"demo": "python3 demos/build.py",
|
|
56
57
|
"demo:pipeline": "bash examples/make_demo.sh",
|
package/scripts/look.py
CHANGED
|
@@ -28,7 +28,12 @@ def fmt_hms(sec: float) -> str:
|
|
|
28
28
|
return f"{int(h):02d}:{int(m):02d}:{s_:06.3f}"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def timecode_filter(font_prefix: str) -> str:
|
|
31
|
+
def timecode_filter(font_prefix: str, offset: float = 0.0) -> str:
|
|
32
|
+
"""Burned-in timecode. `offset` is added to each frame's pts: an input-side -ss resets
|
|
33
|
+
timestamps to 0 at the seek point, so a sheet that starts reading mid-slice must add the seek
|
|
34
|
+
back or every tile is labelled `offset` seconds early (the contact sheet below seeks step/2)."""
|
|
35
|
+
if offset > 0:
|
|
36
|
+
return f"drawtext=text='%{{pts\\:hms\\:{offset:.6f}}}':{font_prefix}{FONT}"
|
|
32
37
|
return f"drawtext=text='%{{pts\\:hms}}':{font_prefix}{FONT}"
|
|
33
38
|
|
|
34
39
|
|
|
@@ -256,10 +261,12 @@ def main() -> int:
|
|
|
256
261
|
tile_w = max(2, (args.width // cols) // 2 * 2)
|
|
257
262
|
out = args.output or os.path.join(outdir, f"{stem}_sheet.png")
|
|
258
263
|
# sample at the middle of each slice so the first/last tiles are not black lead-in/out frames
|
|
259
|
-
vf = (f"select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,{step * 0.98:.6f})',scale={tile_w}:-2{tc},"
|
|
260
|
-
f"tile={cols}x{rows}:padding=2:margin=2:color=0x202020")
|
|
261
264
|
# with only a handful of frames a mid-slice seek skips past them all: start at 0 instead
|
|
262
265
|
seek = 0.0 if frames_avail < 4 else step / 2
|
|
266
|
+
# the seek resets pts to 0, so the burned-in timecode gets the seek added back
|
|
267
|
+
tc_sheet = tc.replace(timecode_filter(font_prefix), timecode_filter(font_prefix, seek)) if seek else tc
|
|
268
|
+
vf = (f"select='isnan(prev_selected_t)+gte(t-prev_selected_t\\,{step * 0.98:.6f})',scale={tile_w}:-2{tc_sheet},"
|
|
269
|
+
f"tile={cols}x{rows}:padding=2:margin=2:color=0x202020")
|
|
263
270
|
cmd = ffmpeg_base() + ["-ss", f"{seek:.6f}", "-i", args.input, "-vf", vf, "-frames:v", "1", out]
|
|
264
271
|
run(cmd)
|
|
265
272
|
outputs.append(out)
|