@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
@@ -0,0 +1,541 @@
1
+ /**
2
+ * Video editing engine — speed, color grading, effects, crop, reverse,
3
+ * audio extraction, subtitles, keyframe animation, picture-in-picture.
4
+ *
5
+ * All processing via ffmpeg (no npm dependencies).
6
+ */
7
+ import { execFile } from 'child_process';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import { logger } from '../../lib/logger.js';
11
+ import { getMediaDuration } from './audio.js';
12
+ // ─── Shared ffmpeg runner ────────────────────────────────────────────
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
+ /** Check if a media file has an audio stream */
35
+ function hasAudioStream(filePath) {
36
+ return new Promise((resolve) => {
37
+ execFile('ffprobe', ['-v', 'quiet', '-select_streams', 'a', '-show_entries', 'stream=codec_type', '-of', 'csv=p=0', filePath], (error, stdout) => {
38
+ if (error) {
39
+ resolve(false);
40
+ return;
41
+ }
42
+ resolve(stdout.trim().length > 0);
43
+ });
44
+ });
45
+ }
46
+ function fileInfo(filePath) {
47
+ const stats = fs.statSync(filePath);
48
+ return `${(stats.size / 1024 / 1024).toFixed(2)} MB`;
49
+ }
50
+ export async function adjustVideoSpeed(config) {
51
+ const { inputPath, outputPath, speed, audioMode = 'match' } = config;
52
+ assertExists(inputPath, 'Input video');
53
+ ensureDir(outputPath);
54
+ if (speed < 0.25 || speed > 4.0)
55
+ throw new Error('Speed must be between 0.25 and 4.0');
56
+ const pts = (1 / speed).toFixed(6);
57
+ const hasAudio = await hasAudioStream(inputPath);
58
+ logger.info(`Adjusting speed: ${speed}x (PTS: ${pts}, audio: ${audioMode}, hasAudio: ${hasAudio})`);
59
+ const videoFilter = `setpts=${pts}*PTS`;
60
+ const args = ['-y', '-i', inputPath];
61
+ if (!hasAudio || audioMode === 'mute') {
62
+ args.push('-vf', videoFilter, '-an');
63
+ }
64
+ else if (audioMode === 'match') {
65
+ const atempoChain = buildAtempoChain(speed);
66
+ args.push('-filter_complex', `[0:v]${videoFilter}[v];[0:a]${atempoChain}[a]`);
67
+ args.push('-map', '[v]', '-map', '[a]');
68
+ }
69
+ else {
70
+ args.push('-vf', videoFilter, '-c:a', 'copy');
71
+ }
72
+ args.push('-c:v', 'libx264', '-crf', '18', '-preset', 'medium', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', outputPath);
73
+ await runFfmpeg(args);
74
+ logger.info(`Speed adjusted: ${outputPath} (${fileInfo(outputPath)})`);
75
+ return outputPath;
76
+ }
77
+ /** Build chained atempo filters (each 0.5-2.0 range) */
78
+ function buildAtempoChain(speed) {
79
+ const filters = [];
80
+ let remaining = speed;
81
+ while (remaining > 2.0) {
82
+ filters.push('atempo=2.0');
83
+ remaining /= 2.0;
84
+ }
85
+ while (remaining < 0.5) {
86
+ filters.push('atempo=0.5');
87
+ remaining /= 0.5;
88
+ }
89
+ filters.push(`atempo=${remaining.toFixed(4)}`);
90
+ return filters.join(',');
91
+ }
92
+ export async function applyColorGrade(config) {
93
+ const { inputPath, outputPath, brightness = 0, contrast = 1, saturation = 1, gamma = 1, temperature = 0, } = config;
94
+ assertExists(inputPath, 'Input video');
95
+ ensureDir(outputPath);
96
+ const filters = [];
97
+ // eq filter for brightness, contrast, saturation, gamma
98
+ const eqParts = [];
99
+ if (brightness !== 0)
100
+ eqParts.push(`brightness=${brightness.toFixed(3)}`);
101
+ if (contrast !== 1)
102
+ eqParts.push(`contrast=${contrast.toFixed(3)}`);
103
+ if (saturation !== 1)
104
+ eqParts.push(`saturation=${saturation.toFixed(3)}`);
105
+ if (gamma !== 1)
106
+ eqParts.push(`gamma=${gamma.toFixed(3)}`);
107
+ if (eqParts.length > 0)
108
+ filters.push(`eq=${eqParts.join(':')}`);
109
+ // Temperature via colortemperature filter (ffmpeg 5.1+, fallback to colorchannelmixer)
110
+ if (temperature !== 0) {
111
+ // Warm = boost red/green, reduce blue. Cool = opposite.
112
+ const t = temperature;
113
+ const rr = (1 + t * 0.15).toFixed(3);
114
+ const gg = (1 + t * 0.05).toFixed(3);
115
+ const bb = (1 - t * 0.2).toFixed(3);
116
+ filters.push(`colorchannelmixer=${rr}:0:0:0:0:${gg}:0:0:0:0:${bb}:0`);
117
+ }
118
+ if (filters.length === 0) {
119
+ throw new Error('No color adjustments specified. Set at least one of: brightness, contrast, saturation, gamma, temperature.');
120
+ }
121
+ logger.info(`Applying color grade: ${filters.join(', ')}`);
122
+ const args = [
123
+ '-y', '-i', inputPath,
124
+ '-vf', filters.join(','),
125
+ '-c:a', 'copy',
126
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
127
+ '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
128
+ outputPath,
129
+ ];
130
+ await runFfmpeg(args);
131
+ logger.info(`Color graded: ${outputPath} (${fileInfo(outputPath)})`);
132
+ return outputPath;
133
+ }
134
+ export async function applyVideoEffect(config) {
135
+ const { inputPath, outputPath, effect, intensity = 0.5 } = config;
136
+ assertExists(inputPath, 'Input video');
137
+ ensureDir(outputPath);
138
+ const i = Math.max(0, Math.min(1, intensity));
139
+ let vf;
140
+ switch (effect) {
141
+ case 'blur': {
142
+ const radius = Math.round(2 + i * 18); // 2-20
143
+ vf = `boxblur=${radius}:${radius}`;
144
+ break;
145
+ }
146
+ case 'sharpen': {
147
+ const amount = (i * 2).toFixed(2); // 0-2
148
+ vf = `unsharp=5:5:${amount}:5:5:0`;
149
+ break;
150
+ }
151
+ case 'vignette': {
152
+ const angle = (0.3 + i * 0.5).toFixed(2); // 0.3-0.8 radians
153
+ vf = `vignette=angle=${angle}`;
154
+ break;
155
+ }
156
+ case 'grayscale': {
157
+ // Blend: original*(1-i) + grayscale*i via saturation
158
+ const sat = (1 - i).toFixed(3);
159
+ vf = `eq=saturation=${sat}`;
160
+ break;
161
+ }
162
+ case 'sepia': {
163
+ // Desaturate + warm tone
164
+ const desat = (1 - i * 0.8).toFixed(3);
165
+ const warm = (1 + i * 0.15).toFixed(3);
166
+ const coolB = (1 - i * 0.2).toFixed(3);
167
+ vf = `eq=saturation=${desat},colorchannelmixer=${warm}:0:0:0:0:1:0:0:0:0:${coolB}:0`;
168
+ break;
169
+ }
170
+ case 'noise': {
171
+ const strength = Math.round(5 + i * 40); // 5-45 (higher values explode file size)
172
+ vf = `noise=alls=${strength}:allf=t`;
173
+ break;
174
+ }
175
+ case 'glow': {
176
+ // Duplicate + blur + blend (soft glow)
177
+ const blurR = Math.round(5 + i * 25);
178
+ vf = `split[a][b];[b]boxblur=${blurR}:${blurR}[blurred];[a][blurred]blend=all_mode=screen:all_opacity=${(i * 0.5).toFixed(2)}`;
179
+ break;
180
+ }
181
+ default:
182
+ throw new Error(`Unknown effect: ${effect}`);
183
+ }
184
+ logger.info(`Applying ${effect} (intensity: ${i.toFixed(2)})`);
185
+ // Noise is high-entropy — use higher CRF to keep file size sane
186
+ const crf = effect === 'noise' ? '35' : '18';
187
+ const args = [
188
+ '-y', '-i', inputPath,
189
+ '-vf', vf,
190
+ '-c:a', 'copy',
191
+ '-c:v', 'libx264', '-crf', crf, '-preset', 'medium',
192
+ '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
193
+ outputPath,
194
+ ];
195
+ // glow uses filter_complex (split+blend) — needs different arg structure
196
+ if (effect === 'glow') {
197
+ args.length = 0;
198
+ args.push('-y', '-i', inputPath, '-filter_complex', `[0:v]${vf}[out]`, '-map', '[out]', '-map', '0:a?', '-c:v', 'libx264', '-crf', crf, '-preset', 'medium', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', outputPath);
199
+ }
200
+ await runFfmpeg(args);
201
+ logger.info(`Effect applied: ${outputPath} (${fileInfo(outputPath)})`);
202
+ return outputPath;
203
+ }
204
+ export async function cropVideo(config) {
205
+ const { inputPath, outputPath, width, height, x = 'center', y = 'center' } = config;
206
+ assertExists(inputPath, 'Input video');
207
+ ensureDir(outputPath);
208
+ const xExpr = x === 'center' ? '(iw-ow)/2' : String(x);
209
+ const yExpr = y === 'center' ? '(ih-oh)/2' : String(y);
210
+ logger.info(`Cropping to ${width}x${height} at ${xExpr},${yExpr}`);
211
+ const args = [
212
+ '-y', '-i', inputPath,
213
+ '-vf', `crop=${width}:${height}:${xExpr}:${yExpr}`,
214
+ '-c:a', 'copy',
215
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
216
+ '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
217
+ outputPath,
218
+ ];
219
+ await runFfmpeg(args);
220
+ logger.info(`Cropped: ${outputPath} (${fileInfo(outputPath)})`);
221
+ return outputPath;
222
+ }
223
+ export async function reverseClip(config) {
224
+ const { inputPath, outputPath, reverseAudio = true } = config;
225
+ assertExists(inputPath, 'Input video');
226
+ ensureDir(outputPath);
227
+ const hasAudio = await hasAudioStream(inputPath);
228
+ logger.info(`Reversing video (audio: ${reverseAudio}, hasAudio: ${hasAudio})`);
229
+ const args = ['-y', '-i', inputPath];
230
+ if (!hasAudio) {
231
+ args.push('-vf', 'reverse', '-an');
232
+ }
233
+ else if (reverseAudio) {
234
+ args.push('-vf', 'reverse', '-af', 'areverse');
235
+ }
236
+ else {
237
+ args.push('-vf', 'reverse', '-c:a', 'copy');
238
+ }
239
+ args.push('-c:v', 'libx264', '-crf', '18', '-preset', 'medium', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', outputPath);
240
+ await runFfmpeg(args);
241
+ logger.info(`Reversed: ${outputPath} (${fileInfo(outputPath)})`);
242
+ return outputPath;
243
+ }
244
+ export async function extractAudio(config) {
245
+ const { inputPath, outputPath, format = 'mp3', bitrate = '192k' } = config;
246
+ assertExists(inputPath, 'Input video');
247
+ ensureDir(outputPath);
248
+ const hasAudio = await hasAudioStream(inputPath);
249
+ if (!hasAudio)
250
+ throw new Error('Input video has no audio stream to extract');
251
+ logger.info(`Extracting audio as ${format}`);
252
+ const args = ['-y', '-i', inputPath, '-vn'];
253
+ switch (format) {
254
+ case 'mp3':
255
+ args.push('-c:a', 'libmp3lame', '-b:a', bitrate);
256
+ break;
257
+ case 'aac':
258
+ args.push('-c:a', 'aac', '-b:a', bitrate);
259
+ break;
260
+ case 'wav':
261
+ args.push('-c:a', 'pcm_s16le');
262
+ break;
263
+ case 'flac':
264
+ args.push('-c:a', 'flac');
265
+ break;
266
+ }
267
+ args.push(outputPath);
268
+ await runFfmpeg(args);
269
+ logger.info(`Audio extracted: ${outputPath} (${fileInfo(outputPath)})`);
270
+ return outputPath;
271
+ }
272
+ export async function burnSubtitles(config) {
273
+ const { inputPath, outputPath, subtitlePath, fontSize = 24, fontColor = '&Hffffff', outlineColor = '&H000000', outlineWidth = 2, position = 'bottom', } = config;
274
+ assertExists(inputPath, 'Input video');
275
+ assertExists(subtitlePath, 'Subtitle file');
276
+ ensureDir(outputPath);
277
+ // Determine alignment based on position (ASS alignment values)
278
+ const alignment = position === 'top' ? 8 : position === 'center' ? 5 : 2;
279
+ const marginV = position === 'center' ? 0 : 30;
280
+ // Escape path for ffmpeg (backslashes and colons)
281
+ const escapedSubPath = subtitlePath.replace(/\\/g, '/').replace(/:/g, '\\:');
282
+ const styleOverride = `FontSize=${fontSize},PrimaryColour=${fontColor},OutlineColour=${outlineColor},Outline=${outlineWidth},Alignment=${alignment},MarginV=${marginV}`;
283
+ logger.info(`Burning subtitles (${position}, size: ${fontSize})`);
284
+ const args = [
285
+ '-y', '-i', inputPath,
286
+ '-vf', `subtitles='${escapedSubPath}':force_style='${styleOverride}'`,
287
+ '-c:a', 'copy',
288
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
289
+ '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
290
+ outputPath,
291
+ ];
292
+ await runFfmpeg(args);
293
+ logger.info(`Subtitles burned: ${outputPath} (${fileInfo(outputPath)})`);
294
+ return outputPath;
295
+ }
296
+ export async function autoCaption(config) {
297
+ const { inputPath, outputPath, language, fontSize = 28, position = 'bottom', keepSrt = true, } = config;
298
+ assertExists(inputPath, 'Input video');
299
+ ensureDir(outputPath);
300
+ // Step 1: Extract audio to temp WAV
301
+ const tempDir = path.join('/tmp', `caption-${Date.now()}`);
302
+ fs.mkdirSync(tempDir, { recursive: true });
303
+ const tempAudio = path.join(tempDir, 'audio.wav');
304
+ const srtPath = outputPath.replace(/\.[^.]+$/, '.srt');
305
+ logger.info('Step 1/3: Extracting audio for transcription...');
306
+ await extractAudio({
307
+ inputPath,
308
+ outputPath: tempAudio,
309
+ format: 'wav',
310
+ });
311
+ // Step 2: Transcribe with Whisper API
312
+ logger.info('Step 2/3: Transcribing with Whisper...');
313
+ const srtContent = await transcribeWithWhisper(tempAudio, language);
314
+ fs.writeFileSync(srtPath, srtContent, 'utf-8');
315
+ logger.info(`SRT written: ${srtPath} (${srtContent.split('\n\n').length} segments)`);
316
+ // Step 3: Burn subtitles
317
+ logger.info('Step 3/3: Burning captions into video...');
318
+ await burnSubtitles({
319
+ inputPath,
320
+ outputPath,
321
+ subtitlePath: srtPath,
322
+ fontSize,
323
+ position,
324
+ });
325
+ // Cleanup temp
326
+ try {
327
+ fs.rmSync(tempDir, { recursive: true });
328
+ }
329
+ catch { /* ignore */ }
330
+ if (!keepSrt)
331
+ try {
332
+ fs.unlinkSync(srtPath);
333
+ }
334
+ catch { /* ignore */ }
335
+ return { videoPath: outputPath, srtPath };
336
+ }
337
+ async function transcribeWithWhisper(audioPath, language) {
338
+ const apiKey = process.env.OPENAI_API_KEY;
339
+ if (!apiKey)
340
+ throw new Error('OPENAI_API_KEY required for auto-captioning (Whisper API)');
341
+ const fileBuffer = fs.readFileSync(audioPath);
342
+ const formData = new FormData();
343
+ formData.append('file', new Blob([fileBuffer], { type: 'audio/wav' }), 'audio.wav');
344
+ formData.append('model', 'whisper-1');
345
+ formData.append('response_format', 'srt');
346
+ if (language)
347
+ formData.append('language', language);
348
+ const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
349
+ method: 'POST',
350
+ headers: { Authorization: `Bearer ${apiKey}` },
351
+ body: formData,
352
+ });
353
+ if (!response.ok) {
354
+ const error = await response.text();
355
+ throw new Error(`Whisper API failed (${response.status}): ${error}`);
356
+ }
357
+ return await response.text();
358
+ }
359
+ export async function addKeyframeAnimation(config) {
360
+ const { inputPath, outputPath, keyframes } = config;
361
+ assertExists(inputPath, 'Input video');
362
+ ensureDir(outputPath);
363
+ if (keyframes.length < 2)
364
+ throw new Error('Need at least 2 keyframes for animation');
365
+ // Sort keyframes by time
366
+ const sorted = [...keyframes].sort((a, b) => a.time - b.time);
367
+ // Get video info for calculating crop/zoom
368
+ const duration = await getMediaDuration(inputPath);
369
+ const videoInfo = await getVideoResolution(inputPath);
370
+ const outW = config.outputWidth ?? videoInfo.width;
371
+ const outH = config.outputHeight ?? videoInfo.height;
372
+ logger.info(`Keyframe animation: ${sorted.length} keyframes over ${duration.toFixed(1)}s`);
373
+ // Build zoompan filter expression
374
+ // zoompan requires frame-by-frame zoom/x/y expressions
375
+ // We interpolate between keyframes linearly
376
+ const fps = 60;
377
+ const zoomExpr = buildInterpolationExpr(sorted, 'scale', 1, fps);
378
+ const panXExpr = buildInterpolationExpr(sorted, 'panX', 0, fps);
379
+ const panYExpr = buildInterpolationExpr(sorted, 'panY', 0, fps);
380
+ // zoompan: zoom=expr, x=expr, y=expr, d=1 (per-frame), s=output size, fps=fps
381
+ const filter = `zoompan=z='${zoomExpr}':x='${panXExpr}':y='${panYExpr}':d=1:s=${outW}x${outH}:fps=${fps}`;
382
+ const args = [
383
+ '-y', '-i', inputPath,
384
+ '-vf', filter,
385
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
386
+ '-pix_fmt', 'yuv420p',
387
+ '-t', String(duration),
388
+ '-c:a', 'copy',
389
+ '-movflags', '+faststart',
390
+ outputPath,
391
+ ];
392
+ await runFfmpeg(args, 600_000); // Longer timeout for keyframe processing
393
+ logger.info(`Keyframe animation applied: ${outputPath} (${fileInfo(outputPath)})`);
394
+ return outputPath;
395
+ }
396
+ /** Build ffmpeg expression that interpolates between keyframe values per-frame */
397
+ function buildInterpolationExpr(keyframes, prop, defaultVal, fps) {
398
+ if (keyframes.length === 0)
399
+ return String(defaultVal);
400
+ if (keyframes.length === 1)
401
+ return String(keyframes[0][prop] ?? defaultVal);
402
+ // Build piecewise linear interpolation using if() expressions
403
+ // Frame number = on (current frame index in zoompan)
404
+ // We use 'on' which is the output frame index
405
+ const parts = [];
406
+ for (let i = 0; i < keyframes.length - 1; i++) {
407
+ const kf1 = keyframes[i];
408
+ const kf2 = keyframes[i + 1];
409
+ const v1 = kf1[prop] ?? defaultVal;
410
+ const v2 = kf2[prop] ?? defaultVal;
411
+ const f1 = Math.round(kf1.time * fps);
412
+ const f2 = Math.round(kf2.time * fps);
413
+ const frameRange = f2 - f1;
414
+ if (frameRange <= 0)
415
+ continue;
416
+ // Linear interpolation: v1 + (v2-v1) * (on-f1) / frameRange
417
+ const slope = ((v2 - v1) / frameRange).toFixed(8);
418
+ const interp = `${v1}+${slope}*(on-${f1})`;
419
+ if (i === 0 && i === keyframes.length - 2) {
420
+ // Only segment — use directly
421
+ parts.push(interp);
422
+ }
423
+ else if (i === keyframes.length - 2) {
424
+ // Last segment
425
+ parts.push(interp);
426
+ }
427
+ else {
428
+ parts.push(`if(lt(on,${f2}),${interp},`);
429
+ }
430
+ }
431
+ // Close all if() statements
432
+ const expr = parts.join('') + ')'.repeat(Math.max(0, parts.length - 1));
433
+ return expr;
434
+ }
435
+ async function getVideoResolution(filePath) {
436
+ return new Promise((resolve, reject) => {
437
+ execFile('ffprobe', ['-v', 'quiet', '-select_streams', 'v:0', '-show_entries', 'stream=width,height', '-of', 'json', filePath], (error, stdout) => {
438
+ if (error) {
439
+ reject(new Error(`ffprobe failed: ${error.message}`));
440
+ return;
441
+ }
442
+ try {
443
+ const data = JSON.parse(stdout);
444
+ const stream = data.streams?.[0];
445
+ resolve({ width: stream?.width ?? 1920, height: stream?.height ?? 1080 });
446
+ }
447
+ catch {
448
+ resolve({ width: 1920, height: 1080 });
449
+ }
450
+ });
451
+ });
452
+ }
453
+ export async function composePip(config) {
454
+ const { mainVideo, overlayVideo, outputPath, position = 'bottom-right', scale = 0.3, startTime = 0, endTime, borderWidth = 0, borderColor = 'white', } = config;
455
+ assertExists(mainVideo, 'Main video');
456
+ assertExists(overlayVideo, 'Overlay video');
457
+ ensureDir(outputPath);
458
+ const s = Math.max(0.1, Math.min(0.5, scale));
459
+ logger.info(`PiP: ${position}, scale: ${s}, border: ${borderWidth}px`);
460
+ // Position expressions
461
+ const margin = 20;
462
+ let xExpr, yExpr;
463
+ switch (position) {
464
+ case 'top-left':
465
+ xExpr = String(margin);
466
+ yExpr = String(margin);
467
+ break;
468
+ case 'top-right':
469
+ xExpr = `W-w-${margin}`;
470
+ yExpr = String(margin);
471
+ break;
472
+ case 'bottom-left':
473
+ xExpr = String(margin);
474
+ yExpr = `H-h-${margin}`;
475
+ break;
476
+ case 'center':
477
+ xExpr = '(W-w)/2';
478
+ yExpr = '(H-h)/2';
479
+ break;
480
+ case 'bottom-right':
481
+ default:
482
+ xExpr = `W-w-${margin}`;
483
+ yExpr = `H-h-${margin}`;
484
+ break;
485
+ }
486
+ // Enable expression for time-limited overlay
487
+ const enableExpr = endTime
488
+ ? `:enable='between(t,${startTime},${endTime})'`
489
+ : startTime > 0 ? `:enable='gte(t,${startTime})'` : '';
490
+ let filterComplex;
491
+ if (borderWidth > 0) {
492
+ // Add border pad around overlay
493
+ filterComplex = [
494
+ `[1:v]scale=iw*${s}:ih*${s}[pip_raw]`,
495
+ `[pip_raw]pad=iw+${borderWidth * 2}:ih+${borderWidth * 2}:${borderWidth}:${borderWidth}:color=${borderColor}[pip]`,
496
+ `[0:v][pip]overlay=${xExpr}:${yExpr}${enableExpr}[out]`,
497
+ ].join(';');
498
+ }
499
+ else {
500
+ filterComplex = [
501
+ `[1:v]scale=iw*${s}:ih*${s}[pip]`,
502
+ `[0:v][pip]overlay=${xExpr}:${yExpr}${enableExpr}[out]`,
503
+ ].join(';');
504
+ }
505
+ const args = [
506
+ '-y',
507
+ '-i', mainVideo,
508
+ '-i', overlayVideo,
509
+ '-filter_complex', filterComplex,
510
+ '-map', '[out]', '-map', '0:a?',
511
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
512
+ '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-b:a', '192k',
513
+ '-shortest',
514
+ '-movflags', '+faststart',
515
+ outputPath,
516
+ ];
517
+ await runFfmpeg(args);
518
+ logger.info(`PiP composed: ${outputPath} (${fileInfo(outputPath)})`);
519
+ return outputPath;
520
+ }
521
+ export async function addAudioDucking(config) {
522
+ const { inputPath, outputPath, duckLevel = 0.3, attack = 0.5, release = 1.0, } = config;
523
+ assertExists(inputPath, 'Input video');
524
+ ensureDir(outputPath);
525
+ logger.info(`Audio ducking: level=${duckLevel}, attack=${attack}s, release=${release}s`);
526
+ // Use compand filter to reduce loud parts and normalize quiet
527
+ // This simulates ducking by applying dynamic range compression
528
+ const threshold = -20; // dB threshold
529
+ const ratio = (1 / duckLevel).toFixed(1);
530
+ const args = [
531
+ '-y', '-i', inputPath,
532
+ '-af', `compand=attacks=${attack}:decays=${release}:points=-80/-80|${threshold}/${threshold}|0/-${Math.round((1 - duckLevel) * 20)}:gain=0`,
533
+ '-c:v', 'copy',
534
+ '-movflags', '+faststart',
535
+ outputPath,
536
+ ];
537
+ await runFfmpeg(args);
538
+ logger.info(`Audio ducking applied: ${outputPath} (${fileInfo(outputPath)})`);
539
+ return outputPath;
540
+ }
541
+ //# sourceMappingURL=editing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editing.js","sourceRoot":"","sources":["../../../src/tools/engine/editing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ffmpeg encoding pipeline — stitches PNG frames into cinema-grade video
3
+ */
4
+ import type { EncodingConfig } from './types.js';
5
+ interface EncodeResult {
6
+ outputPath: string;
7
+ format: string;
8
+ codec: string;
9
+ fps: number;
10
+ sizeBytes: number;
11
+ sizeMB: string;
12
+ duration: number;
13
+ totalFrames: number;
14
+ }
15
+ /**
16
+ * Encode a sequence of PNG frames into a video file using ffmpeg
17
+ */
18
+ export declare function encodeFrames(framesDir: string, framePattern: string, outputPath: string, totalFrames: number, config?: EncodingConfig): Promise<EncodeResult>;
19
+ /**
20
+ * Add a fade-in and/or fade-out to an existing video
21
+ */
22
+ export declare function addFade(inputPath: string, outputPath: string, fadeInDuration: number | undefined, fadeOutDuration: number | undefined, totalDuration: number): Promise<void>;
23
+ /**
24
+ * Concatenate multiple video clips with crossfade transitions
25
+ */
26
+ export declare function concatenateWithTransition(clips: string[], outputPath: string, transitionDuration?: number, transitionType?: string): Promise<void>;
27
+ /**
28
+ * Clean up temporary frame files
29
+ */
30
+ export declare function cleanupFrames(framesDir: string): void;
31
+ export {};