ffmpeg-skill 1.4.10 → 1.4.11
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 +1 -1
- package/scripts/_common.py +15 -0
- package/scripts/batch.py +1 -1
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 (`1.4.
|
|
24
|
+
| `skill.version` | the npm / package.json version (`1.4.11`) | 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`
|
|
@@ -83,7 +83,7 @@ on, the line says so.
|
|
|
83
83
|
```json
|
|
84
84
|
{
|
|
85
85
|
"contract_version": "1.0",
|
|
86
|
-
"skill": {"id": "ffmpeg-skill", "version": "1.4.
|
|
86
|
+
"skill": {"id": "ffmpeg-skill", "version": "1.4.11", "execution_mode": "local", "kind": "execution",
|
|
87
87
|
"entrypoints": {"cli": "...", "mcp": "...", "contract": "...", "doctor": "..."},
|
|
88
88
|
"not_provided": ["AI reasoning", "decisions", "production plans", "project IR", "approvals", "network access", "transcription engine"]},
|
|
89
89
|
"requirements": {"python": ">=3.9 (standard library only)", "ffmpeg": ">=5.0", "ffprobe": ">=5.0"},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ffmpeg-skill",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.11",
|
|
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",
|
package/scripts/_common.py
CHANGED
|
@@ -437,6 +437,20 @@ def refuse_output_is_input(output: str, *inputs: str) -> None:
|
|
|
437
437
|
f"(the result would replace the source) -- choose a different --output/-o path", kind="input")
|
|
438
438
|
|
|
439
439
|
|
|
440
|
+
def _check_output_path(cmd: Sequence[str]) -> None:
|
|
441
|
+
"""An output whose directory does not exist, or that names a directory, is a caller mistake:
|
|
442
|
+
say so as `kind: input` before ffmpeg runs, instead of the muxer's "No such file or directory"
|
|
443
|
+
as `kind: ffmpeg` (which reads as an encoder failure) or an `OUTPUT_INVALID` after the fact."""
|
|
444
|
+
output = cmd[-1]
|
|
445
|
+
if output == "-" or output.startswith("pipe:") or output.startswith("-"):
|
|
446
|
+
return
|
|
447
|
+
if os.path.isdir(output):
|
|
448
|
+
die(f"output {output!r} is a directory; pass a file path (e.g. {os.path.join(output, 'result.mp4')!r})")
|
|
449
|
+
parent = os.path.dirname(os.path.abspath(output))
|
|
450
|
+
if not os.path.isdir(parent):
|
|
451
|
+
die(f"output directory {parent!r} does not exist; create it first (this tool never creates directories)")
|
|
452
|
+
|
|
453
|
+
|
|
440
454
|
def _check_existing_output(cmd: Sequence[str]) -> None:
|
|
441
455
|
"""An output path that already exists is someone's file: a previous result, a source the
|
|
442
456
|
agent mis-named, a deliverable from another run. ffmpeg's -y (which every command carries so
|
|
@@ -522,6 +536,7 @@ def run(cmd: Sequence[str], *, quiet: bool = False, check: bool = True) -> subpr
|
|
|
522
536
|
is_ffmpeg = _is_ffmpeg(cmd)
|
|
523
537
|
if is_ffmpeg:
|
|
524
538
|
_check_no_overwrite_input(cmd)
|
|
539
|
+
_check_output_path(cmd)
|
|
525
540
|
_check_existing_output(cmd)
|
|
526
541
|
STATE.commands.append(_cmdline(cmd))
|
|
527
542
|
if not quiet:
|
package/scripts/batch.py
CHANGED
|
@@ -144,7 +144,7 @@ def main() -> int:
|
|
|
144
144
|
args = ap.parse_args()
|
|
145
145
|
apply_common(args)
|
|
146
146
|
|
|
147
|
-
folder = Path(args.folder)
|
|
147
|
+
folder = Path(args.folder).resolve() # relative 'bdir' used to become bdir/bdir/out once joined with the default outdir
|
|
148
148
|
if not folder.is_dir():
|
|
149
149
|
die(f"not a folder: {folder}")
|
|
150
150
|
try:
|