@studiomeyer/mcp-video 1.0.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/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/workflows/ci.yml +34 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +75 -0
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/USAGE.md +144 -0
- package/dist/handlers/capcut.d.ts +6 -0
- package/dist/handlers/capcut.js +229 -0
- package/dist/handlers/capcut.js.map +1 -0
- package/dist/handlers/editing.d.ts +6 -0
- package/dist/handlers/editing.js +242 -0
- package/dist/handlers/editing.js.map +1 -0
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +33 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/post-production.d.ts +5 -0
- package/dist/handlers/post-production.js +109 -0
- package/dist/handlers/post-production.js.map +1 -0
- package/dist/handlers/smart-screenshot.d.ts +5 -0
- package/dist/handlers/smart-screenshot.js +83 -0
- package/dist/handlers/smart-screenshot.js.map +1 -0
- package/dist/handlers/tts.d.ts +5 -0
- package/dist/handlers/tts.js +83 -0
- package/dist/handlers/tts.js.map +1 -0
- package/dist/handlers/video.d.ts +5 -0
- package/dist/handlers/video.js +127 -0
- package/dist/handlers/video.js.map +1 -0
- package/dist/lib/dual-transport.d.ts +42 -0
- package/dist/lib/dual-transport.js +208 -0
- package/dist/lib/dual-transport.js.map +1 -0
- package/dist/lib/logger.d.ts +12 -0
- package/dist/lib/logger.js +42 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/types.d.ts +16 -0
- package/dist/lib/types.js +15 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/schemas/capcut.d.ts +608 -0
- package/dist/schemas/capcut.js +411 -0
- package/dist/schemas/capcut.js.map +1 -0
- package/dist/schemas/editing.d.ts +822 -0
- package/dist/schemas/editing.js +466 -0
- package/dist/schemas/editing.js.map +1 -0
- package/dist/schemas/index.d.ts +2366 -0
- package/dist/schemas/index.js +15 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/post-production.d.ts +379 -0
- package/dist/schemas/post-production.js +268 -0
- package/dist/schemas/post-production.js.map +1 -0
- package/dist/schemas/smart-screenshot.d.ts +127 -0
- package/dist/schemas/smart-screenshot.js +122 -0
- package/dist/schemas/smart-screenshot.js.map +1 -0
- package/dist/schemas/tts.d.ts +220 -0
- package/dist/schemas/tts.js +194 -0
- package/dist/schemas/tts.js.map +1 -0
- package/dist/schemas/video.d.ts +236 -0
- package/dist/schemas/video.js +210 -0
- package/dist/schemas/video.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +239 -0
- package/dist/server.js.map +1 -0
- package/dist/server.test.d.ts +1 -0
- package/dist/server.test.js +87 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/engine/audio-mixer.d.ts +40 -0
- package/dist/tools/engine/audio-mixer.js +169 -0
- package/dist/tools/engine/audio-mixer.js.map +1 -0
- package/dist/tools/engine/audio.d.ts +22 -0
- package/dist/tools/engine/audio.js +73 -0
- package/dist/tools/engine/audio.js.map +1 -0
- package/dist/tools/engine/beat-sync.d.ts +31 -0
- package/dist/tools/engine/beat-sync.js +270 -0
- package/dist/tools/engine/beat-sync.js.map +1 -0
- package/dist/tools/engine/capture.d.ts +12 -0
- package/dist/tools/engine/capture.js +290 -0
- package/dist/tools/engine/capture.js.map +1 -0
- package/dist/tools/engine/chroma-key.d.ts +27 -0
- package/dist/tools/engine/chroma-key.js +154 -0
- package/dist/tools/engine/chroma-key.js.map +1 -0
- package/dist/tools/engine/concat.d.ts +49 -0
- package/dist/tools/engine/concat.js +149 -0
- package/dist/tools/engine/concat.js.map +1 -0
- package/dist/tools/engine/cursor.d.ts +26 -0
- package/dist/tools/engine/cursor.js +185 -0
- package/dist/tools/engine/cursor.js.map +1 -0
- package/dist/tools/engine/easing.d.ts +15 -0
- package/dist/tools/engine/easing.js +100 -0
- package/dist/tools/engine/easing.js.map +1 -0
- package/dist/tools/engine/editing.d.ts +158 -0
- package/dist/tools/engine/editing.js +541 -0
- package/dist/tools/engine/editing.js.map +1 -0
- package/dist/tools/engine/encoder.d.ts +31 -0
- package/dist/tools/engine/encoder.js +154 -0
- package/dist/tools/engine/encoder.js.map +1 -0
- package/dist/tools/engine/index.d.ts +30 -0
- package/dist/tools/engine/index.js +23 -0
- package/dist/tools/engine/index.js.map +1 -0
- package/dist/tools/engine/lut-presets.d.ts +25 -0
- package/dist/tools/engine/lut-presets.js +141 -0
- package/dist/tools/engine/lut-presets.js.map +1 -0
- package/dist/tools/engine/narrated-video.d.ts +63 -0
- package/dist/tools/engine/narrated-video.js +163 -0
- package/dist/tools/engine/narrated-video.js.map +1 -0
- package/dist/tools/engine/scenes.d.ts +17 -0
- package/dist/tools/engine/scenes.js +223 -0
- package/dist/tools/engine/scenes.js.map +1 -0
- package/dist/tools/engine/smart-screenshot.d.ts +80 -0
- package/dist/tools/engine/smart-screenshot.js +744 -0
- package/dist/tools/engine/smart-screenshot.js.map +1 -0
- package/dist/tools/engine/social-format.d.ts +66 -0
- package/dist/tools/engine/social-format.js +107 -0
- package/dist/tools/engine/social-format.js.map +1 -0
- package/dist/tools/engine/template-renderer.d.ts +45 -0
- package/dist/tools/engine/template-renderer.js +233 -0
- package/dist/tools/engine/template-renderer.js.map +1 -0
- package/dist/tools/engine/templates.d.ts +87 -0
- package/dist/tools/engine/templates.js +272 -0
- package/dist/tools/engine/templates.js.map +1 -0
- package/dist/tools/engine/text-animations.d.ts +33 -0
- package/dist/tools/engine/text-animations.js +192 -0
- package/dist/tools/engine/text-animations.js.map +1 -0
- package/dist/tools/engine/text-overlay.d.ts +27 -0
- package/dist/tools/engine/text-overlay.js +84 -0
- package/dist/tools/engine/text-overlay.js.map +1 -0
- package/dist/tools/engine/tts.d.ts +54 -0
- package/dist/tools/engine/tts.js +186 -0
- package/dist/tools/engine/tts.js.map +1 -0
- package/dist/tools/engine/types.d.ts +166 -0
- package/dist/tools/engine/types.js +13 -0
- package/dist/tools/engine/types.js.map +1 -0
- package/dist/tools/engine/voice-effects.d.ts +18 -0
- package/dist/tools/engine/voice-effects.js +215 -0
- package/dist/tools/engine/voice-effects.js.map +1 -0
- package/dist/tools/index.d.ts +32 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/package.json +56 -0
- package/scripts/check-deps.js +39 -0
- package/src/handlers/capcut.ts +245 -0
- package/src/handlers/editing.ts +260 -0
- package/src/handlers/index.ts +34 -0
- package/src/handlers/post-production.ts +136 -0
- package/src/handlers/smart-screenshot.ts +86 -0
- package/src/handlers/tts.ts +103 -0
- package/src/handlers/video.ts +137 -0
- package/src/lib/dual-transport.ts +272 -0
- package/src/lib/logger.ts +59 -0
- package/src/lib/types.ts +25 -0
- package/src/schemas/capcut.ts +418 -0
- package/src/schemas/editing.ts +476 -0
- package/src/schemas/index.ts +15 -0
- package/src/schemas/post-production.ts +273 -0
- package/src/schemas/smart-screenshot.ts +122 -0
- package/src/schemas/tts.ts +197 -0
- package/src/schemas/video.ts +211 -0
- package/src/server.test.ts +99 -0
- package/src/server.ts +289 -0
- package/src/tools/engine/audio-mixer.ts +244 -0
- package/src/tools/engine/audio.ts +115 -0
- package/src/tools/engine/beat-sync.ts +356 -0
- package/src/tools/engine/capture.ts +360 -0
- package/src/tools/engine/chroma-key.ts +202 -0
- package/src/tools/engine/concat.ts +242 -0
- package/src/tools/engine/cursor.ts +222 -0
- package/src/tools/engine/easing.ts +120 -0
- package/src/tools/engine/editing.ts +809 -0
- package/src/tools/engine/encoder.ts +208 -0
- package/src/tools/engine/index.ts +33 -0
- package/src/tools/engine/lut-presets.ts +235 -0
- package/src/tools/engine/narrated-video.ts +267 -0
- package/src/tools/engine/scenes.ts +309 -0
- package/src/tools/engine/smart-screenshot.ts +923 -0
- package/src/tools/engine/social-format.ts +146 -0
- package/src/tools/engine/template-renderer.ts +294 -0
- package/src/tools/engine/templates.ts +370 -0
- package/src/tools/engine/text-animations.ts +282 -0
- package/src/tools/engine/text-overlay.ts +143 -0
- package/src/tools/engine/tts.ts +284 -0
- package/src/tools/engine/types.ts +191 -0
- package/src/tools/engine/voice-effects.ts +258 -0
- package/src/tools/index.ts +67 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +7 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Video Production — MCP Server v1.0.0
|
|
4
|
+
*
|
|
5
|
+
* 8 consolidated tools (from 33+) for recording, editing, color grading,
|
|
6
|
+
* audio, text, compositing, speech/narration, and smart screenshots.
|
|
7
|
+
*
|
|
8
|
+
* Port: 9847 (HTTP mode, configurable via MCP_PORT)
|
|
9
|
+
*/
|
|
10
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { logger } from './lib/logger.js';
|
|
13
|
+
import { startDualTransport } from './lib/dual-transport.js';
|
|
14
|
+
import { videoHandlers } from './handlers/video.js';
|
|
15
|
+
import { editingHandlers } from './handlers/editing.js';
|
|
16
|
+
import { capcutHandlers } from './handlers/capcut.js';
|
|
17
|
+
import { postProductionHandlers } from './handlers/post-production.js';
|
|
18
|
+
import { ttsHandlers } from './handlers/tts.js';
|
|
19
|
+
import { smartScreenshotHandlers } from './handlers/smart-screenshot.js';
|
|
20
|
+
const SERVER_NAME = 'mcp-video';
|
|
21
|
+
const SERVER_VERSION = '1.0.0';
|
|
22
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
23
|
+
const ALL_HANDLERS = {
|
|
24
|
+
...videoHandlers,
|
|
25
|
+
...editingHandlers,
|
|
26
|
+
...capcutHandlers,
|
|
27
|
+
...postProductionHandlers,
|
|
28
|
+
...ttsHandlers,
|
|
29
|
+
...smartScreenshotHandlers,
|
|
30
|
+
};
|
|
31
|
+
/** Delegate to existing handler by original tool name, fix content type literals */
|
|
32
|
+
async function delegate(name, args) {
|
|
33
|
+
const handler = ALL_HANDLERS[name];
|
|
34
|
+
if (!handler)
|
|
35
|
+
throw new Error(`Unknown handler: ${name}`);
|
|
36
|
+
const result = await handler(args);
|
|
37
|
+
// Ensure content items have literal 'text' type for MCP SDK
|
|
38
|
+
if (result?.content) {
|
|
39
|
+
for (const item of result.content) {
|
|
40
|
+
if (item.type === 'text')
|
|
41
|
+
item.type = 'text';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
export function createMcpServer() {
|
|
47
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, {
|
|
48
|
+
instructions: `# Video Server — Tool Selection Guide
|
|
49
|
+
|
|
50
|
+
You have 8 tools for video production. Each has a \`type\` parameter to select the operation.
|
|
51
|
+
|
|
52
|
+
## Recording:
|
|
53
|
+
- **video_record** — Record websites: cinema (full control), scroll (quick scroll-through), multi-device (desktop+tablet+mobile)
|
|
54
|
+
|
|
55
|
+
## Editing:
|
|
56
|
+
- **video_edit** — Edit clips: speed (slow-mo/timelapse), crop, reverse, keyframe (zoom/pan), pip (picture-in-picture)
|
|
57
|
+
- **video_color** — Color & effects: grade (brightness/contrast/etc), effect (blur/sharpen/vignette/etc), lut (22 cinema presets), chroma (green screen)
|
|
58
|
+
- **video_audio** — Audio: extract, music (background), ducking (auto volume), mix (multi-track), voice (effects like echo/robot/whisper)
|
|
59
|
+
- **video_text** — Text & captions: subtitles (burn SRT), caption (Whisper AI auto-caption), overlay (animated text layers), animate (15 text animations)
|
|
60
|
+
|
|
61
|
+
## Post-Production:
|
|
62
|
+
- **video_compose** — Compose: concat (join clips), intro (animated intro/outro), social (format for Instagram/TikTok/etc), social-all (batch all platforms), beat-sync (cuts on music beats), templates (list), render (from template)
|
|
63
|
+
|
|
64
|
+
## Speech & AI:
|
|
65
|
+
- **video_speech** — TTS & narration: generate (ElevenLabs/OpenAI speech), voices (list available), narrated (full narrated video from script)
|
|
66
|
+
|
|
67
|
+
## Screenshots:
|
|
68
|
+
- **video_screenshot** — Smart screenshots: capture (element-aware), detect (page feature analysis)
|
|
69
|
+
|
|
70
|
+
## Decision Flow:
|
|
71
|
+
1. Record website? → video_record
|
|
72
|
+
2. Edit existing video? → video_edit
|
|
73
|
+
3. Color/effects? → video_color
|
|
74
|
+
4. Audio work? → video_audio
|
|
75
|
+
5. Text/captions? → video_text
|
|
76
|
+
6. Join clips/format? → video_compose
|
|
77
|
+
7. Need voiceover? → video_speech
|
|
78
|
+
8. Screenshots? → video_screenshot`,
|
|
79
|
+
});
|
|
80
|
+
// ── 1. video_record ────────────────────────────────────
|
|
81
|
+
server.registerTool('video_record', {
|
|
82
|
+
title: 'Record Website Video',
|
|
83
|
+
description: 'Record website videos. Types: cinema (full 60fps with scenes/cursor), scroll (quick scroll-through), multi-device (desktop+tablet+mobile).',
|
|
84
|
+
inputSchema: z.object({
|
|
85
|
+
type: z.enum(['cinema', 'scroll', 'multi-device']).describe('Recording type'),
|
|
86
|
+
url: z.string().min(1).describe('Website URL'),
|
|
87
|
+
}).passthrough(),
|
|
88
|
+
annotations: { title: 'Record Video', readOnlyHint: false, openWorldHint: true },
|
|
89
|
+
}, async (args) => {
|
|
90
|
+
const { type, ...rest } = args;
|
|
91
|
+
const handlerName = type === 'cinema' ? 'record_website_video'
|
|
92
|
+
: type === 'scroll' ? 'record_website_scroll'
|
|
93
|
+
: 'record_multi_device';
|
|
94
|
+
return await delegate(handlerName, rest);
|
|
95
|
+
});
|
|
96
|
+
// ── 2. video_edit ──────────────────────────────────────
|
|
97
|
+
server.registerTool('video_edit', {
|
|
98
|
+
title: 'Edit Video',
|
|
99
|
+
description: 'Edit video clips. Types: speed (0.25x-4x), crop (region), reverse (± audio), keyframe (zoom/pan animation), pip (picture-in-picture overlay).',
|
|
100
|
+
inputSchema: z.object({
|
|
101
|
+
type: z.enum(['speed', 'crop', 'reverse', 'keyframe', 'pip']).describe('Edit operation'),
|
|
102
|
+
}).passthrough(),
|
|
103
|
+
annotations: { title: 'Edit Video', readOnlyHint: false },
|
|
104
|
+
}, async (args) => {
|
|
105
|
+
const { type, ...rest } = args;
|
|
106
|
+
const map = {
|
|
107
|
+
speed: 'adjust_video_speed', crop: 'crop_video', reverse: 'reverse_clip',
|
|
108
|
+
keyframe: 'add_keyframe_animation', pip: 'compose_picture_in_pip',
|
|
109
|
+
};
|
|
110
|
+
return await delegate(map[type], rest);
|
|
111
|
+
});
|
|
112
|
+
// ── 3. video_color ─────────────────────────────────────
|
|
113
|
+
server.registerTool('video_color', {
|
|
114
|
+
title: 'Color & Effects',
|
|
115
|
+
description: 'Color grading & visual effects. Types: grade (brightness/contrast/saturation/gamma/temperature), effect (blur/sharpen/vignette/grayscale/sepia/noise/glow), lut (22 cinema presets), chroma (green/blue screen replacement).',
|
|
116
|
+
inputSchema: z.object({
|
|
117
|
+
type: z.enum(['grade', 'effect', 'lut', 'chroma']).describe('Color operation'),
|
|
118
|
+
}).passthrough(),
|
|
119
|
+
annotations: { title: 'Color & Effects', readOnlyHint: false },
|
|
120
|
+
}, async (args) => {
|
|
121
|
+
const { type, ...rest } = args;
|
|
122
|
+
const map = {
|
|
123
|
+
grade: 'apply_color_grade', effect: 'apply_video_effect',
|
|
124
|
+
lut: 'apply_lut_preset', chroma: 'apply_chroma_key',
|
|
125
|
+
};
|
|
126
|
+
return await delegate(map[type], rest);
|
|
127
|
+
});
|
|
128
|
+
// ── 4. video_audio ─────────────────────────────────────
|
|
129
|
+
server.registerTool('video_audio', {
|
|
130
|
+
title: 'Audio Tools',
|
|
131
|
+
description: 'Audio operations. Types: extract (MP3/AAC/WAV/FLAC from video), music (add background music with fade), ducking (auto volume reduction), mix (multi-track mixing), voice (9 voice effects: echo/reverb/deep/chipmunk/robot/whisper/radio/megaphone/underwater).',
|
|
132
|
+
inputSchema: z.object({
|
|
133
|
+
type: z.enum(['extract', 'music', 'ducking', 'mix', 'voice']).describe('Audio operation'),
|
|
134
|
+
}).passthrough(),
|
|
135
|
+
annotations: { title: 'Audio', readOnlyHint: false },
|
|
136
|
+
}, async (args) => {
|
|
137
|
+
const { type, ...rest } = args;
|
|
138
|
+
const map = {
|
|
139
|
+
extract: 'extract_audio', music: 'add_background_music',
|
|
140
|
+
ducking: 'add_audio_ducking', mix: 'mix_audio_tracks', voice: 'apply_voice_effect',
|
|
141
|
+
};
|
|
142
|
+
return await delegate(map[type], rest);
|
|
143
|
+
});
|
|
144
|
+
// ── 5. video_text ──────────────────────────────────────
|
|
145
|
+
server.registerTool('video_text', {
|
|
146
|
+
title: 'Text & Captions',
|
|
147
|
+
description: 'Text overlays & captions. Types: subtitles (burn SRT/ASS), caption (Whisper AI auto-caption), overlay (animated text layers), animate (15 text animation styles).',
|
|
148
|
+
inputSchema: z.object({
|
|
149
|
+
type: z.enum(['subtitles', 'caption', 'overlay', 'animate']).describe('Text operation'),
|
|
150
|
+
}).passthrough(),
|
|
151
|
+
annotations: { title: 'Text & Captions', readOnlyHint: false },
|
|
152
|
+
}, async (args) => {
|
|
153
|
+
const { type, ...rest } = args;
|
|
154
|
+
const map = {
|
|
155
|
+
subtitles: 'burn_subtitles', caption: 'auto_caption',
|
|
156
|
+
overlay: 'add_text_overlay', animate: 'animate_text',
|
|
157
|
+
};
|
|
158
|
+
return await delegate(map[type], rest);
|
|
159
|
+
});
|
|
160
|
+
// ── 6. video_compose ───────────────────────────────────
|
|
161
|
+
server.registerTool('video_compose', {
|
|
162
|
+
title: 'Compose & Export',
|
|
163
|
+
description: 'Compose and export videos. Types: concat (join clips with transitions), intro (animated intro/outro), social (convert to Instagram/TikTok/YouTube/LinkedIn), social-all (batch all platforms), beat-sync (cut on music beats), templates (list available), render (render from template).',
|
|
164
|
+
inputSchema: z.object({
|
|
165
|
+
type: z.enum(['concat', 'intro', 'social', 'social-all', 'beat-sync', 'templates', 'render']).describe('Compose operation'),
|
|
166
|
+
}).passthrough(),
|
|
167
|
+
annotations: { title: 'Compose & Export', readOnlyHint: false },
|
|
168
|
+
}, async (args) => {
|
|
169
|
+
const { type, ...rest } = args;
|
|
170
|
+
const map = {
|
|
171
|
+
concat: 'concatenate_videos', intro: 'generate_intro',
|
|
172
|
+
social: 'convert_social_format', 'social-all': 'convert_all_social_formats',
|
|
173
|
+
'beat-sync': 'sync_to_beat', templates: 'list_video_templates', render: 'render_template',
|
|
174
|
+
};
|
|
175
|
+
return await delegate(map[type], rest);
|
|
176
|
+
});
|
|
177
|
+
// ── 7. video_speech ────────────────────────────────────
|
|
178
|
+
server.registerTool('video_speech', {
|
|
179
|
+
title: 'Speech & Narration',
|
|
180
|
+
description: 'Text-to-speech & narrated videos. Types: generate (ElevenLabs/OpenAI TTS), voices (list available voices), narrated (full narrated video: script → TTS → website recording → sync → MP4).',
|
|
181
|
+
inputSchema: z.object({
|
|
182
|
+
type: z.enum(['generate', 'voices', 'narrated']).describe('Speech operation'),
|
|
183
|
+
}).passthrough(),
|
|
184
|
+
annotations: { title: 'Speech & Narration', readOnlyHint: false, openWorldHint: true },
|
|
185
|
+
}, async (args) => {
|
|
186
|
+
const { type, ...rest } = args;
|
|
187
|
+
const map = {
|
|
188
|
+
generate: 'generate_speech', voices: 'list_voices', narrated: 'create_narrated_video',
|
|
189
|
+
};
|
|
190
|
+
return await delegate(map[type], rest);
|
|
191
|
+
});
|
|
192
|
+
// ── 8. video_screenshot ────────────────────────────────
|
|
193
|
+
server.registerTool('video_screenshot', {
|
|
194
|
+
title: 'Smart Screenshot',
|
|
195
|
+
description: 'Smart element-aware screenshots. Types: capture (detect & screenshot specific page features: chat, pricing, hero, etc.), detect (analyze page features without screenshotting).',
|
|
196
|
+
inputSchema: z.object({
|
|
197
|
+
type: z.enum(['capture', 'detect']).describe('Screenshot operation'),
|
|
198
|
+
url: z.string().min(1).describe('Website URL'),
|
|
199
|
+
}).passthrough(),
|
|
200
|
+
annotations: { title: 'Smart Screenshot', readOnlyHint: true, openWorldHint: true },
|
|
201
|
+
}, async (args) => {
|
|
202
|
+
const { type, ...rest } = args;
|
|
203
|
+
const handlerName = type === 'capture' ? 'screenshot_element' : 'detect_page_features';
|
|
204
|
+
return await delegate(handlerName, rest);
|
|
205
|
+
});
|
|
206
|
+
return server;
|
|
207
|
+
}
|
|
208
|
+
// ─── Startup Validation ──────────────────────────────────
|
|
209
|
+
import { execSync } from 'child_process';
|
|
210
|
+
import * as fs from 'fs';
|
|
211
|
+
function checkDependencies() {
|
|
212
|
+
for (const bin of ['ffmpeg', 'ffprobe']) {
|
|
213
|
+
try {
|
|
214
|
+
execSync(`which ${bin}`, { stdio: 'pipe' });
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
logger.error(`${bin} not found. Install ffmpeg: https://ffmpeg.org/download.html`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const outputDir = process.env.VIDEO_OUTPUT_DIR || './output';
|
|
222
|
+
if (!fs.existsSync(outputDir)) {
|
|
223
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
224
|
+
logger.info(`Created output directory: ${outputDir}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// ─── Start ────────────────────────────────────────────────
|
|
228
|
+
checkDependencies();
|
|
229
|
+
startDualTransport(createMcpServer, {
|
|
230
|
+
serverName: SERVER_NAME,
|
|
231
|
+
serverVersion: SERVER_VERSION,
|
|
232
|
+
defaultPort: 9847,
|
|
233
|
+
}).then((result) => {
|
|
234
|
+
logger.info(`Video MCP Server running — 8 tools (${result.type}${result.port ? ` :${result.port}` : ''})`);
|
|
235
|
+
}).catch((error) => {
|
|
236
|
+
logger.logError('Fatal error', error);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
});
|
|
239
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
vi.mock('./lib/logger.js', () => ({
|
|
3
|
+
logger: { info: vi.fn(), logError: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
|
|
4
|
+
}));
|
|
5
|
+
vi.mock('./lib/dual-transport.js', () => ({
|
|
6
|
+
startDualTransport: vi.fn().mockResolvedValue({ type: 'stdio' }),
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('./lib/types.js', () => ({
|
|
9
|
+
jsonResponse: (data, isError) => ({
|
|
10
|
+
content: [{ type: 'text', text: JSON.stringify(data) }],
|
|
11
|
+
...(isError ? { isError: true } : {}),
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
vi.mock('playwright', () => ({
|
|
15
|
+
chromium: { launch: vi.fn() },
|
|
16
|
+
}));
|
|
17
|
+
describe('createMcpServer', () => {
|
|
18
|
+
it('creates server with 8 tools', async () => {
|
|
19
|
+
const { createMcpServer } = await import('./server.js');
|
|
20
|
+
const server = createMcpServer();
|
|
21
|
+
expect(server).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('delegate function', () => {
|
|
25
|
+
it('server exports createMcpServer', async () => {
|
|
26
|
+
const mod = await import('./server.js');
|
|
27
|
+
expect(typeof mod.createMcpServer).toBe('function');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('handler registries', () => {
|
|
31
|
+
it('video handlers exist', async () => {
|
|
32
|
+
const { videoHandlers } = await import('./handlers/video.js');
|
|
33
|
+
expect(videoHandlers).toBeDefined();
|
|
34
|
+
expect(videoHandlers.record_website_video).toBeDefined();
|
|
35
|
+
expect(videoHandlers.record_website_scroll).toBeDefined();
|
|
36
|
+
expect(videoHandlers.record_multi_device).toBeDefined();
|
|
37
|
+
});
|
|
38
|
+
it('editing handlers exist', async () => {
|
|
39
|
+
const { editingHandlers } = await import('./handlers/editing.js');
|
|
40
|
+
expect(editingHandlers).toBeDefined();
|
|
41
|
+
expect(editingHandlers.adjust_video_speed).toBeDefined();
|
|
42
|
+
expect(editingHandlers.crop_video).toBeDefined();
|
|
43
|
+
expect(editingHandlers.reverse_clip).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
it('capcut handlers exist', async () => {
|
|
46
|
+
const { capcutHandlers } = await import('./handlers/capcut.js');
|
|
47
|
+
expect(capcutHandlers).toBeDefined();
|
|
48
|
+
expect(capcutHandlers.apply_lut_preset).toBeDefined();
|
|
49
|
+
expect(capcutHandlers.sync_to_beat).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
it('post-production handlers exist', async () => {
|
|
52
|
+
const { postProductionHandlers } = await import('./handlers/post-production.js');
|
|
53
|
+
expect(postProductionHandlers).toBeDefined();
|
|
54
|
+
expect(postProductionHandlers.concatenate_videos).toBeDefined();
|
|
55
|
+
expect(postProductionHandlers.convert_social_format).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
it('tts handlers exist', async () => {
|
|
58
|
+
const { ttsHandlers } = await import('./handlers/tts.js');
|
|
59
|
+
expect(ttsHandlers).toBeDefined();
|
|
60
|
+
expect(ttsHandlers.generate_speech).toBeDefined();
|
|
61
|
+
expect(ttsHandlers.list_voices).toBeDefined();
|
|
62
|
+
expect(ttsHandlers.create_narrated_video).toBeDefined();
|
|
63
|
+
});
|
|
64
|
+
it('screenshot handlers exist', async () => {
|
|
65
|
+
const { smartScreenshotHandlers } = await import('./handlers/smart-screenshot.js');
|
|
66
|
+
expect(smartScreenshotHandlers).toBeDefined();
|
|
67
|
+
expect(smartScreenshotHandlers.screenshot_element).toBeDefined();
|
|
68
|
+
expect(smartScreenshotHandlers.detect_page_features).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('tool consolidation', () => {
|
|
72
|
+
it('all handler names are covered by delegate map', () => {
|
|
73
|
+
const allOriginalNames = [
|
|
74
|
+
'record_website_video', 'record_website_scroll', 'record_multi_device',
|
|
75
|
+
'adjust_video_speed', 'crop_video', 'reverse_clip', 'add_keyframe_animation', 'compose_picture_in_pip',
|
|
76
|
+
'apply_color_grade', 'apply_video_effect', 'apply_lut_preset', 'apply_chroma_key',
|
|
77
|
+
'extract_audio', 'add_background_music', 'add_audio_ducking', 'mix_audio_tracks', 'apply_voice_effect',
|
|
78
|
+
'burn_subtitles', 'auto_caption', 'add_text_overlay', 'animate_text',
|
|
79
|
+
'concatenate_videos', 'generate_intro', 'convert_social_format', 'convert_all_social_formats',
|
|
80
|
+
'sync_to_beat', 'list_video_templates', 'render_template',
|
|
81
|
+
'generate_speech', 'list_voices', 'create_narrated_video',
|
|
82
|
+
'screenshot_element', 'detect_page_features',
|
|
83
|
+
];
|
|
84
|
+
expect(allOriginalNames.length).toBe(33);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../src/server.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Mixer Engine — Multi-track audio mixing with auto-ducking.
|
|
3
|
+
*
|
|
4
|
+
* Mixes N audio tracks (voiceover + music + SFX) into one.
|
|
5
|
+
* Auto-ducking: automatically lowers music volume when speech is detected.
|
|
6
|
+
* Per-track: volume, fade in/out.
|
|
7
|
+
*/
|
|
8
|
+
export interface AudioTrack {
|
|
9
|
+
/** Path to audio or video file (audio stream will be used) */
|
|
10
|
+
path: string;
|
|
11
|
+
/** Volume: 0.0-2.0 (1.0 = original). Default: 1.0 */
|
|
12
|
+
volume?: number;
|
|
13
|
+
/** Fade in duration in seconds (default: 0) */
|
|
14
|
+
fadeIn?: number;
|
|
15
|
+
/** Fade out duration in seconds (default: 0) */
|
|
16
|
+
fadeOut?: number;
|
|
17
|
+
/** Start time offset in seconds — delay this track. Default: 0 */
|
|
18
|
+
delay?: number;
|
|
19
|
+
/** Track role for auto-ducking: 'voice' tracks trigger ducking on 'music' tracks */
|
|
20
|
+
role?: 'voice' | 'music' | 'sfx';
|
|
21
|
+
}
|
|
22
|
+
export interface AudioMixConfig {
|
|
23
|
+
/** Audio/video tracks to mix together */
|
|
24
|
+
tracks: AudioTrack[];
|
|
25
|
+
outputPath: string;
|
|
26
|
+
/** Enable auto-ducking: music volume reduces when voice is active. Default: false */
|
|
27
|
+
autoDuck?: boolean;
|
|
28
|
+
/** How much to reduce music volume during speech: 0.0-1.0 (0.2 = reduce to 20%). Default: 0.2 */
|
|
29
|
+
duckLevel?: number;
|
|
30
|
+
/** Output format: 'mp3', 'aac', 'wav'. Default: 'aac' */
|
|
31
|
+
format?: 'mp3' | 'aac' | 'wav';
|
|
32
|
+
/** Duration of output in seconds. If omitted, uses longest track. */
|
|
33
|
+
duration?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface AudioMixResult {
|
|
36
|
+
outputPath: string;
|
|
37
|
+
trackCount: number;
|
|
38
|
+
ducking: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function mixAudioTracks(config: AudioMixConfig): Promise<AudioMixResult>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Mixer Engine — Multi-track audio mixing with auto-ducking.
|
|
3
|
+
*
|
|
4
|
+
* Mixes N audio tracks (voiceover + music + SFX) into one.
|
|
5
|
+
* Auto-ducking: automatically lowers music volume when speech is detected.
|
|
6
|
+
* Per-track: volume, fade in/out.
|
|
7
|
+
*/
|
|
8
|
+
import { execFile } from 'child_process';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { logger } from '../../lib/logger.js';
|
|
12
|
+
// ─── Helpers ────────────────────────────────────────────────────────
|
|
13
|
+
function runFfmpeg(args, timeoutMs = 300_000) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
execFile('ffmpeg', args, { maxBuffer: 100 * 1024 * 1024, timeout: timeoutMs }, (error, stdout, stderr) => {
|
|
16
|
+
if (error) {
|
|
17
|
+
logger.error(`ffmpeg failed: ${stderr}`);
|
|
18
|
+
reject(new Error(`ffmpeg failed: ${stderr || error.message}`));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
resolve(stdout);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function ensureDir(filePath) {
|
|
26
|
+
const dir = path.dirname(filePath);
|
|
27
|
+
if (!fs.existsSync(dir))
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
function assertExists(filePath, label = 'File') {
|
|
31
|
+
if (!fs.existsSync(filePath))
|
|
32
|
+
throw new Error(`${label} not found: ${filePath}`);
|
|
33
|
+
}
|
|
34
|
+
function fileInfo(filePath) {
|
|
35
|
+
const stats = fs.statSync(filePath);
|
|
36
|
+
return `${(stats.size / 1024 / 1024).toFixed(2)} MB`;
|
|
37
|
+
}
|
|
38
|
+
function getMediaDuration(filePath) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
execFile('ffprobe', ['-v', 'quiet', '-show_entries', 'format=duration', '-of', 'csv=p=0', filePath], (error, stdout) => {
|
|
41
|
+
if (error) {
|
|
42
|
+
reject(new Error(`ffprobe failed: ${error.message}`));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const dur = parseFloat(stdout.trim());
|
|
46
|
+
resolve(isNaN(dur) ? 0 : dur);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// ─── Main Function ──────────────────────────────────────────────────
|
|
51
|
+
export async function mixAudioTracks(config) {
|
|
52
|
+
const { tracks, outputPath, autoDuck = false, duckLevel = 0.2, format = 'aac', duration, } = config;
|
|
53
|
+
if (tracks.length < 2)
|
|
54
|
+
throw new Error('Need at least 2 audio tracks to mix');
|
|
55
|
+
if (tracks.length > 8)
|
|
56
|
+
throw new Error('Maximum 8 tracks supported');
|
|
57
|
+
// Validate all files exist
|
|
58
|
+
for (const track of tracks) {
|
|
59
|
+
assertExists(track.path, `Audio track`);
|
|
60
|
+
}
|
|
61
|
+
ensureDir(outputPath);
|
|
62
|
+
logger.info(`Mixing ${tracks.length} audio tracks (duck: ${autoDuck}, format: ${format})`);
|
|
63
|
+
const args = ['-y'];
|
|
64
|
+
const filterParts = [];
|
|
65
|
+
const inputLabels = [];
|
|
66
|
+
// Add inputs
|
|
67
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
68
|
+
args.push('-i', tracks[i].path);
|
|
69
|
+
}
|
|
70
|
+
// Build per-track filter chains
|
|
71
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
72
|
+
const track = tracks[i];
|
|
73
|
+
const vol = track.volume ?? 1.0;
|
|
74
|
+
const fadeIn = track.fadeIn ?? 0;
|
|
75
|
+
const fadeOut = track.fadeOut ?? 0;
|
|
76
|
+
const delay = track.delay ?? 0;
|
|
77
|
+
const subFilters = [];
|
|
78
|
+
// Volume adjustment
|
|
79
|
+
if (vol !== 1.0) {
|
|
80
|
+
subFilters.push(`volume=${vol.toFixed(3)}`);
|
|
81
|
+
}
|
|
82
|
+
// Delay (adelay in milliseconds)
|
|
83
|
+
if (delay > 0) {
|
|
84
|
+
subFilters.push(`adelay=${Math.round(delay * 1000)}|${Math.round(delay * 1000)}`);
|
|
85
|
+
}
|
|
86
|
+
// Fade in
|
|
87
|
+
if (fadeIn > 0) {
|
|
88
|
+
subFilters.push(`afade=t=in:st=0:d=${fadeIn}`);
|
|
89
|
+
}
|
|
90
|
+
// Fade out — need duration for this
|
|
91
|
+
if (fadeOut > 0) {
|
|
92
|
+
try {
|
|
93
|
+
const dur = duration ?? await getMediaDuration(track.path);
|
|
94
|
+
if (dur > fadeOut) {
|
|
95
|
+
subFilters.push(`afade=t=out:st=${(dur - fadeOut).toFixed(2)}:d=${fadeOut}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Skip fade out if we can't get duration
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const label = `a${i}`;
|
|
103
|
+
if (subFilters.length > 0) {
|
|
104
|
+
filterParts.push(`[${i}:a]${subFilters.join(',')}[${label}]`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
filterParts.push(`[${i}:a]anull[${label}]`);
|
|
108
|
+
}
|
|
109
|
+
inputLabels.push(`[${label}]`);
|
|
110
|
+
}
|
|
111
|
+
if (autoDuck && tracks.some(t => t.role === 'voice') && tracks.some(t => t.role === 'music')) {
|
|
112
|
+
// Auto-ducking: use sidechaincompress on music tracks triggered by voice
|
|
113
|
+
const voiceIdx = tracks.findIndex(t => t.role === 'voice');
|
|
114
|
+
const musicIdx = tracks.findIndex(t => t.role === 'music');
|
|
115
|
+
if (voiceIdx !== -1 && musicIdx !== -1) {
|
|
116
|
+
const voiceLabel = `a${voiceIdx}`;
|
|
117
|
+
const musicLabel = `a${musicIdx}`;
|
|
118
|
+
const duckRatio = Math.round(1 / Math.max(0.05, duckLevel));
|
|
119
|
+
// Sidechain compress: music is ducked when voice is loud
|
|
120
|
+
filterParts.push(`[${musicLabel}][${voiceLabel}]sidechaincompress=threshold=0.02:ratio=${duckRatio}:attack=20:release=300:level_sc=1[ducked]`);
|
|
121
|
+
// Build final mix — replace music label with ducked
|
|
122
|
+
const mixLabels = inputLabels.map((label, idx) => {
|
|
123
|
+
if (idx === musicIdx)
|
|
124
|
+
return '[ducked]';
|
|
125
|
+
if (idx === voiceIdx)
|
|
126
|
+
return `[${voiceLabel}]`;
|
|
127
|
+
return label;
|
|
128
|
+
});
|
|
129
|
+
// Use amix to combine all
|
|
130
|
+
filterParts.push(`${mixLabels.join('')}amix=inputs=${tracks.length}:duration=longest:dropout_transition=2:normalize=0[out]`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Fallback: simple amix
|
|
134
|
+
filterParts.push(`${inputLabels.join('')}amix=inputs=${tracks.length}:duration=longest:dropout_transition=2:normalize=0[out]`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Simple amix without ducking
|
|
139
|
+
filterParts.push(`${inputLabels.join('')}amix=inputs=${tracks.length}:duration=longest:dropout_transition=2:normalize=0[out]`);
|
|
140
|
+
}
|
|
141
|
+
args.push('-filter_complex', filterParts.join(';'));
|
|
142
|
+
args.push('-map', '[out]');
|
|
143
|
+
// Duration limit
|
|
144
|
+
if (duration) {
|
|
145
|
+
args.push('-t', String(duration));
|
|
146
|
+
}
|
|
147
|
+
// Output codec
|
|
148
|
+
switch (format) {
|
|
149
|
+
case 'mp3':
|
|
150
|
+
args.push('-c:a', 'libmp3lame', '-b:a', '192k');
|
|
151
|
+
break;
|
|
152
|
+
case 'wav':
|
|
153
|
+
args.push('-c:a', 'pcm_s16le');
|
|
154
|
+
break;
|
|
155
|
+
case 'aac':
|
|
156
|
+
default:
|
|
157
|
+
args.push('-c:a', 'aac', '-b:a', '192k');
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
args.push(outputPath);
|
|
161
|
+
await runFfmpeg(args);
|
|
162
|
+
logger.info(`Audio mixed: ${tracks.length} tracks → ${outputPath} (${fileInfo(outputPath)})`);
|
|
163
|
+
return {
|
|
164
|
+
outputPath,
|
|
165
|
+
trackCount: tracks.length,
|
|
166
|
+
ducking: autoDuck,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=audio-mixer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-mixer.js","sourceRoot":"","sources":["../../../src/tools/engine/audio-mixer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio engine — background music, fade, loop, volume control
|
|
3
|
+
* All processing via ffmpeg + ffprobe (no npm dependencies)
|
|
4
|
+
*/
|
|
5
|
+
export declare function getMediaDuration(filePath: string): Promise<number>;
|
|
6
|
+
export interface AddMusicConfig {
|
|
7
|
+
/** Path to video file */
|
|
8
|
+
videoPath: string;
|
|
9
|
+
/** Path to audio file (mp3, wav, aac, ogg) */
|
|
10
|
+
musicPath: string;
|
|
11
|
+
/** Output path */
|
|
12
|
+
outputPath: string;
|
|
13
|
+
/** Music volume 0.0-1.0 (default: 0.25) */
|
|
14
|
+
musicVolume?: number;
|
|
15
|
+
/** Fade in duration in seconds (default: 2) */
|
|
16
|
+
fadeInDuration?: number;
|
|
17
|
+
/** Fade out duration in seconds (default: 3) */
|
|
18
|
+
fadeOutDuration?: number;
|
|
19
|
+
/** Loop music if shorter than video (default: true) */
|
|
20
|
+
loopMusic?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function addBackgroundMusic(config: AddMusicConfig): Promise<string>;
|