@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,154 @@
1
+ /**
2
+ * ffmpeg encoding pipeline — stitches PNG frames into cinema-grade video
3
+ */
4
+ import { execFile } from 'child_process';
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import { logger } from '../../lib/logger.js';
8
+ const CODEC_MAP = {
9
+ h264: {
10
+ codec: 'libx264',
11
+ format: 'mp4',
12
+ extraArgs: ['-pix_fmt', 'yuv420p', '-movflags', '+faststart'],
13
+ },
14
+ h265: {
15
+ codec: 'libx265',
16
+ format: 'mp4',
17
+ extraArgs: ['-pix_fmt', 'yuv420p', '-tag:v', 'hvc1'],
18
+ },
19
+ vp9: {
20
+ codec: 'libvpx-vp9',
21
+ format: 'webm',
22
+ extraArgs: ['-pix_fmt', 'yuv420p', '-row-mt', '1'],
23
+ },
24
+ };
25
+ /**
26
+ * Encode a sequence of PNG frames into a video file using ffmpeg
27
+ */
28
+ export async function encodeFrames(framesDir, framePattern, outputPath, totalFrames, config = {}) {
29
+ const { codec: codecName = 'h264', crf = 18, preset = 'slow', fps = 60, } = config;
30
+ const codecConfig = CODEC_MAP[codecName];
31
+ const format = config.format ?? codecConfig.format;
32
+ const finalOutput = outputPath.endsWith(`.${format}`)
33
+ ? outputPath
34
+ : `${outputPath}.${format}`;
35
+ // Ensure output directory exists
36
+ const outputDir = path.dirname(finalOutput);
37
+ if (!fs.existsSync(outputDir)) {
38
+ fs.mkdirSync(outputDir, { recursive: true });
39
+ }
40
+ const inputPattern = path.join(framesDir, framePattern);
41
+ const args = [
42
+ '-y', // Overwrite output
43
+ '-framerate', String(fps), // Input frame rate
44
+ '-i', inputPattern, // Input pattern
45
+ '-c:v', codecConfig.codec, // Video codec
46
+ '-preset', preset, // Encoding speed/quality
47
+ '-crf', String(crf), // Quality factor
48
+ ...codecConfig.extraArgs, // Codec-specific args
49
+ finalOutput, // Output file
50
+ ];
51
+ logger.info(`Encoding ${totalFrames} frames → ${finalOutput} (${codecName}, CRF ${crf}, ${fps}fps)`);
52
+ await runFfmpeg(args);
53
+ // Get file stats
54
+ const stats = fs.statSync(finalOutput);
55
+ const duration = totalFrames / fps;
56
+ return {
57
+ outputPath: finalOutput,
58
+ format,
59
+ codec: codecName,
60
+ fps,
61
+ sizeBytes: stats.size,
62
+ sizeMB: (stats.size / (1024 * 1024)).toFixed(2),
63
+ duration,
64
+ totalFrames,
65
+ };
66
+ }
67
+ /**
68
+ * Add a fade-in and/or fade-out to an existing video
69
+ */
70
+ export async function addFade(inputPath, outputPath, fadeInDuration = 0.5, fadeOutDuration = 0.5, totalDuration) {
71
+ const fadeOutStart = totalDuration - fadeOutDuration;
72
+ const filter = `fade=t=in:st=0:d=${fadeInDuration},fade=t=out:st=${fadeOutStart}:d=${fadeOutDuration}`;
73
+ const args = [
74
+ '-y',
75
+ '-i', inputPath,
76
+ '-vf', filter,
77
+ '-c:v', 'libx264',
78
+ '-crf', '18',
79
+ '-preset', 'medium',
80
+ '-pix_fmt', 'yuv420p',
81
+ '-movflags', '+faststart',
82
+ outputPath,
83
+ ];
84
+ await runFfmpeg(args);
85
+ }
86
+ /**
87
+ * Concatenate multiple video clips with crossfade transitions
88
+ */
89
+ export async function concatenateWithTransition(clips, outputPath, transitionDuration = 1, transitionType = 'fade') {
90
+ if (clips.length < 2) {
91
+ // Single clip — just copy
92
+ if (clips[0] && clips[0] !== outputPath) {
93
+ fs.copyFileSync(clips[0], outputPath);
94
+ }
95
+ return;
96
+ }
97
+ // Build ffmpeg xfade filter chain for multiple clips
98
+ const inputs = [];
99
+ const filterParts = [];
100
+ for (const clip of clips) {
101
+ inputs.push('-i', clip);
102
+ }
103
+ // Chain xfade filters
104
+ let prevLabel = '0:v';
105
+ for (let i = 1; i < clips.length; i++) {
106
+ const outLabel = i === clips.length - 1 ? '' : `[v${i}]`;
107
+ const offset = i * 5 - transitionDuration; // Approximate offset
108
+ const filter = `[${prevLabel}][${i}:v]xfade=transition=${transitionType}:duration=${transitionDuration}:offset=${offset}`;
109
+ filterParts.push(filter + (outLabel ? outLabel : ''));
110
+ prevLabel = outLabel ? `v${i}` : '';
111
+ }
112
+ const args = [
113
+ '-y',
114
+ ...inputs,
115
+ '-filter_complex', filterParts.join(';'),
116
+ '-c:v', 'libx264',
117
+ '-crf', '18',
118
+ '-pix_fmt', 'yuv420p',
119
+ '-movflags', '+faststart',
120
+ outputPath,
121
+ ];
122
+ await runFfmpeg(args);
123
+ }
124
+ /**
125
+ * Run ffmpeg command and return a promise
126
+ */
127
+ function runFfmpeg(args) {
128
+ return new Promise((resolve, reject) => {
129
+ execFile('ffmpeg', args, { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
130
+ if (error) {
131
+ logger.error(`ffmpeg failed: ${stderr}`);
132
+ reject(new Error(`ffmpeg failed: ${stderr || error.message}`));
133
+ return;
134
+ }
135
+ logger.debug(`ffmpeg output: ${stderr.slice(-200)}`);
136
+ resolve(stdout);
137
+ });
138
+ });
139
+ }
140
+ /**
141
+ * Clean up temporary frame files
142
+ */
143
+ export function cleanupFrames(framesDir) {
144
+ try {
145
+ if (fs.existsSync(framesDir)) {
146
+ fs.rmSync(framesDir, { recursive: true, force: true });
147
+ logger.debug(`Cleaned up frames: ${framesDir}`);
148
+ }
149
+ }
150
+ catch (error) {
151
+ logger.warn(`Failed to cleanup frames: ${framesDir}`);
152
+ }
153
+ }
154
+ //# sourceMappingURL=encoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../../src/tools/engine/encoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export { recordWebsite } from './capture.js';
2
+ export { encodeFrames, addFade, concatenateWithTransition, cleanupFrames } from './encoder.js';
3
+ export { getEasing, applyEasing, EASINGS } from './easing.js';
4
+ export { injectCursor, moveCursor, moveCursorToElement, animateClick, hideCursor } from './cursor.js';
5
+ export { executeScenes, createDefaultScenes } from './scenes.js';
6
+ export { addBackgroundMusic, getMediaDuration } from './audio.js';
7
+ export { concatenateVideos, generateIntro, TRANSITIONS } from './concat.js';
8
+ export { convertToSocialFormat, convertToAllFormats, SOCIAL_FORMATS } from './social-format.js';
9
+ export { addTextOverlays } from './text-overlay.js';
10
+ export { generateSpeech, listElevenLabsVoices } from './tts.js';
11
+ export { createNarratedVideo } from './narrated-video.js';
12
+ export { smartScreenshot } from './smart-screenshot.js';
13
+ export type { SmartScreenshotConfig, SmartScreenshotResult, SmartTarget, DetectedFeature } from './smart-screenshot.js';
14
+ export { applyLutPreset, listLutPresets, ALL_LUT_PRESETS, PRESET_DESCRIPTIONS } from './lut-presets.js';
15
+ export type { LutPreset, LutPresetConfig } from './lut-presets.js';
16
+ export { applyVoiceEffect, ALL_VOICE_EFFECTS, VOICE_EFFECT_DESCRIPTIONS } from './voice-effects.js';
17
+ export type { VoiceEffect, VoiceEffectConfig } from './voice-effects.js';
18
+ export { applyChromaKey } from './chroma-key.js';
19
+ export type { ChromaKeyConfig } from './chroma-key.js';
20
+ export { syncToBeats } from './beat-sync.js';
21
+ export type { BeatSyncConfig, BeatSyncResult } from './beat-sync.js';
22
+ export { animateText, ALL_TEXT_ANIMATIONS, TEXT_ANIMATION_DESCRIPTIONS } from './text-animations.js';
23
+ export type { TextAnimation, TextAnimationConfig, TextPosition as AnimTextPosition } from './text-animations.js';
24
+ export { mixAudioTracks } from './audio-mixer.js';
25
+ export type { AudioTrack, AudioMixConfig, AudioMixResult } from './audio-mixer.js';
26
+ export { listTemplates, getTemplate, getTemplateSummaries, getTemplateCategories } from './templates.js';
27
+ export type { VideoTemplate, TemplateCategory, TemplateSlot } from './templates.js';
28
+ export { renderTemplate } from './template-renderer.js';
29
+ export type { RenderTemplateConfig, RenderResult, TemplateAssets } from './template-renderer.js';
30
+ export * from './types.js';
@@ -0,0 +1,23 @@
1
+ export { recordWebsite } from './capture.js';
2
+ export { encodeFrames, addFade, concatenateWithTransition, cleanupFrames } from './encoder.js';
3
+ export { getEasing, applyEasing, EASINGS } from './easing.js';
4
+ export { injectCursor, moveCursor, moveCursorToElement, animateClick, hideCursor } from './cursor.js';
5
+ export { executeScenes, createDefaultScenes } from './scenes.js';
6
+ export { addBackgroundMusic, getMediaDuration } from './audio.js';
7
+ export { concatenateVideos, generateIntro, TRANSITIONS } from './concat.js';
8
+ export { convertToSocialFormat, convertToAllFormats, SOCIAL_FORMATS } from './social-format.js';
9
+ export { addTextOverlays } from './text-overlay.js';
10
+ export { generateSpeech, listElevenLabsVoices } from './tts.js';
11
+ export { createNarratedVideo } from './narrated-video.js';
12
+ export { smartScreenshot } from './smart-screenshot.js';
13
+ // CapCut-tier engines
14
+ export { applyLutPreset, listLutPresets, ALL_LUT_PRESETS, PRESET_DESCRIPTIONS } from './lut-presets.js';
15
+ export { applyVoiceEffect, ALL_VOICE_EFFECTS, VOICE_EFFECT_DESCRIPTIONS } from './voice-effects.js';
16
+ export { applyChromaKey } from './chroma-key.js';
17
+ export { syncToBeats } from './beat-sync.js';
18
+ export { animateText, ALL_TEXT_ANIMATIONS, TEXT_ANIMATION_DESCRIPTIONS } from './text-animations.js';
19
+ export { mixAudioTracks } from './audio-mixer.js';
20
+ export { listTemplates, getTemplate, getTemplateSummaries, getTemplateCategories } from './templates.js';
21
+ export { renderTemplate } from './template-renderer.js';
22
+ export * from './types.js';
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/engine/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,sBAAsB;AACtB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAExG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAEpG,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAErG,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEzG,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGxD,cAAc,YAAY,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * LUT Preset Engine — 22 cinematic color grade presets via FFmpeg filter chains.
3
+ *
4
+ * No external .cube files needed — each preset is a combination of
5
+ * colorbalance, eq, curves, colorchannelmixer, and hue filters.
6
+ * Intensity parameter (0.0-1.0) blends graded output with the original.
7
+ */
8
+ export type LutPreset = 'cinematic-teal-orange' | 'cinematic-teal-orange-subtle' | 'vintage-film' | 'vintage-kodachrome' | 'cross-process' | 'moody-dark' | 'warm-golden' | 'cold-blue' | 'film-noir' | 'noir-blue-tint' | 'bleach-bypass' | 'cyberpunk-neon' | 'cyberpunk-teal-pink' | 'desaturated-fincher' | 'pastel-dream' | 'matrix-green' | 'sepia' | 'blockbuster-extreme' | 'muted-forest' | 'high-contrast-music' | 'faded-lofi' | 'sunset-magic-hour';
9
+ export interface LutPresetConfig {
10
+ inputPath: string;
11
+ outputPath: string;
12
+ /** The color grade preset to apply */
13
+ preset: LutPreset;
14
+ /** Blend intensity: 0.0 (original) to 1.0 (full effect). Default: 1.0 */
15
+ intensity?: number;
16
+ }
17
+ /** Human-readable descriptions for each preset */
18
+ export declare const PRESET_DESCRIPTIONS: Record<LutPreset, string>;
19
+ export declare const ALL_LUT_PRESETS: LutPreset[];
20
+ export declare function applyLutPreset(config: LutPresetConfig): Promise<string>;
21
+ /** List all available presets with descriptions */
22
+ export declare function listLutPresets(): Array<{
23
+ name: LutPreset;
24
+ description: string;
25
+ }>;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * LUT Preset Engine — 22 cinematic color grade presets via FFmpeg filter chains.
3
+ *
4
+ * No external .cube files needed — each preset is a combination of
5
+ * colorbalance, eq, curves, colorchannelmixer, and hue filters.
6
+ * Intensity parameter (0.0-1.0) blends graded output with the original.
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
+ // ─── Preset Definitions ─────────────────────────────────────────────
13
+ const PRESET_FILTERS = {
14
+ 'cinematic-teal-orange': "colorbalance=rs=-0.15:gs=-0.05:bs=0.25:rm=0.0:gm=-0.02:bm=0.05:rh=0.15:gh=0.02:bh=-0.2,eq=contrast=1.15:saturation=0.9:gamma=0.95,curves=r='0/0 0.25/0.22 0.5/0.55 0.75/0.80 1/1':b='0/0.05 0.25/0.28 0.5/0.45 0.75/0.70 1/0.9'",
15
+ 'cinematic-teal-orange-subtle': "colorbalance=rs=-0.1:gs=-0.03:bs=0.18:rh=0.12:gh=0.02:bh=-0.15,eq=contrast=1.1:saturation=0.75:brightness=-0.02,curves=preset=medium_contrast",
16
+ 'vintage-film': "curves=r='0/0.11 0.42/0.51 1/0.95':g='0/0 0.50/0.48 1/1':b='0/0.22 0.49/0.44 1/0.8',eq=saturation=0.8:contrast=0.9:gamma=1.1,colorbalance=rs=0.05:gs=0.02:bs=-0.05:rh=0.08:gh=0.05:bh=-0.03",
17
+ 'vintage-kodachrome': "curves=r='0/0 0.15/0.18 0.5/0.58 0.85/0.88 1/1':g='0/0 0.5/0.48 1/0.92':b='0/0.06 0.5/0.44 1/0.85',eq=saturation=1.15:contrast=1.1,colorbalance=rs=0.04:gs=-0.02:bs=0.06:rh=0.06:gh=0.03:bh=-0.08",
18
+ 'cross-process': "curves=r='0/0.2 0.5/0.6 1/0.9':g='0/0 0.5/0.55 1/1':b='0/0.3 0.5/0.4 1/0.8',eq=saturation=1.15:contrast=1.1",
19
+ 'moody-dark': "eq=contrast=1.3:brightness=-0.08:saturation=0.65:gamma=0.85,colorbalance=rs=-0.05:gs=-0.02:bs=0.12:rm=-0.03:gm=-0.02:bm=0.05:rh=0.0:gh=-0.02:bh=0.05,curves=master='0/0 0.15/0.05 0.5/0.42 1/0.95'",
20
+ 'warm-golden': "colorbalance=rs=0.12:gs=0.05:bs=-0.12:rm=0.06:gm=0.03:bm=-0.06:rh=0.1:gh=0.06:bh=-0.1,eq=saturation=1.15:contrast=1.05:brightness=0.03,curves=b='0/0 0.5/0.42 1/0.85'",
21
+ 'cold-blue': "colorbalance=rs=-0.12:gs=-0.03:bs=0.2:rm=-0.06:gm=-0.02:bm=0.1:rh=-0.08:gh=0.0:bh=0.12,eq=saturation=0.7:contrast=1.1:brightness=0.05:gamma=1.1",
22
+ 'film-noir': "colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3:0,eq=contrast=1.5:brightness=-0.05:gamma=0.9,curves=preset=strong_contrast",
23
+ 'noir-blue-tint': "colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.35:.45:.35:0,eq=contrast=1.4:brightness=-0.04:gamma=0.9",
24
+ 'bleach-bypass': "eq=contrast=1.4:saturation=0.5:gamma=0.9,curves=preset=strong_contrast,colorbalance=rs=-0.02:gs=-0.02:bs=0.04",
25
+ 'cyberpunk-neon': "eq=contrast=1.25:saturation=1.6:brightness=-0.05:gamma=0.9,colorbalance=rs=-0.1:gs=-0.15:bs=0.2:rm=0.15:gm=-0.1:bm=0.1:rh=0.2:gh=-0.05:bh=0.15,curves=r='0/0 0.3/0.2 0.6/0.7 1/1':b='0/0.05 0.4/0.5 1/1'",
26
+ 'cyberpunk-teal-pink': "colorbalance=rs=-0.15:gs=-0.1:bs=0.25:rm=0.2:gm=-0.12:bm=0.12:rh=0.2:gh=-0.08:bh=0.2,eq=saturation=1.5:contrast=1.3:brightness=-0.06,hue=h=5",
27
+ 'desaturated-fincher': "colorbalance=rs=-0.08:gs=-0.03:bs=0.12:rh=0.08:gh=0.02:bh=-0.1,eq=contrast=1.2:saturation=0.55:brightness=-0.03:gamma=0.92,curves=master='0/0 0.2/0.12 0.5/0.48 0.8/0.82 1/0.95'",
28
+ 'pastel-dream': "eq=contrast=0.8:saturation=0.6:brightness=0.08:gamma=1.2,curves=master='0.0/0.15 0.5/0.55 1/0.9',colorbalance=rs=0.05:gs=0.03:bs=0.06:rh=0.04:gh=0.04:bh=0.02",
29
+ 'matrix-green': "colorchannelmixer=rr=0.3:rg=0.6:rb=0.1:gr=0.1:gg=0.9:gb=0.0:br=0.1:bg=0.4:bb=0.5,eq=contrast=1.2:brightness=-0.03:gamma=0.9,curves=preset=increase_contrast",
30
+ 'sepia': "colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131:0,eq=contrast=1.05:brightness=0.02",
31
+ 'blockbuster-extreme': "colorbalance=rs=-0.2:gs=-0.1:bs=0.35:rm=0.05:gm=-0.03:bm=0.0:rh=0.2:gh=0.05:bh=-0.25,eq=contrast=1.25:saturation=1.1:gamma=0.92,curves=r='0/0 0.25/0.28 0.5/0.58 1/1':b='0/0.08 0.5/0.42 1/0.85'",
32
+ 'muted-forest': "colorbalance=rs=0.03:gs=0.05:bs=-0.05:rm=-0.02:gm=0.04:bm=-0.03:rh=0.02:gh=0.02:bh=-0.04,eq=saturation=0.65:contrast=1.05:gamma=1.05,curves=g='0/0 0.5/0.52 1/0.92':r='0/0 0.5/0.48 1/0.95'",
33
+ 'high-contrast-music': "eq=contrast=1.4:saturation=1.4:brightness=-0.02:gamma=0.85,curves=preset=strong_contrast",
34
+ 'faded-lofi': "curves=master='0/0.08 0.25/0.2 0.75/0.78 1/0.92':r='0/0.05 1/0.95':b='0/0.08 1/0.88',eq=saturation=0.75:contrast=0.95",
35
+ 'sunset-magic-hour': "colorbalance=rs=0.06:gs=-0.02:bs=0.08:rm=0.1:gm=0.04:bm=-0.05:rh=0.15:gh=0.08:bh=-0.12,eq=saturation=1.2:contrast=1.1:brightness=0.02,curves=r='0/0 0.5/0.56 1/1':b='0/0.03 0.5/0.44 1/0.88'",
36
+ };
37
+ /** Human-readable descriptions for each preset */
38
+ export const PRESET_DESCRIPTIONS = {
39
+ 'cinematic-teal-orange': 'Hollywood blockbuster look — teal shadows, orange highlights (Transformers, Mad Max)',
40
+ 'cinematic-teal-orange-subtle': 'Restrained teal-orange for drama/thriller tone',
41
+ 'vintage-film': 'Faded 70s film — lifted blacks, warm cast, slightly desaturated',
42
+ 'vintage-kodachrome': 'Iconic Kodachrome — saturated reds/yellows, slightly cool shadows',
43
+ 'cross-process': 'Vivid, surreal color shifts — the "wrong chemistry" lab look',
44
+ 'moody-dark': 'Crushed blacks, cold undertone — dark drama atmosphere',
45
+ 'warm-golden': 'Sun-kissed warmth — golden hour / magic hour look',
46
+ 'cold-blue': 'Icy blue, desaturated — arctic / winter feel',
47
+ 'film-noir': 'Classic black & white with dramatic contrast and deep blacks',
48
+ 'noir-blue-tint': 'B&W base with subtle cold blue wash',
49
+ 'bleach-bypass': 'High contrast + desaturated + metallic — analog lab technique',
50
+ 'cyberpunk-neon': 'Vivid blues, magentas, teals — oversaturated neon city',
51
+ 'cyberpunk-teal-pink': 'Teal-pink variant cyberpunk — Blade Runner vibes',
52
+ 'desaturated-fincher': 'Muted, controlled palette — David Fincher style (Gone Girl, Seven)',
53
+ 'pastel-dream': 'Soft, lifted, airy — low contrast pastel feel',
54
+ 'matrix-green': 'Green-tinted computer world from The Matrix',
55
+ 'sepia': 'Classic warm sepia tone — antique photograph look',
56
+ 'blockbuster-extreme': 'Aggressive orange & teal for action/superhero films',
57
+ 'muted-forest': 'Desaturated greens and browns — indie film / A24 aesthetic',
58
+ 'high-contrast-music': 'Punchy, vivid, crushed blacks — music video look',
59
+ 'faded-lofi': 'Instagram-style faded shadows with slight color cast',
60
+ 'sunset-magic-hour': 'Deep warm amber highlights, slightly purple shadows',
61
+ };
62
+ export const ALL_LUT_PRESETS = Object.keys(PRESET_FILTERS);
63
+ // ─── Helpers ────────────────────────────────────────────────────────
64
+ function runFfmpeg(args, timeoutMs = 300_000) {
65
+ return new Promise((resolve, reject) => {
66
+ execFile('ffmpeg', args, { maxBuffer: 100 * 1024 * 1024, timeout: timeoutMs }, (error, stdout, stderr) => {
67
+ if (error) {
68
+ logger.error(`ffmpeg failed: ${stderr}`);
69
+ reject(new Error(`ffmpeg failed: ${stderr || error.message}`));
70
+ return;
71
+ }
72
+ resolve(stdout);
73
+ });
74
+ });
75
+ }
76
+ function ensureDir(filePath) {
77
+ const dir = path.dirname(filePath);
78
+ if (!fs.existsSync(dir))
79
+ fs.mkdirSync(dir, { recursive: true });
80
+ }
81
+ function assertExists(filePath, label = 'File') {
82
+ if (!fs.existsSync(filePath))
83
+ throw new Error(`${label} not found: ${filePath}`);
84
+ }
85
+ function fileInfo(filePath) {
86
+ const stats = fs.statSync(filePath);
87
+ return `${(stats.size / 1024 / 1024).toFixed(2)} MB`;
88
+ }
89
+ // ─── Main Function ──────────────────────────────────────────────────
90
+ export async function applyLutPreset(config) {
91
+ const { inputPath, outputPath, preset, intensity = 1.0 } = config;
92
+ assertExists(inputPath, 'Input video');
93
+ ensureDir(outputPath);
94
+ const filterChain = PRESET_FILTERS[preset];
95
+ if (!filterChain) {
96
+ throw new Error(`Unknown LUT preset: ${preset}. Available: ${ALL_LUT_PRESETS.join(', ')}`);
97
+ }
98
+ const clampedIntensity = Math.max(0, Math.min(1, intensity));
99
+ logger.info(`Applying LUT preset: ${preset} (intensity: ${clampedIntensity})`);
100
+ let args;
101
+ if (clampedIntensity >= 0.99) {
102
+ // Full intensity — no blending needed
103
+ args = [
104
+ '-y', '-i', inputPath,
105
+ '-vf', filterChain,
106
+ '-c:a', 'copy',
107
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
108
+ '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
109
+ outputPath,
110
+ ];
111
+ }
112
+ else {
113
+ // Partial intensity — blend with original using split+blend
114
+ const origWeight = (1 - clampedIntensity).toFixed(4);
115
+ const gradedWeight = clampedIntensity.toFixed(4);
116
+ const filterComplex = [
117
+ `[0:v]split[original][tograde]`,
118
+ `[tograde]${filterChain}[graded]`,
119
+ `[original][graded]blend=all_expr='A*${origWeight}+B*${gradedWeight}'[out]`,
120
+ ].join(';');
121
+ args = [
122
+ '-y', '-i', inputPath,
123
+ '-filter_complex', filterComplex,
124
+ '-map', '[out]', '-map', '0:a?',
125
+ '-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
126
+ '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
127
+ outputPath,
128
+ ];
129
+ }
130
+ await runFfmpeg(args);
131
+ logger.info(`LUT preset applied: ${preset} → ${outputPath} (${fileInfo(outputPath)})`);
132
+ return outputPath;
133
+ }
134
+ /** List all available presets with descriptions */
135
+ export function listLutPresets() {
136
+ return ALL_LUT_PRESETS.map(name => ({
137
+ name,
138
+ description: PRESET_DESCRIPTIONS[name],
139
+ }));
140
+ }
141
+ //# sourceMappingURL=lut-presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lut-presets.js","sourceRoot":"","sources":["../../../src/tools/engine/lut-presets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAqC7C,uEAAuE;AAEvE,MAAM,cAAc,GAA8B;IAChD,uBAAuB,EACrB,iOAAiO;IAEnO,8BAA8B,EAC5B,+IAA+I;IAEjJ,cAAc,EACZ,6LAA6L;IAE/L,oBAAoB,EAClB,mMAAmM;IAErM,eAAe,EACb,6GAA6G;IAE/G,YAAY,EACV,oMAAoM;IAEtM,aAAa,EACX,uKAAuK;IAEzK,WAAW,EACT,iJAAiJ;IAEnJ,WAAW,EACT,6HAA6H;IAE/H,gBAAgB,EACd,kGAAkG;IAEpG,eAAe,EACb,+GAA+G;IAEjH,gBAAgB,EACd,0MAA0M;IAE5M,qBAAqB,EACnB,8IAA8I;IAEhJ,qBAAqB,EACnB,kLAAkL;IAEpL,cAAc,EACZ,+JAA+J;IAEjK,cAAc,EACZ,6JAA6J;IAE/J,OAAO,EACL,uGAAuG;IAEzG,qBAAqB,EACnB,kMAAkM;IAEpM,cAAc,EACZ,6LAA6L;IAE/L,qBAAqB,EACnB,0FAA0F;IAE5F,YAAY,EACV,uHAAuH;IAEzH,mBAAmB,EACjB,8LAA8L;CACjM,CAAC;AAEF,kDAAkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAA8B;IAC5D,uBAAuB,EAAE,sFAAsF;IAC/G,8BAA8B,EAAE,gDAAgD;IAChF,cAAc,EAAE,iEAAiE;IACjF,oBAAoB,EAAE,mEAAmE;IACzF,eAAe,EAAE,8DAA8D;IAC/E,YAAY,EAAE,wDAAwD;IACtE,aAAa,EAAE,mDAAmD;IAClE,WAAW,EAAE,8CAA8C;IAC3D,WAAW,EAAE,8DAA8D;IAC3E,gBAAgB,EAAE,qCAAqC;IACvD,eAAe,EAAE,+DAA+D;IAChF,gBAAgB,EAAE,wDAAwD;IAC1E,qBAAqB,EAAE,kDAAkD;IACzE,qBAAqB,EAAE,oEAAoE;IAC3F,cAAc,EAAE,+CAA+C;IAC/D,cAAc,EAAE,6CAA6C;IAC7D,OAAO,EAAE,mDAAmD;IAC5D,qBAAqB,EAAE,qDAAqD;IAC5E,cAAc,EAAE,4DAA4D;IAC5E,qBAAqB,EAAE,kDAAkD;IACzE,YAAY,EAAE,sDAAsD;IACpE,mBAAmB,EAAE,qDAAqD;CAC3E,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAgB,CAAC;AAE1E,uEAAuE;AAEvE,SAAS,SAAS,CAAC,IAAc,EAAE,SAAS,GAAG,OAAO;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACvG,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,KAAK,CAAC,kBAAkB,MAAM,EAAE,CAAC,CAAC;gBACzC,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB,EAAE,KAAK,GAAG,MAAM;IACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,eAAe,QAAQ,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAChC,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACvD,CAAC;AAED,uEAAuE;AAEvE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAuB;IAC1D,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC;IAElE,YAAY,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACvC,SAAS,CAAC,UAAU,CAAC,CAAC;IAEtB,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,gBAAgB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,MAAM,gBAAgB,gBAAgB,GAAG,CAAC,CAAC;IAE/E,IAAI,IAAc,CAAC;IAEnB,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;QAC7B,sCAAsC;QACtC,IAAI,GAAG;YACL,IAAI,EAAE,IAAI,EAAE,SAAS;YACrB,KAAK,EAAE,WAAW;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ;YACpD,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY;YAChD,UAAU;SACX,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,4DAA4D;QAC5D,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,YAAY,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,aAAa,GAAG;YACpB,+BAA+B;YAC/B,YAAY,WAAW,UAAU;YACjC,uCAAuC,UAAU,MAAM,YAAY,QAAQ;SAC5E,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEZ,IAAI,GAAG;YACL,IAAI,EAAE,IAAI,EAAE,SAAS;YACrB,iBAAiB,EAAE,aAAa;YAChC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;YAC/B,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ;YACpD,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY;YAChD,UAAU;SACX,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,CAAC,IAAI,CAAC,uBAAuB,MAAM,MAAM,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACvF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,cAAc;IAC5B,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI;QACJ,WAAW,EAAE,mBAAmB,CAAC,IAAI,CAAC;KACvC,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Narrated Video Engine
3
+ * Combines TTS voice generation with website recording
4
+ * for fully automated explainer videos
5
+ *
6
+ * Flow:
7
+ * 1. Generate speech from script segments
8
+ * 2. Record website scenes synchronized to speech durations
9
+ * 3. Merge video + audio into final output
10
+ */
11
+ import type { TTSProvider, ElevenLabsVoice, ElevenLabsModel, OpenAIVoice, OpenAIModel } from './tts.js';
12
+ import type { Scene, ViewportPreset } from './types.js';
13
+ export interface NarrationSegment {
14
+ /** Text to speak for this segment */
15
+ text: string;
16
+ /** Scene action during this segment */
17
+ scene: Scene;
18
+ /** Extra padding time after speech (seconds, default: 0.5) */
19
+ paddingAfter?: number;
20
+ }
21
+ export interface NarratedVideoConfig {
22
+ /** URL to record */
23
+ url: string;
24
+ /** Narration script segments */
25
+ segments: NarrationSegment[];
26
+ /** Output path (without extension) */
27
+ outputPath: string;
28
+ /** TTS provider (default: elevenlabs) */
29
+ provider?: TTSProvider;
30
+ /** Language (default: en) */
31
+ language?: string;
32
+ /** Viewport (default: desktop) */
33
+ viewport?: ViewportPreset;
34
+ /** ElevenLabs voice (default: adam) */
35
+ elevenLabsVoice?: ElevenLabsVoice | string;
36
+ /** ElevenLabs model */
37
+ elevenLabsModel?: ElevenLabsModel;
38
+ /** OpenAI voice */
39
+ openaiVoice?: OpenAIVoice;
40
+ /** OpenAI model */
41
+ openaiModel?: OpenAIModel;
42
+ /** Speaking speed (default: 1.0) */
43
+ speed?: number;
44
+ /** Music volume if background music provided (default: 0.1) */
45
+ backgroundMusicVolume?: number;
46
+ /** Background music path (optional) */
47
+ backgroundMusicPath?: string;
48
+ }
49
+ export interface NarratedVideoResult {
50
+ success: boolean;
51
+ video: {
52
+ path: string;
53
+ duration: number;
54
+ sizeMB: string;
55
+ };
56
+ audio: {
57
+ totalSegments: number;
58
+ totalDuration: number;
59
+ provider: TTSProvider;
60
+ };
61
+ url: string;
62
+ }
63
+ export declare function createNarratedVideo(config: NarratedVideoConfig): Promise<NarratedVideoResult>;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Narrated Video Engine
3
+ * Combines TTS voice generation with website recording
4
+ * for fully automated explainer videos
5
+ *
6
+ * Flow:
7
+ * 1. Generate speech from script segments
8
+ * 2. Record website scenes synchronized to speech durations
9
+ * 3. Merge video + audio into final output
10
+ */
11
+ import { execFile } from 'child_process';
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { logger } from '../../lib/logger.js';
15
+ import { generateSpeech } from './tts.js';
16
+ import { recordWebsite } from './capture.js';
17
+ import { getMediaDuration } from './audio.js';
18
+ // ─── Main Function ──────────────────────────────────────────────────
19
+ export async function createNarratedVideo(config) {
20
+ const { url, segments, outputPath, provider = 'elevenlabs', language = 'en', viewport = 'desktop', } = config;
21
+ const tempDir = `/tmp/narrated-video-${Date.now()}`;
22
+ fs.mkdirSync(tempDir, { recursive: true });
23
+ try {
24
+ // ─── Step 1: Generate all speech segments ───────────────────
25
+ logger.info(`Generating ${segments.length} speech segment(s)...`);
26
+ const audioPaths = [];
27
+ const audioDurations = [];
28
+ for (let i = 0; i < segments.length; i++) {
29
+ const seg = segments[i];
30
+ const audioPath = path.join(tempDir, `segment-${String(i).padStart(3, '0')}.mp3`);
31
+ const ttsResult = await generateSpeech({
32
+ text: seg.text,
33
+ outputPath: audioPath,
34
+ provider,
35
+ language,
36
+ elevenLabsVoice: config.elevenLabsVoice,
37
+ elevenLabsModel: config.elevenLabsModel,
38
+ openaiVoice: config.openaiVoice,
39
+ openaiModel: config.openaiModel,
40
+ speed: config.speed,
41
+ });
42
+ audioPaths.push(ttsResult.audioPath);
43
+ audioDurations.push(ttsResult.duration);
44
+ logger.info(`Segment ${i + 1}: "${seg.text.slice(0, 50)}..." → ${ttsResult.duration.toFixed(1)}s`);
45
+ }
46
+ // ─── Step 2: Concatenate audio segments ─────────────────────
47
+ logger.info('Concatenating audio segments...');
48
+ const fullAudioPath = path.join(tempDir, 'full-narration.mp3');
49
+ await concatenateAudio(audioPaths, fullAudioPath);
50
+ const totalAudioDuration = await getMediaDuration(fullAudioPath);
51
+ logger.info(`Total narration: ${totalAudioDuration.toFixed(1)}s`);
52
+ // ─── Step 3: Build scenes with matched durations ────────────
53
+ logger.info('Building synchronized scenes...');
54
+ const scenes = [];
55
+ for (let i = 0; i < segments.length; i++) {
56
+ const seg = segments[i];
57
+ const padding = seg.paddingAfter ?? 0.5;
58
+ const sceneDuration = audioDurations[i] + padding;
59
+ // Override scene duration to match audio
60
+ const scene = { ...seg.scene };
61
+ if ('duration' in scene) {
62
+ scene.duration = sceneDuration;
63
+ }
64
+ scenes.push(scene);
65
+ }
66
+ // ─── Step 4: Record website with synced scenes ──────────────
67
+ logger.info('Recording website with synchronized scenes...');
68
+ const videoOnlyPath = path.join(tempDir, 'video-only');
69
+ const recordResult = await recordWebsite({
70
+ url,
71
+ outputPath: videoOnlyPath,
72
+ viewport,
73
+ fps: 60,
74
+ scenes,
75
+ cursor: { enabled: false },
76
+ encoding: { codec: 'h264', crf: 18 },
77
+ });
78
+ // ─── Step 5: Merge video + audio ────────────────────────────
79
+ logger.info('Merging video + narration...');
80
+ const finalPath = outputPath.endsWith('.mp4') ? outputPath : `${outputPath}.mp4`;
81
+ const outDir = path.dirname(finalPath);
82
+ if (!fs.existsSync(outDir))
83
+ fs.mkdirSync(outDir, { recursive: true });
84
+ const mergeArgs = [
85
+ '-y',
86
+ '-i', recordResult.video.path,
87
+ '-i', fullAudioPath,
88
+ ];
89
+ // Optional background music
90
+ if (config.backgroundMusicPath && fs.existsSync(config.backgroundMusicPath)) {
91
+ const musicVol = config.backgroundMusicVolume ?? 0.1;
92
+ mergeArgs.push('-stream_loop', '-1', '-i', config.backgroundMusicPath);
93
+ // Mix narration + background music
94
+ mergeArgs.push('-filter_complex', `[1:a]volume=1.0[narration];[2:a]volume=${musicVol},afade=t=in:st=0:d=2[music];[narration][music]amix=inputs=2:duration=first[aout]`, '-map', '0:v', '-map', '[aout]');
95
+ }
96
+ else {
97
+ mergeArgs.push('-map', '0:v', '-map', '1:a');
98
+ }
99
+ mergeArgs.push('-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k', '-shortest', '-movflags', '+faststart', finalPath);
100
+ await runFfmpeg(mergeArgs);
101
+ const finalStats = fs.statSync(finalPath);
102
+ const finalDuration = await getMediaDuration(finalPath);
103
+ // ─── Cleanup ────────────────────────────────────────────────
104
+ fs.rmSync(tempDir, { recursive: true, force: true });
105
+ logger.info(`Narrated video ready: ${finalPath} (${finalDuration.toFixed(1)}s, ${(finalStats.size / 1024 / 1024).toFixed(2)} MB)`);
106
+ return {
107
+ success: true,
108
+ video: {
109
+ path: finalPath,
110
+ duration: finalDuration,
111
+ sizeMB: (finalStats.size / 1024 / 1024).toFixed(2),
112
+ },
113
+ audio: {
114
+ totalSegments: segments.length,
115
+ totalDuration: totalAudioDuration,
116
+ provider,
117
+ },
118
+ url,
119
+ };
120
+ }
121
+ catch (error) {
122
+ // Cleanup on error
123
+ try {
124
+ fs.rmSync(tempDir, { recursive: true, force: true });
125
+ }
126
+ catch { /* ignore */ }
127
+ throw error;
128
+ }
129
+ }
130
+ // ─── Helpers ────────────────────────────────────────────────────────
131
+ /**
132
+ * Concatenate multiple audio files using ffmpeg concat demuxer
133
+ */
134
+ async function concatenateAudio(files, outputPath) {
135
+ // Create concat list file
136
+ const listPath = outputPath + '.txt';
137
+ const listContent = files.map((f) => `file '${f}'`).join('\n');
138
+ fs.writeFileSync(listPath, listContent);
139
+ await runFfmpeg([
140
+ '-y',
141
+ '-f', 'concat',
142
+ '-safe', '0',
143
+ '-i', listPath,
144
+ '-c:a', 'libmp3lame',
145
+ '-b:a', '192k',
146
+ outputPath,
147
+ ]);
148
+ // Cleanup list file
149
+ fs.unlinkSync(listPath);
150
+ }
151
+ function runFfmpeg(args) {
152
+ return new Promise((resolve, reject) => {
153
+ execFile('ffmpeg', args, { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
154
+ if (error) {
155
+ logger.error(`ffmpeg failed: ${stderr}`);
156
+ reject(new Error(`ffmpeg failed: ${stderr || error.message}`));
157
+ return;
158
+ }
159
+ resolve(stdout);
160
+ });
161
+ });
162
+ }
163
+ //# sourceMappingURL=narrated-video.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narrated-video.js","sourceRoot":"","sources":["../../../src/tools/engine/narrated-video.ts"],"names":[],"mappings":"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"}