vidpipe 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 (213) hide show
  1. package/README.md +243 -0
  2. package/assets/fonts/Montserrat-Bold.ttf +0 -0
  3. package/assets/fonts/Montserrat-Regular.ttf +0 -0
  4. package/assets/fonts/OFL.txt +93 -0
  5. package/dist/__tests__/agents.test.d.ts +2 -0
  6. package/dist/__tests__/agents.test.d.ts.map +1 -0
  7. package/dist/__tests__/agents.test.js +434 -0
  8. package/dist/__tests__/agents.test.js.map +1 -0
  9. package/dist/__tests__/aspectRatio.test.d.ts +2 -0
  10. package/dist/__tests__/aspectRatio.test.d.ts.map +1 -0
  11. package/dist/__tests__/aspectRatio.test.js +406 -0
  12. package/dist/__tests__/aspectRatio.test.js.map +1 -0
  13. package/dist/__tests__/captionGenerator.test.d.ts +2 -0
  14. package/dist/__tests__/captionGenerator.test.d.ts.map +1 -0
  15. package/dist/__tests__/captionGenerator.test.js +435 -0
  16. package/dist/__tests__/captionGenerator.test.js.map +1 -0
  17. package/dist/__tests__/config.test.d.ts +2 -0
  18. package/dist/__tests__/config.test.d.ts.map +1 -0
  19. package/dist/__tests__/config.test.js +81 -0
  20. package/dist/__tests__/config.test.js.map +1 -0
  21. package/dist/__tests__/faceDetection.test.d.ts +2 -0
  22. package/dist/__tests__/faceDetection.test.d.ts.map +1 -0
  23. package/dist/__tests__/faceDetection.test.js +372 -0
  24. package/dist/__tests__/faceDetection.test.js.map +1 -0
  25. package/dist/__tests__/ffmpegTools.test.d.ts +2 -0
  26. package/dist/__tests__/ffmpegTools.test.d.ts.map +1 -0
  27. package/dist/__tests__/ffmpegTools.test.js +464 -0
  28. package/dist/__tests__/ffmpegTools.test.js.map +1 -0
  29. package/dist/__tests__/integration/captionBurn.test.d.ts +2 -0
  30. package/dist/__tests__/integration/captionBurn.test.d.ts.map +1 -0
  31. package/dist/__tests__/integration/captionBurn.test.js +103 -0
  32. package/dist/__tests__/integration/captionBurn.test.js.map +1 -0
  33. package/dist/__tests__/integration/clipComposite.test.d.ts +2 -0
  34. package/dist/__tests__/integration/clipComposite.test.d.ts.map +1 -0
  35. package/dist/__tests__/integration/clipComposite.test.js +56 -0
  36. package/dist/__tests__/integration/clipComposite.test.js.map +1 -0
  37. package/dist/__tests__/integration/faceDetection.test.d.ts +2 -0
  38. package/dist/__tests__/integration/faceDetection.test.d.ts.map +1 -0
  39. package/dist/__tests__/integration/faceDetection.test.js +85 -0
  40. package/dist/__tests__/integration/faceDetection.test.js.map +1 -0
  41. package/dist/__tests__/integration/ffmpegPipeline.test.d.ts +2 -0
  42. package/dist/__tests__/integration/ffmpegPipeline.test.d.ts.map +1 -0
  43. package/dist/__tests__/integration/ffmpegPipeline.test.js +88 -0
  44. package/dist/__tests__/integration/ffmpegPipeline.test.js.map +1 -0
  45. package/dist/__tests__/integration/fixture.d.ts +19 -0
  46. package/dist/__tests__/integration/fixture.d.ts.map +1 -0
  47. package/dist/__tests__/integration/fixture.js +112 -0
  48. package/dist/__tests__/integration/fixture.js.map +1 -0
  49. package/dist/__tests__/integration/fixture.test.d.ts +2 -0
  50. package/dist/__tests__/integration/fixture.test.d.ts.map +1 -0
  51. package/dist/__tests__/integration/fixture.test.js +27 -0
  52. package/dist/__tests__/integration/fixture.test.js.map +1 -0
  53. package/dist/__tests__/integration/realCaptions.test.d.ts +2 -0
  54. package/dist/__tests__/integration/realCaptions.test.d.ts.map +1 -0
  55. package/dist/__tests__/integration/realCaptions.test.js +226 -0
  56. package/dist/__tests__/integration/realCaptions.test.js.map +1 -0
  57. package/dist/__tests__/integration/realPipeline.test.d.ts +2 -0
  58. package/dist/__tests__/integration/realPipeline.test.d.ts.map +1 -0
  59. package/dist/__tests__/integration/realPipeline.test.js +210 -0
  60. package/dist/__tests__/integration/realPipeline.test.js.map +1 -0
  61. package/dist/__tests__/integration/silenceRemoval.test.d.ts +2 -0
  62. package/dist/__tests__/integration/silenceRemoval.test.d.ts.map +1 -0
  63. package/dist/__tests__/integration/silenceRemoval.test.js +93 -0
  64. package/dist/__tests__/integration/silenceRemoval.test.js.map +1 -0
  65. package/dist/__tests__/pipeline.test.d.ts +2 -0
  66. package/dist/__tests__/pipeline.test.d.ts.map +1 -0
  67. package/dist/__tests__/pipeline.test.js +434 -0
  68. package/dist/__tests__/pipeline.test.js.map +1 -0
  69. package/dist/__tests__/services.test.d.ts +2 -0
  70. package/dist/__tests__/services.test.d.ts.map +1 -0
  71. package/dist/__tests__/services.test.js +655 -0
  72. package/dist/__tests__/services.test.js.map +1 -0
  73. package/dist/__tests__/silenceRemoval.test.d.ts +2 -0
  74. package/dist/__tests__/silenceRemoval.test.d.ts.map +1 -0
  75. package/dist/__tests__/silenceRemoval.test.js +266 -0
  76. package/dist/__tests__/silenceRemoval.test.js.map +1 -0
  77. package/dist/__tests__/singlePassEdit.test.d.ts +2 -0
  78. package/dist/__tests__/singlePassEdit.test.d.ts.map +1 -0
  79. package/dist/__tests__/singlePassEdit.test.js +321 -0
  80. package/dist/__tests__/singlePassEdit.test.js.map +1 -0
  81. package/dist/__tests__/smoke.test.d.ts +2 -0
  82. package/dist/__tests__/smoke.test.d.ts.map +1 -0
  83. package/dist/__tests__/smoke.test.js +8 -0
  84. package/dist/__tests__/smoke.test.js.map +1 -0
  85. package/dist/__tests__/utilities.test.d.ts +2 -0
  86. package/dist/__tests__/utilities.test.d.ts.map +1 -0
  87. package/dist/__tests__/utilities.test.js +268 -0
  88. package/dist/__tests__/utilities.test.js.map +1 -0
  89. package/dist/agents/BaseAgent.d.ts +52 -0
  90. package/dist/agents/BaseAgent.d.ts.map +1 -0
  91. package/dist/agents/BaseAgent.js +108 -0
  92. package/dist/agents/BaseAgent.js.map +1 -0
  93. package/dist/agents/BlogAgent.d.ts +3 -0
  94. package/dist/agents/BlogAgent.d.ts.map +1 -0
  95. package/dist/agents/BlogAgent.js +163 -0
  96. package/dist/agents/BlogAgent.js.map +1 -0
  97. package/dist/agents/ChapterAgent.d.ts +11 -0
  98. package/dist/agents/ChapterAgent.d.ts.map +1 -0
  99. package/dist/agents/ChapterAgent.js +191 -0
  100. package/dist/agents/ChapterAgent.js.map +1 -0
  101. package/dist/agents/MediumVideoAgent.d.ts +3 -0
  102. package/dist/agents/MediumVideoAgent.d.ts.map +1 -0
  103. package/dist/agents/MediumVideoAgent.js +219 -0
  104. package/dist/agents/MediumVideoAgent.js.map +1 -0
  105. package/dist/agents/ShortsAgent.d.ts +3 -0
  106. package/dist/agents/ShortsAgent.d.ts.map +1 -0
  107. package/dist/agents/ShortsAgent.js +243 -0
  108. package/dist/agents/ShortsAgent.js.map +1 -0
  109. package/dist/agents/SilenceRemovalAgent.d.ts +9 -0
  110. package/dist/agents/SilenceRemovalAgent.d.ts.map +1 -0
  111. package/dist/agents/SilenceRemovalAgent.js +208 -0
  112. package/dist/agents/SilenceRemovalAgent.js.map +1 -0
  113. package/dist/agents/SocialMediaAgent.d.ts +4 -0
  114. package/dist/agents/SocialMediaAgent.d.ts.map +1 -0
  115. package/dist/agents/SocialMediaAgent.js +248 -0
  116. package/dist/agents/SocialMediaAgent.js.map +1 -0
  117. package/dist/agents/SummaryAgent.d.ts +11 -0
  118. package/dist/agents/SummaryAgent.d.ts.map +1 -0
  119. package/dist/agents/SummaryAgent.js +333 -0
  120. package/dist/agents/SummaryAgent.js.map +1 -0
  121. package/dist/config/brand.d.ts +29 -0
  122. package/dist/config/brand.d.ts.map +1 -0
  123. package/dist/config/brand.js +83 -0
  124. package/dist/config/brand.js.map +1 -0
  125. package/dist/config/environment.d.ts +36 -0
  126. package/dist/config/environment.d.ts.map +1 -0
  127. package/dist/config/environment.js +44 -0
  128. package/dist/config/environment.js.map +1 -0
  129. package/dist/config/logger.d.ts +5 -0
  130. package/dist/config/logger.d.ts.map +1 -0
  131. package/dist/config/logger.js +13 -0
  132. package/dist/config/logger.js.map +1 -0
  133. package/dist/index.d.ts +2 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +135 -0
  136. package/dist/index.js.map +1 -0
  137. package/dist/pipeline.d.ts +57 -0
  138. package/dist/pipeline.d.ts.map +1 -0
  139. package/dist/pipeline.js +287 -0
  140. package/dist/pipeline.js.map +1 -0
  141. package/dist/services/captionGeneration.d.ts +7 -0
  142. package/dist/services/captionGeneration.d.ts.map +1 -0
  143. package/dist/services/captionGeneration.js +29 -0
  144. package/dist/services/captionGeneration.js.map +1 -0
  145. package/dist/services/fileWatcher.d.ts +19 -0
  146. package/dist/services/fileWatcher.d.ts.map +1 -0
  147. package/dist/services/fileWatcher.js +120 -0
  148. package/dist/services/fileWatcher.js.map +1 -0
  149. package/dist/services/gitOperations.d.ts +3 -0
  150. package/dist/services/gitOperations.d.ts.map +1 -0
  151. package/dist/services/gitOperations.js +43 -0
  152. package/dist/services/gitOperations.js.map +1 -0
  153. package/dist/services/socialPosting.d.ts +38 -0
  154. package/dist/services/socialPosting.d.ts.map +1 -0
  155. package/dist/services/socialPosting.js +102 -0
  156. package/dist/services/socialPosting.js.map +1 -0
  157. package/dist/services/transcription.d.ts +3 -0
  158. package/dist/services/transcription.d.ts.map +1 -0
  159. package/dist/services/transcription.js +100 -0
  160. package/dist/services/transcription.js.map +1 -0
  161. package/dist/services/videoIngestion.d.ts +3 -0
  162. package/dist/services/videoIngestion.d.ts.map +1 -0
  163. package/dist/services/videoIngestion.js +103 -0
  164. package/dist/services/videoIngestion.js.map +1 -0
  165. package/dist/tools/captions/captionGenerator.d.ts +84 -0
  166. package/dist/tools/captions/captionGenerator.d.ts.map +1 -0
  167. package/dist/tools/captions/captionGenerator.js +390 -0
  168. package/dist/tools/captions/captionGenerator.js.map +1 -0
  169. package/dist/tools/ffmpeg/aspectRatio.d.ts +101 -0
  170. package/dist/tools/ffmpeg/aspectRatio.d.ts.map +1 -0
  171. package/dist/tools/ffmpeg/aspectRatio.js +338 -0
  172. package/dist/tools/ffmpeg/aspectRatio.js.map +1 -0
  173. package/dist/tools/ffmpeg/audioExtraction.d.ts +16 -0
  174. package/dist/tools/ffmpeg/audioExtraction.d.ts.map +1 -0
  175. package/dist/tools/ffmpeg/audioExtraction.js +86 -0
  176. package/dist/tools/ffmpeg/audioExtraction.js.map +1 -0
  177. package/dist/tools/ffmpeg/captionBurning.d.ts +8 -0
  178. package/dist/tools/ffmpeg/captionBurning.d.ts.map +1 -0
  179. package/dist/tools/ffmpeg/captionBurning.js +71 -0
  180. package/dist/tools/ffmpeg/captionBurning.js.map +1 -0
  181. package/dist/tools/ffmpeg/clipExtraction.d.ts +23 -0
  182. package/dist/tools/ffmpeg/clipExtraction.d.ts.map +1 -0
  183. package/dist/tools/ffmpeg/clipExtraction.js +178 -0
  184. package/dist/tools/ffmpeg/clipExtraction.js.map +1 -0
  185. package/dist/tools/ffmpeg/faceDetection.d.ts +127 -0
  186. package/dist/tools/ffmpeg/faceDetection.d.ts.map +1 -0
  187. package/dist/tools/ffmpeg/faceDetection.js +500 -0
  188. package/dist/tools/ffmpeg/faceDetection.js.map +1 -0
  189. package/dist/tools/ffmpeg/frameCapture.d.ts +10 -0
  190. package/dist/tools/ffmpeg/frameCapture.d.ts.map +1 -0
  191. package/dist/tools/ffmpeg/frameCapture.js +48 -0
  192. package/dist/tools/ffmpeg/frameCapture.js.map +1 -0
  193. package/dist/tools/ffmpeg/silenceDetection.d.ts +10 -0
  194. package/dist/tools/ffmpeg/silenceDetection.d.ts.map +1 -0
  195. package/dist/tools/ffmpeg/silenceDetection.js +55 -0
  196. package/dist/tools/ffmpeg/silenceDetection.js.map +1 -0
  197. package/dist/tools/ffmpeg/singlePassEdit.d.ts +25 -0
  198. package/dist/tools/ffmpeg/singlePassEdit.d.ts.map +1 -0
  199. package/dist/tools/ffmpeg/singlePassEdit.js +123 -0
  200. package/dist/tools/ffmpeg/singlePassEdit.js.map +1 -0
  201. package/dist/tools/search/exaClient.d.ts +8 -0
  202. package/dist/tools/search/exaClient.d.ts.map +1 -0
  203. package/dist/tools/search/exaClient.js +38 -0
  204. package/dist/tools/search/exaClient.js.map +1 -0
  205. package/dist/tools/whisper/whisperClient.d.ts +3 -0
  206. package/dist/tools/whisper/whisperClient.d.ts.map +1 -0
  207. package/dist/tools/whisper/whisperClient.js +77 -0
  208. package/dist/tools/whisper/whisperClient.js.map +1 -0
  209. package/dist/types/index.d.ts +305 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +44 -0
  212. package/dist/types/index.js.map +1 -0
  213. package/package.json +63 -0
@@ -0,0 +1,178 @@
1
+ import ffmpeg from 'fluent-ffmpeg';
2
+ import { execFile } from 'child_process';
3
+ import { promises as fs } from 'fs';
4
+ import pathMod from 'path';
5
+ import logger from '../../config/logger';
6
+ const ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg';
7
+ const ffprobePath = process.env.FFPROBE_PATH || 'ffprobe';
8
+ ffmpeg.setFfmpegPath(ffmpegPath);
9
+ ffmpeg.setFfprobePath(ffprobePath);
10
+ /**
11
+ * Extract a single clip segment using stream copy (-c copy) for speed.
12
+ * @param buffer Seconds of padding added before start and after end (default 1.0)
13
+ */
14
+ export async function extractClip(videoPath, start, end, outputPath, buffer = 1.0) {
15
+ const outputDir = pathMod.dirname(outputPath);
16
+ await fs.mkdir(outputDir, { recursive: true });
17
+ const bufferedStart = Math.max(0, start - buffer);
18
+ const bufferedEnd = end + buffer;
19
+ const duration = bufferedEnd - bufferedStart;
20
+ logger.info(`Extracting clip [${start}s–${end}s] (buffered: ${bufferedStart.toFixed(2)}s–${bufferedEnd.toFixed(2)}s) → ${outputPath}`);
21
+ return new Promise((resolve, reject) => {
22
+ ffmpeg(videoPath)
23
+ .setStartTime(bufferedStart)
24
+ .setDuration(duration)
25
+ .outputOptions('-c copy')
26
+ .outputOptions('-avoid_negative_ts make_zero')
27
+ .output(outputPath)
28
+ .on('end', () => {
29
+ logger.info(`Clip extraction complete: ${outputPath}`);
30
+ resolve(outputPath);
31
+ })
32
+ .on('error', (err) => {
33
+ logger.error(`Clip extraction failed: ${err.message}`);
34
+ reject(new Error(`Clip extraction failed: ${err.message}`));
35
+ })
36
+ .run();
37
+ });
38
+ }
39
+ /**
40
+ * Extract multiple non-contiguous segments and concatenate them into one clip.
41
+ * Each segment is padded by `buffer` seconds on both sides for smoother cuts.
42
+ * Re-encodes and uses concat demuxer for clean joins.
43
+ * @param buffer Seconds of padding added before start and after end of each segment (default 1.0)
44
+ */
45
+ export async function extractCompositeClip(videoPath, segments, outputPath, buffer = 1.0) {
46
+ if (!segments || segments.length === 0) {
47
+ throw new Error('At least one segment is required');
48
+ }
49
+ if (segments.length === 1) {
50
+ return extractClip(videoPath, segments[0].start, segments[0].end, outputPath, buffer);
51
+ }
52
+ const outputDir = pathMod.dirname(outputPath);
53
+ await fs.mkdir(outputDir, { recursive: true });
54
+ const tempDir = pathMod.join(outputDir, `.temp-${Date.now()}`);
55
+ await fs.mkdir(tempDir, { recursive: true });
56
+ const tempFiles = [];
57
+ try {
58
+ // Extract each segment to a temp file (re-encode for reliable concat)
59
+ for (let i = 0; i < segments.length; i++) {
60
+ const seg = segments[i];
61
+ const tempPath = pathMod.join(tempDir, `segment-${i}.mp4`);
62
+ tempFiles.push(tempPath);
63
+ const bufferedStart = Math.max(0, seg.start - buffer);
64
+ const bufferedEnd = seg.end + buffer;
65
+ logger.info(`Extracting segment ${i + 1}/${segments.length} [${seg.start}s–${seg.end}s] (buffered: ${bufferedStart.toFixed(2)}s–${bufferedEnd.toFixed(2)}s)`);
66
+ await new Promise((resolve, reject) => {
67
+ ffmpeg(videoPath)
68
+ .setStartTime(bufferedStart)
69
+ .setDuration(bufferedEnd - bufferedStart)
70
+ .outputOptions(['-threads', '4', '-preset', 'ultrafast'])
71
+ .output(tempPath)
72
+ .on('end', () => resolve())
73
+ .on('error', (err) => reject(new Error(`Segment ${i} extraction failed: ${err.message}`)))
74
+ .run();
75
+ });
76
+ }
77
+ // Build concat list file
78
+ const concatListPath = pathMod.join(tempDir, 'concat-list.txt');
79
+ const listContent = tempFiles.map((f) => `file '${f.replace(/'/g, "'\\''")}'`).join('\n');
80
+ await fs.writeFile(concatListPath, listContent);
81
+ // Concatenate segments (re-encode for clean joins across buffered segments)
82
+ logger.info(`Concatenating ${segments.length} segments → ${outputPath}`);
83
+ await new Promise((resolve, reject) => {
84
+ ffmpeg()
85
+ .input(concatListPath)
86
+ .inputOptions(['-f', 'concat', '-safe', '0'])
87
+ .outputOptions(['-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '23', '-threads', '4', '-c:a', 'aac'])
88
+ .output(outputPath)
89
+ .on('end', () => resolve())
90
+ .on('error', (err) => reject(new Error(`Concat failed: ${err.message}`)))
91
+ .run();
92
+ });
93
+ logger.info(`Composite clip complete: ${outputPath}`);
94
+ return outputPath;
95
+ }
96
+ finally {
97
+ // Clean up temp files
98
+ await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
99
+ }
100
+ }
101
+ /**
102
+ * Extract multiple non-contiguous segments and concatenate them with crossfade
103
+ * transitions using FFmpeg xfade/acrossfade filters.
104
+ * Falls back to extractCompositeClip if only one segment is provided.
105
+ *
106
+ * @param transitionDuration Crossfade duration in seconds (default 0.5)
107
+ * @param buffer Seconds of padding added before/after each segment (default 1.0)
108
+ */
109
+ export async function extractCompositeClipWithTransitions(videoPath, segments, outputPath, transitionDuration = 0.5, buffer = 1.0) {
110
+ if (!segments || segments.length === 0) {
111
+ throw new Error('At least one segment is required');
112
+ }
113
+ // Single segment — no transitions needed
114
+ if (segments.length === 1) {
115
+ return extractClip(videoPath, segments[0].start, segments[0].end, outputPath, buffer);
116
+ }
117
+ // Two segments — no transitions needed, use regular composite
118
+ if (segments.length === 2 && transitionDuration <= 0) {
119
+ return extractCompositeClip(videoPath, segments, outputPath, buffer);
120
+ }
121
+ const outputDir = pathMod.dirname(outputPath);
122
+ await fs.mkdir(outputDir, { recursive: true });
123
+ // Build filter_complex for xfade transitions between segments
124
+ const filterParts = [];
125
+ const segDurations = [];
126
+ for (let i = 0; i < segments.length; i++) {
127
+ const seg = segments[i];
128
+ const bufferedStart = Math.max(0, seg.start - buffer);
129
+ const bufferedEnd = seg.end + buffer;
130
+ const duration = bufferedEnd - bufferedStart;
131
+ segDurations.push(duration);
132
+ filterParts.push(`[0:v]trim=start=${bufferedStart.toFixed(3)}:end=${bufferedEnd.toFixed(3)},setpts=PTS-STARTPTS[v${i}]`);
133
+ filterParts.push(`[0:a]atrim=start=${bufferedStart.toFixed(3)}:end=${bufferedEnd.toFixed(3)},asetpts=PTS-STARTPTS[a${i}]`);
134
+ }
135
+ // Chain xfade transitions: [v0][v1]xfade → [xv0]; [xv0][v2]xfade → [xv1]; ...
136
+ let prevVideo = 'v0';
137
+ let prevAudio = 'a0';
138
+ let cumulativeDuration = segDurations[0];
139
+ for (let i = 1; i < segments.length; i++) {
140
+ const offset = Math.max(0, cumulativeDuration - transitionDuration);
141
+ const outVideo = i === segments.length - 1 ? 'vout' : `xv${i - 1}`;
142
+ const outAudio = i === segments.length - 1 ? 'aout' : `xa${i - 1}`;
143
+ filterParts.push(`[${prevVideo}][v${i}]xfade=transition=fade:duration=${transitionDuration.toFixed(3)}:offset=${offset.toFixed(3)}[${outVideo}]`);
144
+ filterParts.push(`[${prevAudio}][a${i}]acrossfade=d=${transitionDuration.toFixed(3)}[${outAudio}]`);
145
+ prevVideo = outVideo;
146
+ prevAudio = outAudio;
147
+ // After xfade, the combined duration shrinks by transitionDuration
148
+ cumulativeDuration = cumulativeDuration - transitionDuration + segDurations[i];
149
+ }
150
+ const filterComplex = filterParts.join(';\n');
151
+ const args = [
152
+ '-y',
153
+ '-i', videoPath,
154
+ '-filter_complex', filterComplex,
155
+ '-map', '[vout]',
156
+ '-map', '[aout]',
157
+ '-c:v', 'libx264',
158
+ '-preset', 'ultrafast',
159
+ '-crf', '23',
160
+ '-threads', '4',
161
+ '-c:a', 'aac',
162
+ '-b:a', '128k',
163
+ outputPath,
164
+ ];
165
+ logger.info(`[ClipExtraction] Compositing ${segments.length} segments with xfade transitions → ${outputPath}`);
166
+ return new Promise((resolve, reject) => {
167
+ execFile(ffmpegPath, args, { maxBuffer: 50 * 1024 * 1024 }, (error, _stdout, stderr) => {
168
+ if (error) {
169
+ logger.error(`[ClipExtraction] xfade composite failed: ${stderr}`);
170
+ reject(new Error(`xfade composite clip failed: ${error.message}`));
171
+ return;
172
+ }
173
+ logger.info(`[ClipExtraction] xfade composite complete: ${outputPath}`);
174
+ resolve(outputPath);
175
+ });
176
+ });
177
+ }
178
+ //# sourceMappingURL=clipExtraction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipExtraction.js","sourceRoot":"","sources":["../../../src/tools/ffmpeg/clipExtraction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Bounding box and metadata for a detected webcam overlay in a screen recording.
3
+ *
4
+ * @property x - Left edge in pixels (original video resolution)
5
+ * @property y - Top edge in pixels (original video resolution)
6
+ * @property width - Width of the webcam region in pixels
7
+ * @property height - Height of the webcam region in pixels
8
+ * @property position - Which corner of the frame the webcam occupies
9
+ * @property confidence - Detection confidence 0–1 (combines skin-tone consistency
10
+ * across frames with per-frame score strength)
11
+ */
12
+ export interface WebcamRegion {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
18
+ confidence: number;
19
+ }
20
+ /**
21
+ * Per-frame analysis result for a single corner region.
22
+ *
23
+ * @property skinToneRatio - Fraction of pixels matching the skin-tone heuristic (0–1)
24
+ * @property variance - Average RGB channel variance — high variance means the
25
+ * region has complex visual content (a face), low variance means a solid
26
+ * background or static UI element.
27
+ */
28
+ export interface CornerAnalysis {
29
+ position: WebcamRegion['position'];
30
+ x: number;
31
+ y: number;
32
+ width: number;
33
+ height: number;
34
+ skinToneRatio: number;
35
+ variance: number;
36
+ }
37
+ export declare function getVideoResolution(videoPath: string): Promise<{
38
+ width: number;
39
+ height: number;
40
+ }>;
41
+ /**
42
+ * Check if a pixel (in RGB) falls within skin-tone range.
43
+ * Uses simplified HSV heuristic: hue ~0-50°, moderate saturation.
44
+ */
45
+ export declare function isSkinTone(r: number, g: number, b: number): boolean;
46
+ /**
47
+ * Find the position with the largest intensity step between adjacent elements.
48
+ *
49
+ * "Peak difference" = the index where `|means[i+1] - means[i]|` is maximized
50
+ * within the search range. This corresponds to the webcam overlay's edge,
51
+ * because the overlay border creates a hard brightness transition that
52
+ * persists across all frames, while content-based edges average out.
53
+ *
54
+ * @param means - 1-D array of averaged intensities (from column or row means)
55
+ * @param searchFrom - Start of search range (inclusive)
56
+ * @param searchTo - End of search range (inclusive)
57
+ * @param minDiff - Minimum step magnitude to accept (rejects noise)
58
+ * @returns `{index, magnitude}` — index of the edge, or -1 if no edge exceeds minDiff
59
+ */
60
+ export declare function findPeakDiff(means: Float64Array, searchFrom: number, searchTo: number, minDiff: number): {
61
+ index: number;
62
+ magnitude: number;
63
+ };
64
+ /**
65
+ * Refine the webcam bounding box by detecting the overlay's spatial edges.
66
+ *
67
+ * ### Why refinement is needed
68
+ * The coarse phase ({@link detectWebcamRegion}'s corner analysis) only identifies
69
+ * which corner contains a webcam — it uses a fixed 25% region and doesn't know
70
+ * the overlay's exact boundaries. Refinement finds pixel-accurate edges.
71
+ *
72
+ * ### Edge detection algorithm
73
+ * 1. For each sample frame, compute **per-column** and **per-row** mean grayscale
74
+ * intensities (restricted to the webcam's half of the frame for stronger signal).
75
+ * 2. **Average across all frames** — the overlay border is spatially fixed and
76
+ * produces a consistent intensity step, while changing video content (slides,
77
+ * code, etc.) averages out to a smooth gradient. This is the key insight that
78
+ * makes the approach work without traditional edge detection filters.
79
+ * 3. Use {@link findPeakDiff} to locate the maximum inter-adjacent intensity
80
+ * difference in the averaged signal — this is the overlay's vertical and
81
+ * horizontal edge.
82
+ * 4. Sanity-check: the resulting rectangle must be 5–55% of the frame in each
83
+ * dimension (webcams are never tiny or most of the screen).
84
+ *
85
+ * @param framePaths - Paths to sample frames at analysis resolution (320×180)
86
+ * @param position - Which corner contains the webcam (from coarse phase)
87
+ * @returns Refined bounding box in analysis-resolution coordinates, or null
88
+ * if no strong edges are found or the result is implausibly sized
89
+ */
90
+ export declare function refineBoundingBox(framePaths: string[], position: WebcamRegion['position']): Promise<{
91
+ x: number;
92
+ y: number;
93
+ width: number;
94
+ height: number;
95
+ } | null>;
96
+ /**
97
+ * Calculate confidence that a corner contains a webcam overlay based on
98
+ * per-frame scores. Combines consistency (fraction of non-zero frames) with
99
+ * average score.
100
+ */
101
+ export declare function calculateCornerConfidence(scores: number[]): number;
102
+ /**
103
+ * Detect a webcam overlay region in a screen recording.
104
+ *
105
+ * ### Two-phase approach
106
+ *
107
+ * **Phase 1 — Coarse corner scan:**
108
+ * Samples 5 frames at even intervals across the video and analyzes each of the
109
+ * four corners (25% × 25% regions) for skin-tone pixels and visual variance.
110
+ * A corner with consistent skin-tone presence across multiple frames is likely
111
+ * a webcam overlay. The scoring formula weights skin ratio by variance — webcam
112
+ * corners are visually busy (a moving face), while solid-color UI elements
113
+ * (like a colored status bar) have low variance even if they match skin tones.
114
+ *
115
+ * **Phase 2 — Refined edge detection ({@link refineBoundingBox}):**
116
+ * Once we know which corner, we find the overlay's exact pixel boundaries by
117
+ * looking for persistent intensity edges across frames.
118
+ *
119
+ * All analysis is performed on downscaled frames (320×180) for speed, then
120
+ * results are scaled back to the original video resolution.
121
+ *
122
+ * @param videoPath - Path to the source video file
123
+ * @returns The detected webcam region in original video resolution, or null
124
+ * if no webcam overlay is found with sufficient confidence
125
+ */
126
+ export declare function detectWebcamRegion(videoPath: string): Promise<WebcamRegion | null>;
127
+ //# sourceMappingURL=faceDetection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faceDetection.d.ts","sourceRoot":"","sources":["../../../src/tools/ffmpeg/faceDetection.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAA;IACnE,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAA;IAClC,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAiDD,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBtG;AA4CD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAWnE;AA8ID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GACzE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAUtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,GACjC,OAAO,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAoEzE;AAID;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAMlE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmGxF"}