@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.
Files changed (184) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  3. package/.github/workflows/ci.yml +34 -0
  4. package/CHANGELOG.md +24 -0
  5. package/CONTRIBUTING.md +75 -0
  6. package/LICENSE +21 -0
  7. package/README.md +198 -0
  8. package/USAGE.md +144 -0
  9. package/dist/handlers/capcut.d.ts +6 -0
  10. package/dist/handlers/capcut.js +229 -0
  11. package/dist/handlers/capcut.js.map +1 -0
  12. package/dist/handlers/editing.d.ts +6 -0
  13. package/dist/handlers/editing.js +242 -0
  14. package/dist/handlers/editing.js.map +1 -0
  15. package/dist/handlers/index.d.ts +2 -0
  16. package/dist/handlers/index.js +33 -0
  17. package/dist/handlers/index.js.map +1 -0
  18. package/dist/handlers/post-production.d.ts +5 -0
  19. package/dist/handlers/post-production.js +109 -0
  20. package/dist/handlers/post-production.js.map +1 -0
  21. package/dist/handlers/smart-screenshot.d.ts +5 -0
  22. package/dist/handlers/smart-screenshot.js +83 -0
  23. package/dist/handlers/smart-screenshot.js.map +1 -0
  24. package/dist/handlers/tts.d.ts +5 -0
  25. package/dist/handlers/tts.js +83 -0
  26. package/dist/handlers/tts.js.map +1 -0
  27. package/dist/handlers/video.d.ts +5 -0
  28. package/dist/handlers/video.js +127 -0
  29. package/dist/handlers/video.js.map +1 -0
  30. package/dist/lib/dual-transport.d.ts +42 -0
  31. package/dist/lib/dual-transport.js +208 -0
  32. package/dist/lib/dual-transport.js.map +1 -0
  33. package/dist/lib/logger.d.ts +12 -0
  34. package/dist/lib/logger.js +42 -0
  35. package/dist/lib/logger.js.map +1 -0
  36. package/dist/lib/types.d.ts +16 -0
  37. package/dist/lib/types.js +15 -0
  38. package/dist/lib/types.js.map +1 -0
  39. package/dist/schemas/capcut.d.ts +608 -0
  40. package/dist/schemas/capcut.js +411 -0
  41. package/dist/schemas/capcut.js.map +1 -0
  42. package/dist/schemas/editing.d.ts +822 -0
  43. package/dist/schemas/editing.js +466 -0
  44. package/dist/schemas/editing.js.map +1 -0
  45. package/dist/schemas/index.d.ts +2366 -0
  46. package/dist/schemas/index.js +15 -0
  47. package/dist/schemas/index.js.map +1 -0
  48. package/dist/schemas/post-production.d.ts +379 -0
  49. package/dist/schemas/post-production.js +268 -0
  50. package/dist/schemas/post-production.js.map +1 -0
  51. package/dist/schemas/smart-screenshot.d.ts +127 -0
  52. package/dist/schemas/smart-screenshot.js +122 -0
  53. package/dist/schemas/smart-screenshot.js.map +1 -0
  54. package/dist/schemas/tts.d.ts +220 -0
  55. package/dist/schemas/tts.js +194 -0
  56. package/dist/schemas/tts.js.map +1 -0
  57. package/dist/schemas/video.d.ts +236 -0
  58. package/dist/schemas/video.js +210 -0
  59. package/dist/schemas/video.js.map +1 -0
  60. package/dist/server.d.ts +11 -0
  61. package/dist/server.js +239 -0
  62. package/dist/server.js.map +1 -0
  63. package/dist/server.test.d.ts +1 -0
  64. package/dist/server.test.js +87 -0
  65. package/dist/server.test.js.map +1 -0
  66. package/dist/tools/engine/audio-mixer.d.ts +40 -0
  67. package/dist/tools/engine/audio-mixer.js +169 -0
  68. package/dist/tools/engine/audio-mixer.js.map +1 -0
  69. package/dist/tools/engine/audio.d.ts +22 -0
  70. package/dist/tools/engine/audio.js +73 -0
  71. package/dist/tools/engine/audio.js.map +1 -0
  72. package/dist/tools/engine/beat-sync.d.ts +31 -0
  73. package/dist/tools/engine/beat-sync.js +270 -0
  74. package/dist/tools/engine/beat-sync.js.map +1 -0
  75. package/dist/tools/engine/capture.d.ts +12 -0
  76. package/dist/tools/engine/capture.js +290 -0
  77. package/dist/tools/engine/capture.js.map +1 -0
  78. package/dist/tools/engine/chroma-key.d.ts +27 -0
  79. package/dist/tools/engine/chroma-key.js +154 -0
  80. package/dist/tools/engine/chroma-key.js.map +1 -0
  81. package/dist/tools/engine/concat.d.ts +49 -0
  82. package/dist/tools/engine/concat.js +149 -0
  83. package/dist/tools/engine/concat.js.map +1 -0
  84. package/dist/tools/engine/cursor.d.ts +26 -0
  85. package/dist/tools/engine/cursor.js +185 -0
  86. package/dist/tools/engine/cursor.js.map +1 -0
  87. package/dist/tools/engine/easing.d.ts +15 -0
  88. package/dist/tools/engine/easing.js +100 -0
  89. package/dist/tools/engine/easing.js.map +1 -0
  90. package/dist/tools/engine/editing.d.ts +158 -0
  91. package/dist/tools/engine/editing.js +541 -0
  92. package/dist/tools/engine/editing.js.map +1 -0
  93. package/dist/tools/engine/encoder.d.ts +31 -0
  94. package/dist/tools/engine/encoder.js +154 -0
  95. package/dist/tools/engine/encoder.js.map +1 -0
  96. package/dist/tools/engine/index.d.ts +30 -0
  97. package/dist/tools/engine/index.js +23 -0
  98. package/dist/tools/engine/index.js.map +1 -0
  99. package/dist/tools/engine/lut-presets.d.ts +25 -0
  100. package/dist/tools/engine/lut-presets.js +141 -0
  101. package/dist/tools/engine/lut-presets.js.map +1 -0
  102. package/dist/tools/engine/narrated-video.d.ts +63 -0
  103. package/dist/tools/engine/narrated-video.js +163 -0
  104. package/dist/tools/engine/narrated-video.js.map +1 -0
  105. package/dist/tools/engine/scenes.d.ts +17 -0
  106. package/dist/tools/engine/scenes.js +223 -0
  107. package/dist/tools/engine/scenes.js.map +1 -0
  108. package/dist/tools/engine/smart-screenshot.d.ts +80 -0
  109. package/dist/tools/engine/smart-screenshot.js +744 -0
  110. package/dist/tools/engine/smart-screenshot.js.map +1 -0
  111. package/dist/tools/engine/social-format.d.ts +66 -0
  112. package/dist/tools/engine/social-format.js +107 -0
  113. package/dist/tools/engine/social-format.js.map +1 -0
  114. package/dist/tools/engine/template-renderer.d.ts +45 -0
  115. package/dist/tools/engine/template-renderer.js +233 -0
  116. package/dist/tools/engine/template-renderer.js.map +1 -0
  117. package/dist/tools/engine/templates.d.ts +87 -0
  118. package/dist/tools/engine/templates.js +272 -0
  119. package/dist/tools/engine/templates.js.map +1 -0
  120. package/dist/tools/engine/text-animations.d.ts +33 -0
  121. package/dist/tools/engine/text-animations.js +192 -0
  122. package/dist/tools/engine/text-animations.js.map +1 -0
  123. package/dist/tools/engine/text-overlay.d.ts +27 -0
  124. package/dist/tools/engine/text-overlay.js +84 -0
  125. package/dist/tools/engine/text-overlay.js.map +1 -0
  126. package/dist/tools/engine/tts.d.ts +54 -0
  127. package/dist/tools/engine/tts.js +186 -0
  128. package/dist/tools/engine/tts.js.map +1 -0
  129. package/dist/tools/engine/types.d.ts +166 -0
  130. package/dist/tools/engine/types.js +13 -0
  131. package/dist/tools/engine/types.js.map +1 -0
  132. package/dist/tools/engine/voice-effects.d.ts +18 -0
  133. package/dist/tools/engine/voice-effects.js +215 -0
  134. package/dist/tools/engine/voice-effects.js.map +1 -0
  135. package/dist/tools/index.d.ts +32 -0
  136. package/dist/tools/index.js +23 -0
  137. package/dist/tools/index.js.map +1 -0
  138. package/package.json +56 -0
  139. package/scripts/check-deps.js +39 -0
  140. package/src/handlers/capcut.ts +245 -0
  141. package/src/handlers/editing.ts +260 -0
  142. package/src/handlers/index.ts +34 -0
  143. package/src/handlers/post-production.ts +136 -0
  144. package/src/handlers/smart-screenshot.ts +86 -0
  145. package/src/handlers/tts.ts +103 -0
  146. package/src/handlers/video.ts +137 -0
  147. package/src/lib/dual-transport.ts +272 -0
  148. package/src/lib/logger.ts +59 -0
  149. package/src/lib/types.ts +25 -0
  150. package/src/schemas/capcut.ts +418 -0
  151. package/src/schemas/editing.ts +476 -0
  152. package/src/schemas/index.ts +15 -0
  153. package/src/schemas/post-production.ts +273 -0
  154. package/src/schemas/smart-screenshot.ts +122 -0
  155. package/src/schemas/tts.ts +197 -0
  156. package/src/schemas/video.ts +211 -0
  157. package/src/server.test.ts +99 -0
  158. package/src/server.ts +289 -0
  159. package/src/tools/engine/audio-mixer.ts +244 -0
  160. package/src/tools/engine/audio.ts +115 -0
  161. package/src/tools/engine/beat-sync.ts +356 -0
  162. package/src/tools/engine/capture.ts +360 -0
  163. package/src/tools/engine/chroma-key.ts +202 -0
  164. package/src/tools/engine/concat.ts +242 -0
  165. package/src/tools/engine/cursor.ts +222 -0
  166. package/src/tools/engine/easing.ts +120 -0
  167. package/src/tools/engine/editing.ts +809 -0
  168. package/src/tools/engine/encoder.ts +208 -0
  169. package/src/tools/engine/index.ts +33 -0
  170. package/src/tools/engine/lut-presets.ts +235 -0
  171. package/src/tools/engine/narrated-video.ts +267 -0
  172. package/src/tools/engine/scenes.ts +309 -0
  173. package/src/tools/engine/smart-screenshot.ts +923 -0
  174. package/src/tools/engine/social-format.ts +146 -0
  175. package/src/tools/engine/template-renderer.ts +294 -0
  176. package/src/tools/engine/templates.ts +370 -0
  177. package/src/tools/engine/text-animations.ts +282 -0
  178. package/src/tools/engine/text-overlay.ts +143 -0
  179. package/src/tools/engine/tts.ts +284 -0
  180. package/src/tools/engine/types.ts +191 -0
  181. package/src/tools/engine/voice-effects.ts +258 -0
  182. package/src/tools/index.ts +67 -0
  183. package/tsconfig.json +19 -0
  184. 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>;