@assemblyline-agents/ffmpeg 0.1.0
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/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/frame-preview.d.ts +8 -0
- package/dist/frame-preview.d.ts.map +1 -0
- package/dist/frame-preview.js +82 -0
- package/dist/frame-preview.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +216 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/process.d.ts +9 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +43 -0
- package/dist/process.js.map +1 -0
- package/dist/tool-input.d.ts +9 -0
- package/dist/tool-input.d.ts.map +1 -0
- package/dist/tool-input.js +48 -0
- package/dist/tool-input.js.map +1 -0
- package/dist/tools.d.ts +3 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +124 -0
- package/dist/tools.js.map +1 -0
- package/dist/workspace.d.ts +11 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +59 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +51 -0
- package/skills/ffmpeg/SKILL.md +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenEve contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @assemblyline-agents/ffmpeg
|
|
2
|
+
|
|
3
|
+
Official Assembly Line stdio connection for workspace-scoped FFmpeg operations.
|
|
4
|
+
|
|
5
|
+
FFmpeg is a host executable, not a remote account connection. This package
|
|
6
|
+
bundles a small MCP server that exposes reviewed media operations and launches
|
|
7
|
+
`ffmpeg` or `ffprobe` without a shell. It never exposes arbitrary command-line
|
|
8
|
+
flags to the model.
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
Install FFmpeg on every runtime host:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
# macOS
|
|
16
|
+
brew install ffmpeg
|
|
17
|
+
|
|
18
|
+
# Debian/Ubuntu
|
|
19
|
+
apt-get update && apt-get install -y ffmpeg
|
|
20
|
+
|
|
21
|
+
assembly-line add ffmpeg agent
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Enable editing explicitly:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { defineFfmpegConnection } from "@assemblyline-agents/ffmpeg";
|
|
28
|
+
|
|
29
|
+
export default defineFfmpegConnection({
|
|
30
|
+
workspaceRoot: "/workspace/media",
|
|
31
|
+
access: {
|
|
32
|
+
read: true,
|
|
33
|
+
write: { approval: "always" }
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The connection exposes:
|
|
39
|
+
|
|
40
|
+
- `probe_media`
|
|
41
|
+
- `trim_media`
|
|
42
|
+
- `transcode_media`
|
|
43
|
+
- `concat_media`
|
|
44
|
+
- `replace_audio`
|
|
45
|
+
- `extract_frames`
|
|
46
|
+
- `reframe_vertical`
|
|
47
|
+
|
|
48
|
+
All input and output paths must resolve inside `workspaceRoot`, including
|
|
49
|
+
symlink targets. The deployment owner can select trusted `ffmpegCommand` and
|
|
50
|
+
`ffprobeCommand` paths; agents cannot replace those executables or add
|
|
51
|
+
unreviewed flags.
|
|
52
|
+
|
|
53
|
+
`concat_media` expects codec-compatible, normalized clips. Transcode source
|
|
54
|
+
clips to a common size, frame rate, codec, and audio layout before stitching
|
|
55
|
+
when they come from different devices or platforms.
|
|
56
|
+
|
|
57
|
+
`extract_frames` returns paths only by default. Set `previewFrames` explicitly
|
|
58
|
+
(one to twelve) only when the user requests or approves a frame-based
|
|
59
|
+
approximation. Assembly Line never invokes frame sampling as a fallback for native
|
|
60
|
+
video understanding, and agents must not claim that sampled stills are
|
|
61
|
+
equivalent to watching temporal video.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
/** Build bounded, evenly sampled MCP image content from an FFmpeg output pattern. */
|
|
3
|
+
export declare function framePreviewResult(outputPattern: string, result: {
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
}, previewFrames: number, workspaceRoot: string): CallToolResult;
|
|
7
|
+
export declare function matchingFramePaths(outputPattern: string): string[];
|
|
8
|
+
//# sourceMappingURL=frame-preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-preview.d.ts","sourceRoot":"","sources":["../src/frame-preview.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAKzE,qFAAqF;AACrF,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC1C,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACpB,cAAc,CA+BhB;AAED,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CAalE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, extname, isAbsolute, join, relative } from "node:path";
|
|
3
|
+
const MAX_PREVIEW_FRAME_BYTES = 20 * 1024 * 1024;
|
|
4
|
+
const MAX_PREVIEW_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
5
|
+
/** Build bounded, evenly sampled MCP image content from an FFmpeg output pattern. */
|
|
6
|
+
export function framePreviewResult(outputPattern, result, previewFrames, workspaceRoot) {
|
|
7
|
+
const root = realpathSync(workspaceRoot);
|
|
8
|
+
const framePaths = matchingFramePaths(outputPattern).filter((path) => isInside(root, realpathSync(path)));
|
|
9
|
+
const previewPaths = sampledPaths(framePaths, previewFrames);
|
|
10
|
+
const images = [];
|
|
11
|
+
const includedPaths = [];
|
|
12
|
+
let totalBytes = 0;
|
|
13
|
+
for (const path of previewPaths) {
|
|
14
|
+
const size = statSync(path).size;
|
|
15
|
+
if (size > MAX_PREVIEW_FRAME_BYTES || totalBytes + size > MAX_PREVIEW_TOTAL_BYTES)
|
|
16
|
+
continue;
|
|
17
|
+
const mimeType = frameMimeType(path);
|
|
18
|
+
if (!mimeType)
|
|
19
|
+
continue;
|
|
20
|
+
images.push({ type: "image", data: readFileSync(path).toString("base64"), mimeType });
|
|
21
|
+
includedPaths.push(path);
|
|
22
|
+
totalBytes += size;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: JSON.stringify({
|
|
29
|
+
outputPattern,
|
|
30
|
+
frameCount: framePaths.length,
|
|
31
|
+
previewPaths: includedPaths,
|
|
32
|
+
stdout: result.stdout,
|
|
33
|
+
stderr: result.stderr
|
|
34
|
+
}, null, 2)
|
|
35
|
+
},
|
|
36
|
+
...images
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function matchingFramePaths(outputPattern) {
|
|
41
|
+
const directory = dirname(outputPattern);
|
|
42
|
+
const filename = basename(outputPattern);
|
|
43
|
+
const token = /%(?:0([1-9][0-9]*))?d/u.exec(filename);
|
|
44
|
+
if (!token || token.index === undefined)
|
|
45
|
+
return [];
|
|
46
|
+
const before = filename.slice(0, token.index);
|
|
47
|
+
const after = filename.slice(token.index + token[0].length);
|
|
48
|
+
const digits = token[1] ? `\\d{${token[1]}}` : "\\d+";
|
|
49
|
+
const matcher = new RegExp(`^${escapeRegex(before)}${digits}${escapeRegex(after)}$`, "u");
|
|
50
|
+
return readdirSync(directory)
|
|
51
|
+
.filter((name) => matcher.test(name))
|
|
52
|
+
.sort((left, right) => left.localeCompare(right, undefined, { numeric: true }))
|
|
53
|
+
.map((name) => join(directory, name));
|
|
54
|
+
}
|
|
55
|
+
function sampledPaths(paths, count) {
|
|
56
|
+
if (count <= 0 || paths.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
if (paths.length <= count)
|
|
59
|
+
return paths;
|
|
60
|
+
if (count === 1)
|
|
61
|
+
return [paths[0]];
|
|
62
|
+
const indexes = new Set(Array.from({ length: count }, (_, index) => Math.round((index * (paths.length - 1)) / (count - 1))));
|
|
63
|
+
return [...indexes].map((index) => paths[index]);
|
|
64
|
+
}
|
|
65
|
+
function frameMimeType(path) {
|
|
66
|
+
const extension = extname(path).toLowerCase();
|
|
67
|
+
if (extension === ".jpg" || extension === ".jpeg")
|
|
68
|
+
return "image/jpeg";
|
|
69
|
+
if (extension === ".png")
|
|
70
|
+
return "image/png";
|
|
71
|
+
if (extension === ".webp")
|
|
72
|
+
return "image/webp";
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
function escapeRegex(value) {
|
|
76
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
77
|
+
}
|
|
78
|
+
function isInside(root, candidate) {
|
|
79
|
+
const relation = relative(root, candidate);
|
|
80
|
+
return !relation.startsWith("..") && !isAbsolute(relation);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=frame-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-preview.js","sourceRoot":"","sources":["../src/frame-preview.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type StdioMcpPluginConnectionOptions } from "@assemblyline-agents/core";
|
|
2
|
+
export interface FfmpegConnectionOptions extends StdioMcpPluginConnectionOptions {
|
|
3
|
+
/** Directory containing every readable input and writable output. */
|
|
4
|
+
workspaceRoot?: string;
|
|
5
|
+
/** Trusted FFmpeg executable selected by the deployment owner. */
|
|
6
|
+
ffmpegCommand?: string;
|
|
7
|
+
/** Trusted FFprobe executable selected by the deployment owner. */
|
|
8
|
+
ffprobeCommand?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function defineFfmpegConnection(options: FfmpegConnectionOptions): import("@assemblyline-agents/core").McpStdioClientConnectionDefinition;
|
|
11
|
+
export declare const openevePlugin: import("@assemblyline-agents/core").PluginModule;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,+BAA+B,EACrC,MAAM,2BAA2B,CAAC;AAKnC,MAAM,WAAW,uBAAwB,SAAQ,+BAA+B;IAC9E,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,0EAuBtE;AAED,eAAO,MAAM,aAAa,kDAA0C,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { connectionPluginMetadata, definePlugin, defineStdioMcpPluginConnection } from "@assemblyline-agents/core";
|
|
4
|
+
const PLUGIN = connectionPluginMetadata("ffmpeg");
|
|
5
|
+
const MCP_SERVER_PATH = fileURLToPath(new URL("./mcp-server.js", import.meta.url));
|
|
6
|
+
export function defineFfmpegConnection(options) {
|
|
7
|
+
const { workspaceRoot: configuredWorkspaceRoot, ffmpegCommand = "ffmpeg", ffprobeCommand = "ffprobe", ...connection } = options;
|
|
8
|
+
const workspaceRoot = resolve(configuredWorkspaceRoot ?? connection.cwd ?? process.cwd());
|
|
9
|
+
const serverArgs = [
|
|
10
|
+
MCP_SERVER_PATH,
|
|
11
|
+
"--workspace-root",
|
|
12
|
+
workspaceRoot,
|
|
13
|
+
"--ffmpeg-command",
|
|
14
|
+
ffmpegCommand,
|
|
15
|
+
"--ffprobe-command",
|
|
16
|
+
ffprobeCommand
|
|
17
|
+
];
|
|
18
|
+
return defineStdioMcpPluginConnection(PLUGIN, {
|
|
19
|
+
...connection,
|
|
20
|
+
command: connection.command ?? process.execPath,
|
|
21
|
+
args: connection.args ?? serverArgs,
|
|
22
|
+
cwd: connection.cwd ?? workspaceRoot
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export const openevePlugin = definePlugin({ connections: [PLUGIN] });
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,wBAAwB,EACxB,YAAY,EACZ,8BAA8B,EAE/B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,GAAG,wBAAwB,CAAC,QAAQ,CAAE,CAAC;AACnD,MAAM,eAAe,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAWnF,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,EACJ,aAAa,EAAE,uBAAuB,EACtC,aAAa,GAAG,QAAQ,EACxB,cAAc,GAAG,SAAS,EAC1B,GAAG,UAAU,EACd,GAAG,OAAO,CAAC;IACZ,MAAM,aAAa,GAAG,OAAO,CAAC,uBAAuB,IAAI,UAAU,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1F,MAAM,UAAU,GAAG;QACjB,eAAe;QACf,kBAAkB;QAClB,aAAa;QACb,kBAAkB;QAClB,aAAa;QACb,mBAAmB;QACnB,cAAc;KACf,CAAC;IACF,OAAO,8BAA8B,CAAC,MAAM,EAAE;QAC5C,GAAG,UAAU;QACb,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ;QAC/C,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,UAAU;QACnC,GAAG,EAAE,UAAU,CAAC,GAAG,IAAI,aAAa;KACrC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { runProcess } from "./process.js";
|
|
8
|
+
import { framePreviewResult } from "./frame-preview.js";
|
|
9
|
+
import { arrayValue, enumValue, evenDimension, integerValue, numberValue, optionalEvenDimension, recordValue, stringValue } from "./tool-input.js";
|
|
10
|
+
import { FFMPEG_TOOLS } from "./tools.js";
|
|
11
|
+
import { MediaWorkspace } from "./workspace.js";
|
|
12
|
+
const options = parseServerOptions(process.argv.slice(2));
|
|
13
|
+
const workspace = new MediaWorkspace(options.workspaceRoot);
|
|
14
|
+
const server = new Server({ name: "openeve-ffmpeg", version: "0.1.0" }, {
|
|
15
|
+
capabilities: { tools: {} },
|
|
16
|
+
instructions: "Media operations are typed and restricted to the configured workspace root."
|
|
17
|
+
});
|
|
18
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: FFMPEG_TOOLS }));
|
|
19
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
20
|
+
try {
|
|
21
|
+
return await callTool(request.params.name, recordValue(request.params.arguments));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return toolResult({ error: error instanceof Error ? error.message : String(error) }, true);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
await server.connect(new StdioServerTransport());
|
|
28
|
+
async function callTool(name, input) {
|
|
29
|
+
if (name === "probe_media") {
|
|
30
|
+
const inputPath = workspace.input(stringValue(input.inputPath, "inputPath"));
|
|
31
|
+
const result = await run(options.ffprobeCommand, [
|
|
32
|
+
"-v", "error",
|
|
33
|
+
"-show_format",
|
|
34
|
+
"-show_streams",
|
|
35
|
+
"-of", "json",
|
|
36
|
+
inputPath
|
|
37
|
+
]);
|
|
38
|
+
return toolResult(JSON.parse(result.stdout || "{}"));
|
|
39
|
+
}
|
|
40
|
+
if (name === "trim_media") {
|
|
41
|
+
const inputPath = workspace.input(stringValue(input.inputPath, "inputPath"));
|
|
42
|
+
const outputPath = workspace.output(stringValue(input.outputPath, "outputPath"));
|
|
43
|
+
const start = numberValue(input.startSeconds, "startSeconds", 0);
|
|
44
|
+
const duration = numberValue(input.durationSeconds, "durationSeconds", Number.MIN_VALUE);
|
|
45
|
+
const args = [
|
|
46
|
+
"-y", "-ss", String(start), "-i", inputPath, "-t", String(duration),
|
|
47
|
+
"-map", "0:v:0", "-map", "0:a?",
|
|
48
|
+
"-c:v", "libx264",
|
|
49
|
+
"-preset", enumValue(input.preset, X264_PRESETS, "medium"),
|
|
50
|
+
"-crf", String(integerValue(input.crf, "crf", 0, 51, 18)),
|
|
51
|
+
"-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart",
|
|
52
|
+
outputPath
|
|
53
|
+
];
|
|
54
|
+
return writeResult(outputPath, await run(options.ffmpegCommand, args));
|
|
55
|
+
}
|
|
56
|
+
if (name === "transcode_media") {
|
|
57
|
+
const inputPath = workspace.input(stringValue(input.inputPath, "inputPath"));
|
|
58
|
+
const outputPath = workspace.output(stringValue(input.outputPath, "outputPath"));
|
|
59
|
+
const codec = enumValue(input.codec, ["h264", "h265", "vp9", "prores"], "h264");
|
|
60
|
+
const width = optionalEvenDimension(input.width, "width", 7680);
|
|
61
|
+
const height = optionalEvenDimension(input.height, "height", 7680);
|
|
62
|
+
const video = videoCodecArguments(codec, input);
|
|
63
|
+
const args = [
|
|
64
|
+
"-y", "-i", inputPath,
|
|
65
|
+
"-map", "0:v:0", "-map", "0:a?",
|
|
66
|
+
...scaleArguments(width, height),
|
|
67
|
+
...video,
|
|
68
|
+
...(codec === "prores" ? ["-c:a", "pcm_s16le"] : ["-c:a", "aac", "-b:a", "192k"]),
|
|
69
|
+
outputPath
|
|
70
|
+
];
|
|
71
|
+
return writeResult(outputPath, await run(options.ffmpegCommand, args));
|
|
72
|
+
}
|
|
73
|
+
if (name === "concat_media") {
|
|
74
|
+
const values = arrayValue(input.inputPaths, "inputPaths", 2, 100);
|
|
75
|
+
const inputPaths = values.map((value, index) => workspace.input(stringValue(value, `inputPaths[${index}]`)));
|
|
76
|
+
const outputPath = workspace.output(stringValue(input.outputPath, "outputPath"));
|
|
77
|
+
const mode = enumValue(input.mode, ["copy", "reencode"], "reencode");
|
|
78
|
+
const temporary = mkdtempSync(join(tmpdir(), "openeve-ffmpeg-concat-"));
|
|
79
|
+
const listPath = join(temporary, "inputs.txt");
|
|
80
|
+
try {
|
|
81
|
+
writeFileSync(listPath, inputPaths.map((path) => `file '${escapeConcatPath(path)}'`).join("\n"));
|
|
82
|
+
const args = [
|
|
83
|
+
"-y", "-f", "concat", "-safe", "0", "-i", listPath,
|
|
84
|
+
...(mode === "copy"
|
|
85
|
+
? ["-c", "copy"]
|
|
86
|
+
: [
|
|
87
|
+
"-c:v", "libx264", "-preset", "medium",
|
|
88
|
+
"-crf", String(integerValue(input.crf, "crf", 0, 51, 18)),
|
|
89
|
+
"-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart"
|
|
90
|
+
]),
|
|
91
|
+
outputPath
|
|
92
|
+
];
|
|
93
|
+
return writeResult(outputPath, await run(options.ffmpegCommand, args));
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (name === "replace_audio") {
|
|
100
|
+
const videoPath = workspace.input(stringValue(input.videoPath, "videoPath"));
|
|
101
|
+
const audioPath = workspace.input(stringValue(input.audioPath, "audioPath"));
|
|
102
|
+
const outputPath = workspace.output(stringValue(input.outputPath, "outputPath"));
|
|
103
|
+
const volume = input.volume === undefined ? 1 : numberValue(input.volume, "volume", 0, 10);
|
|
104
|
+
const args = [
|
|
105
|
+
"-y", "-i", videoPath, "-i", audioPath,
|
|
106
|
+
"-map", "0:v:0", "-map", "1:a:0",
|
|
107
|
+
"-c:v", "copy", "-filter:a", `volume=${volume}`,
|
|
108
|
+
"-c:a", "aac", "-b:a", "192k", "-shortest",
|
|
109
|
+
outputPath
|
|
110
|
+
];
|
|
111
|
+
return writeResult(outputPath, await run(options.ffmpegCommand, args));
|
|
112
|
+
}
|
|
113
|
+
if (name === "extract_frames") {
|
|
114
|
+
const inputPath = workspace.input(stringValue(input.inputPath, "inputPath"));
|
|
115
|
+
const patternValue = stringValue(input.outputPattern, "outputPattern");
|
|
116
|
+
if (!/%(?:0[1-9][0-9]*)?d/u.test(patternValue)) {
|
|
117
|
+
throw new Error("outputPattern must include a frame counter such as %04d.");
|
|
118
|
+
}
|
|
119
|
+
const outputPattern = workspace.outputPattern(patternValue);
|
|
120
|
+
const args = [
|
|
121
|
+
"-y",
|
|
122
|
+
...(input.startSeconds === undefined ? [] : ["-ss", String(numberValue(input.startSeconds, "startSeconds", 0))]),
|
|
123
|
+
"-i", inputPath,
|
|
124
|
+
...(input.durationSeconds === undefined
|
|
125
|
+
? []
|
|
126
|
+
: ["-t", String(numberValue(input.durationSeconds, "durationSeconds", Number.MIN_VALUE))]),
|
|
127
|
+
"-vf", `fps=${numberValue(input.fps, "fps", Number.MIN_VALUE, 60)}`,
|
|
128
|
+
"-q:v", String(integerValue(input.quality, "quality", 2, 31, 2)),
|
|
129
|
+
outputPattern
|
|
130
|
+
];
|
|
131
|
+
const result = await run(options.ffmpegCommand, args);
|
|
132
|
+
return framePreviewResult(outputPattern, result, integerValue(input.previewFrames, "previewFrames", 0, 12, 0), workspace.root);
|
|
133
|
+
}
|
|
134
|
+
if (name === "reframe_vertical") {
|
|
135
|
+
const inputPath = workspace.input(stringValue(input.inputPath, "inputPath"));
|
|
136
|
+
const outputPath = workspace.output(stringValue(input.outputPath, "outputPath"));
|
|
137
|
+
const width = evenDimension(input.width, "width", 2160, 1080);
|
|
138
|
+
const height = evenDimension(input.height, "height", 3840, 1920);
|
|
139
|
+
const focus = enumValue(input.focus, ["left", "center", "right"], "center");
|
|
140
|
+
const horizontal = focus === "left" ? "0" : focus === "right" ? "iw-ow" : "(iw-ow)/2";
|
|
141
|
+
const filter = `scale=${width}:${height}:force_original_aspect_ratio=increase,crop=${width}:${height}:${horizontal}:(ih-oh)/2`;
|
|
142
|
+
const args = [
|
|
143
|
+
"-y", "-i", inputPath,
|
|
144
|
+
"-map", "0:v:0", "-map", "0:a?",
|
|
145
|
+
"-vf", filter,
|
|
146
|
+
"-c:v", "libx264", "-preset", "medium",
|
|
147
|
+
"-crf", String(integerValue(input.crf, "crf", 0, 51, 18)),
|
|
148
|
+
"-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart",
|
|
149
|
+
outputPath
|
|
150
|
+
];
|
|
151
|
+
return writeResult(outputPath, await run(options.ffmpegCommand, args));
|
|
152
|
+
}
|
|
153
|
+
throw new Error(`Unknown FFmpeg tool: ${name}`);
|
|
154
|
+
}
|
|
155
|
+
const X264_PRESETS = [
|
|
156
|
+
"ultrafast", "superfast", "veryfast", "faster", "fast",
|
|
157
|
+
"medium", "slow", "slower", "veryslow"
|
|
158
|
+
];
|
|
159
|
+
function videoCodecArguments(codec, input) {
|
|
160
|
+
if (codec === "prores")
|
|
161
|
+
return ["-c:v", "prores_ks", "-profile:v", "3"];
|
|
162
|
+
if (codec === "vp9") {
|
|
163
|
+
return ["-c:v", "libvpx-vp9", "-crf", String(integerValue(input.crf, "crf", 0, 51, 28)), "-b:v", "0"];
|
|
164
|
+
}
|
|
165
|
+
return [
|
|
166
|
+
"-c:v", codec === "h265" ? "libx265" : "libx264",
|
|
167
|
+
"-preset", enumValue(input.preset, X264_PRESETS, "medium"),
|
|
168
|
+
"-crf", String(integerValue(input.crf, "crf", 0, 51, codec === "h265" ? 24 : 18))
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
function scaleArguments(width, height) {
|
|
172
|
+
if (width && height) {
|
|
173
|
+
return ["-vf", `scale=${width}:${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:(ow-iw)/2:(oh-ih)/2`];
|
|
174
|
+
}
|
|
175
|
+
if (width)
|
|
176
|
+
return ["-vf", `scale=${width}:-2`];
|
|
177
|
+
if (height)
|
|
178
|
+
return ["-vf", `scale=-2:${height}`];
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
function run(command, args) {
|
|
182
|
+
return runProcess(command, args, { cwd: workspace.root });
|
|
183
|
+
}
|
|
184
|
+
function writeResult(outputPath, result) {
|
|
185
|
+
return toolResult({ outputPath, stdout: result.stdout, stderr: result.stderr });
|
|
186
|
+
}
|
|
187
|
+
function parseServerOptions(args) {
|
|
188
|
+
let workspaceRoot = process.cwd();
|
|
189
|
+
let ffmpegCommand = "ffmpeg";
|
|
190
|
+
let ffprobeCommand = "ffprobe";
|
|
191
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
192
|
+
const flag = args[index];
|
|
193
|
+
const value = args[index + 1];
|
|
194
|
+
if (!flag || value === undefined)
|
|
195
|
+
throw new Error(`Missing value for ${flag ?? "server option"}.`);
|
|
196
|
+
if (flag === "--workspace-root")
|
|
197
|
+
workspaceRoot = value;
|
|
198
|
+
else if (flag === "--ffmpeg-command")
|
|
199
|
+
ffmpegCommand = value;
|
|
200
|
+
else if (flag === "--ffprobe-command")
|
|
201
|
+
ffprobeCommand = value;
|
|
202
|
+
else
|
|
203
|
+
throw new Error(`Unknown FFmpeg MCP server option: ${flag}`);
|
|
204
|
+
}
|
|
205
|
+
return { workspaceRoot, ffmpegCommand, ffprobeCommand };
|
|
206
|
+
}
|
|
207
|
+
function escapeConcatPath(value) {
|
|
208
|
+
return value.replaceAll("'", "'\\''");
|
|
209
|
+
}
|
|
210
|
+
function toolResult(value, isError = false) {
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
213
|
+
...(isError ? { isError: true } : {})
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC,aAAa,CAAC,CAuCxB"}
|
package/dist/process.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
const MAX_CAPTURE_BYTES = 512 * 1024;
|
|
3
|
+
export function runProcess(command, args, options) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const child = spawn(command, args, {
|
|
6
|
+
cwd: options.cwd,
|
|
7
|
+
env: process.env,
|
|
8
|
+
shell: false,
|
|
9
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
10
|
+
});
|
|
11
|
+
let stdout = "";
|
|
12
|
+
let stderr = "";
|
|
13
|
+
let capturedBytes = 0;
|
|
14
|
+
const capture = (target, chunk) => {
|
|
15
|
+
capturedBytes += chunk.byteLength;
|
|
16
|
+
if (capturedBytes > MAX_CAPTURE_BYTES) {
|
|
17
|
+
child.kill("SIGKILL");
|
|
18
|
+
reject(new Error(`Process output exceeded ${MAX_CAPTURE_BYTES} bytes.`));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (target === "stdout")
|
|
22
|
+
stdout += chunk.toString();
|
|
23
|
+
else
|
|
24
|
+
stderr += chunk.toString();
|
|
25
|
+
};
|
|
26
|
+
child.stdout.on("data", (chunk) => capture("stdout", chunk));
|
|
27
|
+
child.stderr.on("data", (chunk) => capture("stderr", chunk));
|
|
28
|
+
child.once("error", reject);
|
|
29
|
+
const timeout = setTimeout(() => {
|
|
30
|
+
child.kill("SIGKILL");
|
|
31
|
+
reject(new Error(`Process timed out after ${options.timeoutMs ?? 30 * 60_000}ms.`));
|
|
32
|
+
}, options.timeoutMs ?? 30 * 60_000);
|
|
33
|
+
child.once("close", (code, signal) => {
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
if (code === 0) {
|
|
36
|
+
resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
reject(new Error(`${command} exited with ${code ?? signal ?? "unknown status"}${stderr.trim() ? `: ${stderr.trim()}` : ""}`));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,CAAC;AAOrC,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,IAAc,EACd,OAA4C;IAE5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YACjC,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,MAAM,OAAO,GAAG,CAAC,MAA2B,EAAE,KAAa,EAAQ,EAAE;YACnE,aAAa,IAAI,KAAK,CAAC,UAAU,CAAC;YAClC,IAAI,aAAa,GAAG,iBAAiB,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,iBAAiB,SAAS,CAAC,CAAC,CAAC;gBACzE,OAAO;YACT,CAAC;YACD,IAAI,MAAM,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;;gBAC/C,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClC,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QACrE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,OAAO,CAAC,SAAS,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC;QACtF,CAAC,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACnC,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1D,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,KAAK,CACd,GAAG,OAAO,gBAAgB,IAAI,IAAI,MAAM,IAAI,gBAAgB,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3G,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function recordValue(value: unknown): Record<string, unknown>;
|
|
2
|
+
export declare function stringValue(value: unknown, name: string): string;
|
|
3
|
+
export declare function arrayValue(value: unknown, name: string, minimum: number, maximum: number): unknown[];
|
|
4
|
+
export declare function numberValue(value: unknown, name: string, minimum: number, maximum?: number): number;
|
|
5
|
+
export declare function integerValue(value: unknown, name: string, minimum: number, maximum: number, fallback?: number): number;
|
|
6
|
+
export declare function evenDimension(value: unknown, name: string, maximum: number, fallback: number): number;
|
|
7
|
+
export declare function optionalEvenDimension(value: unknown, name: string, maximum: number): number | undefined;
|
|
8
|
+
export declare function enumValue<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T;
|
|
9
|
+
//# sourceMappingURL=tool-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-input.d.ts","sourceRoot":"","sources":["../src/tool-input.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAInE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAKpG;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,SAA0B,GAChC,MAAM,CAKR;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAMR;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIrG;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvG;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAMjG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function recordValue(value) {
|
|
2
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: {};
|
|
5
|
+
}
|
|
6
|
+
export function stringValue(value, name) {
|
|
7
|
+
if (typeof value !== "string" || !value.trim())
|
|
8
|
+
throw new Error(`${name} must be a non-empty string.`);
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
export function arrayValue(value, name, minimum, maximum) {
|
|
12
|
+
if (!Array.isArray(value) || value.length < minimum || value.length > maximum) {
|
|
13
|
+
throw new Error(`${name} must contain ${minimum} to ${maximum} items.`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
export function numberValue(value, name, minimum, maximum = Number.MAX_SAFE_INTEGER) {
|
|
18
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) {
|
|
19
|
+
throw new Error(`${name} must be a number from ${minimum} to ${maximum}.`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
export function integerValue(value, name, minimum, maximum, fallback) {
|
|
24
|
+
if (value === undefined && fallback !== undefined)
|
|
25
|
+
return fallback;
|
|
26
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) {
|
|
27
|
+
throw new Error(`${name} must be an integer from ${minimum} to ${maximum}.`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
export function evenDimension(value, name, maximum, fallback) {
|
|
32
|
+
const dimension = integerValue(value, name, 16, maximum, fallback);
|
|
33
|
+
if (dimension % 2 !== 0)
|
|
34
|
+
throw new Error(`${name} must be even.`);
|
|
35
|
+
return dimension;
|
|
36
|
+
}
|
|
37
|
+
export function optionalEvenDimension(value, name, maximum) {
|
|
38
|
+
return value === undefined ? undefined : evenDimension(value, name, maximum, 16);
|
|
39
|
+
}
|
|
40
|
+
export function enumValue(value, allowed, fallback) {
|
|
41
|
+
if (value === undefined)
|
|
42
|
+
return fallback;
|
|
43
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
44
|
+
throw new Error(`Value must be one of: ${allowed.join(", ")}.`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=tool-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-input.js","sourceRoot":"","sources":["../src/tool-input.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAE/D,eAAO,MAAM,YAAY,EAAE,IAAI,EA0H9B,CAAC"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export const FFMPEG_TOOLS = [
|
|
2
|
+
{
|
|
3
|
+
name: "probe_media",
|
|
4
|
+
description: "Inspect media streams, codecs, dimensions, frame rates, duration, and container metadata.",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
required: ["inputPath"],
|
|
8
|
+
properties: { inputPath: { type: "string", minLength: 1 } },
|
|
9
|
+
additionalProperties: false
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: "trim_media",
|
|
14
|
+
description: "Create a precisely trimmed H.264/AAC clip from a workspace media file.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
required: ["inputPath", "outputPath", "startSeconds", "durationSeconds"],
|
|
18
|
+
properties: {
|
|
19
|
+
inputPath: { type: "string" },
|
|
20
|
+
outputPath: { type: "string" },
|
|
21
|
+
startSeconds: { type: "number", minimum: 0 },
|
|
22
|
+
durationSeconds: { type: "number", exclusiveMinimum: 0 },
|
|
23
|
+
crf: { type: "integer", minimum: 0, maximum: 51 },
|
|
24
|
+
preset: {
|
|
25
|
+
type: "string",
|
|
26
|
+
enum: ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow"]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: false
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "transcode_media",
|
|
34
|
+
description: "Transcode video to a reviewed codec, optionally resizing and padding to exact dimensions.",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object",
|
|
37
|
+
required: ["inputPath", "outputPath"],
|
|
38
|
+
properties: {
|
|
39
|
+
inputPath: { type: "string" },
|
|
40
|
+
outputPath: { type: "string" },
|
|
41
|
+
codec: { type: "string", enum: ["h264", "h265", "vp9", "prores"] },
|
|
42
|
+
width: { type: "integer", minimum: 16, maximum: 7680 },
|
|
43
|
+
height: { type: "integer", minimum: 16, maximum: 7680 },
|
|
44
|
+
crf: { type: "integer", minimum: 0, maximum: 51 },
|
|
45
|
+
preset: {
|
|
46
|
+
type: "string",
|
|
47
|
+
enum: ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow"]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
additionalProperties: false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "concat_media",
|
|
55
|
+
description: "Stitch two or more normalized media clips in order using FFmpeg's concat demuxer.",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
required: ["inputPaths", "outputPath"],
|
|
59
|
+
properties: {
|
|
60
|
+
inputPaths: { type: "array", minItems: 2, maxItems: 100, items: { type: "string" } },
|
|
61
|
+
outputPath: { type: "string" },
|
|
62
|
+
mode: { type: "string", enum: ["copy", "reencode"] },
|
|
63
|
+
crf: { type: "integer", minimum: 0, maximum: 51 }
|
|
64
|
+
},
|
|
65
|
+
additionalProperties: false
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "replace_audio",
|
|
70
|
+
description: "Replace a video's audio track with a workspace audio file and stop at the shorter stream.",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: "object",
|
|
73
|
+
required: ["videoPath", "audioPath", "outputPath"],
|
|
74
|
+
properties: {
|
|
75
|
+
videoPath: { type: "string" },
|
|
76
|
+
audioPath: { type: "string" },
|
|
77
|
+
outputPath: { type: "string" },
|
|
78
|
+
volume: { type: "number", minimum: 0, maximum: 10 }
|
|
79
|
+
},
|
|
80
|
+
additionalProperties: false
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "extract_frames",
|
|
85
|
+
description: "Extract a numbered image sequence from a video at a controlled frame rate.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
required: ["inputPath", "outputPattern", "fps"],
|
|
89
|
+
properties: {
|
|
90
|
+
inputPath: { type: "string" },
|
|
91
|
+
outputPattern: { type: "string", description: "Workspace path containing a printf counter such as frames/frame-%04d.jpg." },
|
|
92
|
+
fps: { type: "number", exclusiveMinimum: 0, maximum: 60 },
|
|
93
|
+
startSeconds: { type: "number", minimum: 0 },
|
|
94
|
+
durationSeconds: { type: "number", exclusiveMinimum: 0 },
|
|
95
|
+
quality: { type: "integer", minimum: 2, maximum: 31 },
|
|
96
|
+
previewFrames: {
|
|
97
|
+
type: "integer",
|
|
98
|
+
minimum: 0,
|
|
99
|
+
maximum: 12,
|
|
100
|
+
description: "Explicit opt-in: return this many evenly sampled frames as model-visible images. Defaults to 0. Never use this as a silent substitute for native video understanding."
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
additionalProperties: false
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "reframe_vertical",
|
|
108
|
+
description: "Create a 9:16-style vertical crop with a controlled horizontal focus.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
required: ["inputPath", "outputPath"],
|
|
112
|
+
properties: {
|
|
113
|
+
inputPath: { type: "string" },
|
|
114
|
+
outputPath: { type: "string" },
|
|
115
|
+
width: { type: "integer", minimum: 16, maximum: 2160 },
|
|
116
|
+
height: { type: "integer", minimum: 16, maximum: 3840 },
|
|
117
|
+
focus: { type: "string", enum: ["left", "center", "right"] },
|
|
118
|
+
crf: { type: "integer", minimum: 0, maximum: 51 }
|
|
119
|
+
},
|
|
120
|
+
additionalProperties: false
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAW;IAClC;QACE,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,2FAA2F;QACxG,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,WAAW,CAAC;YACvB,UAAU,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;YAC3D,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,wEAAwE;QACrF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,CAAC;YACxE,UAAU,EAAE;gBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC7B,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC9B,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;gBAC5C,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC,EAAE;gBACxD,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;gBACjD,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC;iBACvG;aACF;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,2FAA2F;QACxG,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC;YACrC,UAAU,EAAE;gBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC7B,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC9B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE;gBAClE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;gBACtD,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;gBACvD,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;gBACjD,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC;iBACvG;aACF;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,mFAAmF;QAChG,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;YACtC,UAAU,EAAE;gBACV,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;gBACpF,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC9B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE;gBACpD,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;aAClD;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD;QACE,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,2FAA2F;QACxG,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC;YAClD,UAAU,EAAE;gBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC7B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC7B,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC9B,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;aACpD;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE,4EAA4E;QACzF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,CAAC;YAC/C,UAAU,EAAE;gBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC7B,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2EAA2E,EAAE;gBAC3H,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;gBACzD,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;gBAC5C,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC,EAAE;gBACxD,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;gBACrD,aAAa,EAAE;oBACb,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,EAAE;oBACX,WAAW,EAAE,uKAAuK;iBACrL;aACF;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,uEAAuE;QACpF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC;YACrC,UAAU,EAAE;gBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC7B,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC9B,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;gBACtD,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;gBACvD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE;gBAC5D,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;aAClD;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class MediaWorkspace {
|
|
2
|
+
readonly root: string;
|
|
3
|
+
constructor(root: string);
|
|
4
|
+
input(value: string, name?: string): string;
|
|
5
|
+
output(value: string, name?: string): string;
|
|
6
|
+
outputPattern(value: string): string;
|
|
7
|
+
private lexical;
|
|
8
|
+
private assertInside;
|
|
9
|
+
private existingAncestor;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAaA,qBAAa,cAAc;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;IAIxB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAc,GAAG,MAAM;IAQhD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAe,GAAG,MAAM;IAWlD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQpC,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, realpathSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
import { matchingFramePaths } from "./frame-preview.js";
|
|
4
|
+
export class MediaWorkspace {
|
|
5
|
+
root;
|
|
6
|
+
constructor(root) {
|
|
7
|
+
this.root = realpathSync(resolve(root));
|
|
8
|
+
}
|
|
9
|
+
input(value, name = "inputPath") {
|
|
10
|
+
const candidate = this.lexical(value, name);
|
|
11
|
+
if (!existsSync(candidate))
|
|
12
|
+
throw new Error(`${name} does not exist: ${value}`);
|
|
13
|
+
const real = realpathSync(candidate);
|
|
14
|
+
this.assertInside(real, name);
|
|
15
|
+
return real;
|
|
16
|
+
}
|
|
17
|
+
output(value, name = "outputPath") {
|
|
18
|
+
const candidate = this.lexical(value, name);
|
|
19
|
+
if (existsSync(candidate))
|
|
20
|
+
this.assertInside(realpathSync(candidate), name);
|
|
21
|
+
const parent = dirname(candidate);
|
|
22
|
+
const existingAncestor = this.existingAncestor(parent);
|
|
23
|
+
this.assertInside(realpathSync(existingAncestor), name);
|
|
24
|
+
mkdirSync(parent, { recursive: true });
|
|
25
|
+
this.assertInside(realpathSync(parent), name);
|
|
26
|
+
return candidate;
|
|
27
|
+
}
|
|
28
|
+
outputPattern(value) {
|
|
29
|
+
const pattern = this.output(value, "outputPattern");
|
|
30
|
+
for (const existingPath of matchingFramePaths(pattern)) {
|
|
31
|
+
this.assertInside(realpathSync(existingPath), "outputPattern");
|
|
32
|
+
}
|
|
33
|
+
return pattern;
|
|
34
|
+
}
|
|
35
|
+
lexical(value, name) {
|
|
36
|
+
if (!value.trim())
|
|
37
|
+
throw new Error(`${name} must be a non-empty path.`);
|
|
38
|
+
const candidate = resolve(this.root, value);
|
|
39
|
+
this.assertInside(candidate, name);
|
|
40
|
+
return candidate;
|
|
41
|
+
}
|
|
42
|
+
assertInside(candidate, name) {
|
|
43
|
+
const relation = relative(this.root, candidate);
|
|
44
|
+
if (relation.startsWith("..") || isAbsolute(relation)) {
|
|
45
|
+
throw new Error(`${name} must stay inside ${this.root}.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
existingAncestor(value) {
|
|
49
|
+
let candidate = value;
|
|
50
|
+
while (!existsSync(candidate)) {
|
|
51
|
+
const parent = dirname(candidate);
|
|
52
|
+
if (parent === candidate)
|
|
53
|
+
break;
|
|
54
|
+
candidate = parent;
|
|
55
|
+
}
|
|
56
|
+
return candidate;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@assemblyline-agents/ffmpeg",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"default": "./dist/index.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
13
|
+
"@assemblyline-agents/core": "0.1.0"
|
|
14
|
+
},
|
|
15
|
+
"description": "Official OpenEve workspace-scoped FFmpeg connection plugin.",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"skills"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=22.19.0"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/jasonbadeaux/openeve.git",
|
|
27
|
+
"directory": "packages/ffmpeg"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/jasonbadeaux/openeve/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/jasonbadeaux/openeve#readme",
|
|
33
|
+
"author": "OpenEve contributors",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"openeve",
|
|
36
|
+
"ai",
|
|
37
|
+
"agents",
|
|
38
|
+
"ffmpeg",
|
|
39
|
+
"video",
|
|
40
|
+
"audio",
|
|
41
|
+
"media",
|
|
42
|
+
"plugin"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"check": "tsc -p tsconfig.json --noEmit"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ffmpeg
|
|
3
|
+
description: Inspect, trim, normalize, stitch, re-audio, extract, and vertically reframe media with typed FFmpeg tools.
|
|
4
|
+
---
|
|
5
|
+
# FFmpeg
|
|
6
|
+
|
|
7
|
+
Use `probe_media` before editing unfamiliar media. Record the duration, video
|
|
8
|
+
dimensions, frame rate, codecs, audio layout, and rotation metadata so edits
|
|
9
|
+
are based on the actual streams.
|
|
10
|
+
|
|
11
|
+
For a social recut:
|
|
12
|
+
|
|
13
|
+
1. Build a paper edit with source paths and exact in/out seconds.
|
|
14
|
+
2. Trim each selected beat to a new clip.
|
|
15
|
+
3. Normalize clips from different sources with `transcode_media`.
|
|
16
|
+
4. Stitch normalized clips in narrative order with `concat_media`.
|
|
17
|
+
5. Replace or add the intended audio track.
|
|
18
|
+
6. Use `reframe_vertical` for a 9:16 delivery.
|
|
19
|
+
|
|
20
|
+
Prefer native video understanding for review. If the selected model cannot
|
|
21
|
+
process video natively, say so and stop; do not silently approximate the video
|
|
22
|
+
with sampled frames. Call `extract_frames` with an explicit `previewFrames`
|
|
23
|
+
value only when the user specifically requests or approves a frame-based
|
|
24
|
+
review. Its default is zero, so ordinary frame extraction returns paths only.
|
|
25
|
+
|
|
26
|
+
Prefer short shots with a clear visual purpose. Keep the hook readable in the
|
|
27
|
+
first seconds, preserve speech boundaries, and avoid jump cuts that clip words
|
|
28
|
+
or gestures. Do not claim to have watched a video when only still frames were
|
|
29
|
+
provided, and do not treat sampled frames as equivalent to temporal review.
|
|
30
|
+
|
|
31
|
+
Every editing tool writes files and requires the connection's configured write
|
|
32
|
+
authority. Never attempt to escape the media workspace or work around the typed
|
|
33
|
+
operations with arbitrary FFmpeg flags. Do not remove watermarks, attribution,
|
|
34
|
+
or rights-management signals, and only transform media the user is authorized
|
|
35
|
+
to use.
|