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,464 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ // ── vi.hoisted mock variables ──────────────────────────────────────────────
3
+ const { mockExecFile, mockMkdir, mockRm, mockWriteFile, mockStat, mockMkdtemp, mockCopyFile, mockReaddir, mockUnlink, mockRmdir, mockRename, mockFfmpegInstance, mockFfmpegCtor, mockFfprobe, } = vi.hoisted(() => {
4
+ const inst = {
5
+ input: vi.fn().mockReturnThis(),
6
+ inputOptions: vi.fn().mockReturnThis(),
7
+ output: vi.fn().mockReturnThis(),
8
+ outputOptions: vi.fn().mockReturnThis(),
9
+ audioCodec: vi.fn().mockReturnThis(),
10
+ audioBitrate: vi.fn().mockReturnThis(),
11
+ audioFrequency: vi.fn().mockReturnThis(),
12
+ audioChannels: vi.fn().mockReturnThis(),
13
+ audioFilters: vi.fn().mockReturnThis(),
14
+ noVideo: vi.fn().mockReturnThis(),
15
+ format: vi.fn().mockReturnThis(),
16
+ frames: vi.fn().mockReturnThis(),
17
+ seekInput: vi.fn().mockReturnThis(),
18
+ setStartTime: vi.fn().mockReturnThis(),
19
+ setDuration: vi.fn().mockReturnThis(),
20
+ on: vi.fn(function (event, cb) {
21
+ if (event === 'end')
22
+ setTimeout(() => cb(), 0);
23
+ return this;
24
+ }),
25
+ run: vi.fn(),
26
+ };
27
+ const ctor = vi.fn(() => inst);
28
+ const ffprobe = vi.fn();
29
+ // Attach static helpers to ctor so setFfmpegPath etc. don't blow up
30
+ ctor.setFfmpegPath = vi.fn();
31
+ ctor.setFfprobePath = vi.fn();
32
+ ctor.ffprobe = ffprobe;
33
+ return {
34
+ mockExecFile: vi.fn(),
35
+ mockMkdir: vi.fn().mockResolvedValue(undefined),
36
+ mockRm: vi.fn().mockResolvedValue(undefined),
37
+ mockWriteFile: vi.fn().mockResolvedValue(undefined),
38
+ mockStat: vi.fn(),
39
+ mockMkdtemp: vi.fn(),
40
+ mockCopyFile: vi.fn().mockResolvedValue(undefined),
41
+ mockReaddir: vi.fn().mockResolvedValue([]),
42
+ mockUnlink: vi.fn().mockResolvedValue(undefined),
43
+ mockRmdir: vi.fn().mockResolvedValue(undefined),
44
+ mockRename: vi.fn().mockResolvedValue(undefined),
45
+ mockFfmpegInstance: inst,
46
+ mockFfmpegCtor: ctor,
47
+ mockFfprobe: ffprobe,
48
+ };
49
+ });
50
+ // ── Mocks ──────────────────────────────────────────────────────────────────
51
+ vi.mock('child_process', () => ({
52
+ execFile: mockExecFile,
53
+ }));
54
+ vi.mock('fs', async (importOriginal) => {
55
+ const original = (await importOriginal());
56
+ return {
57
+ ...original,
58
+ promises: {
59
+ ...original.promises,
60
+ mkdir: mockMkdir,
61
+ rm: mockRm,
62
+ writeFile: mockWriteFile,
63
+ stat: mockStat,
64
+ mkdtemp: mockMkdtemp,
65
+ copyFile: mockCopyFile,
66
+ readdir: mockReaddir,
67
+ unlink: mockUnlink,
68
+ rmdir: mockRmdir,
69
+ rename: mockRename,
70
+ },
71
+ };
72
+ });
73
+ vi.mock('fluent-ffmpeg', () => ({
74
+ default: mockFfmpegCtor,
75
+ }));
76
+ vi.mock('../../config/logger.js', () => ({
77
+ default: {
78
+ info: vi.fn(),
79
+ warn: vi.fn(),
80
+ error: vi.fn(),
81
+ debug: vi.fn(),
82
+ },
83
+ }));
84
+ // ── Imports under test (AFTER mocks) ───────────────────────────────────────
85
+ import { extractClip, extractCompositeClip, extractCompositeClipWithTransitions } from '../tools/ffmpeg/clipExtraction.js';
86
+ import { burnCaptions } from '../tools/ffmpeg/captionBurning.js';
87
+ import { extractAudio, splitAudioIntoChunks } from '../tools/ffmpeg/audioExtraction.js';
88
+ import { detectSilence } from '../tools/ffmpeg/silenceDetection.js';
89
+ import { captureFrame, captureFrames } from '../tools/ffmpeg/frameCapture.js';
90
+ // ── Helpers ────────────────────────────────────────────────────────────────
91
+ function resetFfmpegInstance() {
92
+ for (const key of Object.keys(mockFfmpegInstance)) {
93
+ if (typeof mockFfmpegInstance[key]?.mockClear === 'function') {
94
+ mockFfmpegInstance[key].mockClear();
95
+ }
96
+ }
97
+ // Default: fire 'end' immediately
98
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
99
+ if (event === 'end')
100
+ setTimeout(() => cb(), 0);
101
+ return this;
102
+ });
103
+ }
104
+ beforeEach(() => {
105
+ vi.clearAllMocks();
106
+ resetFfmpegInstance();
107
+ });
108
+ // ═══════════════════════════════════════════════════════════════════════════
109
+ // 1. clipExtraction.ts
110
+ // ═══════════════════════════════════════════════════════════════════════════
111
+ describe('clipExtraction', () => {
112
+ describe('extractClip', () => {
113
+ it('creates output dir and calls ffmpeg with correct start/duration', async () => {
114
+ const result = await extractClip('/in.mp4', 10, 20, '/out/clip.mp4', 1);
115
+ expect(mockMkdir).toHaveBeenCalledWith(expect.any(String), { recursive: true });
116
+ expect(mockFfmpegCtor).toHaveBeenCalledWith('/in.mp4');
117
+ expect(mockFfmpegInstance.setStartTime).toHaveBeenCalledWith(9); // 10-1
118
+ expect(mockFfmpegInstance.setDuration).toHaveBeenCalledWith(12); // (20+1)-(10-1)
119
+ expect(mockFfmpegInstance.outputOptions).toHaveBeenCalledWith('-c copy');
120
+ expect(mockFfmpegInstance.run).toHaveBeenCalled();
121
+ expect(result).toBe('/out/clip.mp4');
122
+ });
123
+ it('clamps buffered start to 0 when start < buffer', async () => {
124
+ await extractClip('/in.mp4', 0.5, 5, '/out/clip.mp4', 1);
125
+ expect(mockFfmpegInstance.setStartTime).toHaveBeenCalledWith(0);
126
+ expect(mockFfmpegInstance.setDuration).toHaveBeenCalledWith(6); // (5+1)-0
127
+ });
128
+ it('rejects when ffmpeg emits error', async () => {
129
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
130
+ if (event === 'error')
131
+ setTimeout(() => cb(new Error('boom')), 0);
132
+ return this;
133
+ });
134
+ await expect(extractClip('/in.mp4', 10, 20, '/out/clip.mp4')).rejects.toThrow('Clip extraction failed: boom');
135
+ });
136
+ });
137
+ describe('extractCompositeClip', () => {
138
+ it('throws when no segments', async () => {
139
+ await expect(extractCompositeClip('/in.mp4', [], '/out.mp4')).rejects.toThrow('At least one segment');
140
+ });
141
+ it('delegates to extractClip for single segment', async () => {
142
+ const result = await extractCompositeClip('/in.mp4', [{ start: 5, end: 10, description: 'd' }], '/out.mp4');
143
+ // extractClip uses setStartTime which we can verify
144
+ expect(mockFfmpegInstance.setStartTime).toHaveBeenCalled();
145
+ expect(result).toBe('/out.mp4');
146
+ });
147
+ it('extracts each segment then concatenates for multiple segments', async () => {
148
+ const segs = [
149
+ { start: 5, end: 10, description: 'a' },
150
+ { start: 20, end: 25, description: 'b' },
151
+ ];
152
+ const result = await extractCompositeClip('/in.mp4', segs, '/out.mp4', 1);
153
+ // mkdir for output dir + temp dir
154
+ expect(mockMkdir).toHaveBeenCalledTimes(2);
155
+ // ffmpeg called: 2 segment extractions + 1 concat = 3 times
156
+ expect(mockFfmpegCtor).toHaveBeenCalledTimes(3);
157
+ // concat list written
158
+ expect(mockWriteFile).toHaveBeenCalledTimes(1);
159
+ // temp dir cleaned up
160
+ expect(mockRm).toHaveBeenCalledWith(expect.stringContaining('.temp-'), { recursive: true, force: true });
161
+ expect(result).toBe('/out.mp4');
162
+ });
163
+ });
164
+ describe('extractCompositeClipWithTransitions', () => {
165
+ it('throws when no segments', async () => {
166
+ await expect(extractCompositeClipWithTransitions('/in.mp4', [], '/out.mp4')).rejects.toThrow('At least one segment');
167
+ });
168
+ it('delegates to extractClip for single segment', async () => {
169
+ const result = await extractCompositeClipWithTransitions('/in.mp4', [{ start: 5, end: 10, description: 'd' }], '/out.mp4');
170
+ expect(mockFfmpegInstance.setStartTime).toHaveBeenCalled();
171
+ expect(result).toBe('/out.mp4');
172
+ });
173
+ it('delegates to extractCompositeClip for 2 segments with transitionDuration=0', async () => {
174
+ const segs = [
175
+ { start: 5, end: 10, description: 'a' },
176
+ { start: 20, end: 25, description: 'b' },
177
+ ];
178
+ const result = await extractCompositeClipWithTransitions('/in.mp4', segs, '/out.mp4', 0, 1);
179
+ // Should go through extractCompositeClip path (ffmpegCtor called 3 times)
180
+ expect(mockFfmpegCtor.mock.calls.length).toBeGreaterThanOrEqual(2);
181
+ expect(result).toBe('/out.mp4');
182
+ });
183
+ it('uses execFile with xfade filter_complex for 3+ segments', async () => {
184
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
185
+ if (cb)
186
+ cb(null, '', '');
187
+ return { on: vi.fn() };
188
+ });
189
+ const segs = [
190
+ { start: 0, end: 5, description: 'a' },
191
+ { start: 10, end: 15, description: 'b' },
192
+ { start: 20, end: 25, description: 'c' },
193
+ ];
194
+ const result = await extractCompositeClipWithTransitions('/in.mp4', segs, '/out.mp4', 0.5, 1);
195
+ expect(mockExecFile).toHaveBeenCalledTimes(1);
196
+ const args = mockExecFile.mock.calls[0][1];
197
+ expect(args).toContain('-filter_complex');
198
+ const filterIdx = args.indexOf('-filter_complex');
199
+ const filter = args[filterIdx + 1];
200
+ expect(filter).toContain('xfade');
201
+ expect(filter).toContain('acrossfade');
202
+ expect(filter).toContain('[vout]');
203
+ expect(filter).toContain('[aout]');
204
+ expect(args).toContain('/out.mp4');
205
+ expect(result).toBe('/out.mp4');
206
+ });
207
+ it('rejects when execFile returns error', async () => {
208
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
209
+ if (cb)
210
+ cb(new Error('xfade fail'), '', 'stderr msg');
211
+ return { on: vi.fn() };
212
+ });
213
+ const segs = [
214
+ { start: 0, end: 5, description: 'a' },
215
+ { start: 10, end: 15, description: 'b' },
216
+ { start: 20, end: 25, description: 'c' },
217
+ ];
218
+ await expect(extractCompositeClipWithTransitions('/in.mp4', segs, '/out.mp4', 0.5, 1)).rejects.toThrow('xfade composite clip failed');
219
+ });
220
+ });
221
+ });
222
+ // ═══════════════════════════════════════════════════════════════════════════
223
+ // 2. captionBurning.ts
224
+ // ═══════════════════════════════════════════════════════════════════════════
225
+ describe('captionBurning', () => {
226
+ beforeEach(() => {
227
+ mockMkdtemp.mockResolvedValue('/tmp/caption-xyz');
228
+ mockReaddir.mockResolvedValue(['Bold.ttf', 'Italic.otf', 'notes.txt']);
229
+ });
230
+ it('copies ASS file to temp dir and calls execFile with correct args', async () => {
231
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
232
+ if (cb)
233
+ cb(null, '', '');
234
+ return { on: vi.fn() };
235
+ });
236
+ const result = await burnCaptions('/video.mp4', '/subs.ass', '/out/burned.mp4');
237
+ expect(mockMkdtemp).toHaveBeenCalled();
238
+ expect(mockCopyFile).toHaveBeenCalledWith('/subs.ass', expect.stringContaining('captions.ass'));
239
+ // Fonts copied (only .ttf and .otf)
240
+ expect(mockCopyFile).toHaveBeenCalledWith(expect.stringContaining('Bold.ttf'), expect.any(String));
241
+ expect(mockCopyFile).toHaveBeenCalledWith(expect.stringContaining('Italic.otf'), expect.any(String));
242
+ const args = mockExecFile.mock.calls[0][1];
243
+ expect(args).toContain('-vf');
244
+ expect(args).toContain('ass=captions.ass:fontsdir=.');
245
+ // Cleanup
246
+ expect(mockReaddir).toHaveBeenCalled();
247
+ expect(result).toBe('/out/burned.mp4');
248
+ });
249
+ it('cleans up temp dir on failure', async () => {
250
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
251
+ if (cb)
252
+ cb(new Error('burn fail'), '', 'stderr err');
253
+ return { on: vi.fn() };
254
+ });
255
+ // readdir returns files for cleanup
256
+ mockReaddir.mockResolvedValueOnce(['Bold.ttf', 'Italic.otf', 'notes.txt'])
257
+ .mockResolvedValueOnce(['captions.ass', 'output.mp4']);
258
+ await expect(burnCaptions('/v.mp4', '/s.ass', '/out.mp4')).rejects.toThrow('Caption burning failed');
259
+ // Cleanup still runs
260
+ expect(mockUnlink).toHaveBeenCalled();
261
+ expect(mockRmdir).toHaveBeenCalled();
262
+ });
263
+ it('falls back to copyFile when rename fails', async () => {
264
+ mockRename.mockRejectedValueOnce(new Error('EXDEV'));
265
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
266
+ if (cb)
267
+ cb(null, '', '');
268
+ return { on: vi.fn() };
269
+ });
270
+ const result = await burnCaptions('/video.mp4', '/subs.ass', '/out/burned.mp4');
271
+ expect(mockRename).toHaveBeenCalled();
272
+ // copyFile called for the output fallback (beyond the initial ASS + font copies)
273
+ expect(mockCopyFile).toHaveBeenCalled();
274
+ expect(result).toBe('/out/burned.mp4');
275
+ });
276
+ });
277
+ // ═══════════════════════════════════════════════════════════════════════════
278
+ // 3. audioExtraction.ts
279
+ // ═══════════════════════════════════════════════════════════════════════════
280
+ describe('audioExtraction', () => {
281
+ describe('extractAudio', () => {
282
+ it('extracts mp3 audio by default', async () => {
283
+ const result = await extractAudio('/video.mp4', '/out/audio.mp3');
284
+ expect(mockMkdir).toHaveBeenCalledWith(expect.any(String), { recursive: true });
285
+ expect(mockFfmpegCtor).toHaveBeenCalledWith('/video.mp4');
286
+ expect(mockFfmpegInstance.noVideo).toHaveBeenCalled();
287
+ expect(mockFfmpegInstance.audioChannels).toHaveBeenCalledWith(1);
288
+ expect(mockFfmpegInstance.audioCodec).toHaveBeenCalledWith('libmp3lame');
289
+ expect(mockFfmpegInstance.audioBitrate).toHaveBeenCalledWith('64k');
290
+ expect(mockFfmpegInstance.audioFrequency).toHaveBeenCalledWith(16000);
291
+ expect(mockFfmpegInstance.run).toHaveBeenCalled();
292
+ expect(result).toBe('/out/audio.mp3');
293
+ });
294
+ it('extracts wav audio when format is wav', async () => {
295
+ await extractAudio('/video.mp4', '/out/audio.wav', { format: 'wav' });
296
+ expect(mockFfmpegInstance.audioCodec).toHaveBeenCalledWith('pcm_s16le');
297
+ expect(mockFfmpegInstance.audioFrequency).toHaveBeenCalledWith(16000);
298
+ });
299
+ it('rejects when ffmpeg emits error', async () => {
300
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
301
+ if (event === 'error')
302
+ setTimeout(() => cb(new Error('audio fail')), 0);
303
+ return this;
304
+ });
305
+ await expect(extractAudio('/v.mp4', '/o.mp3')).rejects.toThrow('Audio extraction failed: audio fail');
306
+ });
307
+ });
308
+ describe('splitAudioIntoChunks', () => {
309
+ it('returns single path when file is small enough', async () => {
310
+ mockStat.mockResolvedValue({ size: 10 * 1024 * 1024 }); // 10MB
311
+ const result = await splitAudioIntoChunks('/audio.mp3', 24);
312
+ expect(result).toEqual(['/audio.mp3']);
313
+ });
314
+ it('splits large file into chunks', async () => {
315
+ mockStat.mockResolvedValue({ size: 48 * 1024 * 1024 }); // 48MB
316
+ mockFfprobe.mockImplementation((_path, cb) => {
317
+ cb(null, { format: { duration: 600 } }); // 10 minutes
318
+ });
319
+ const result = await splitAudioIntoChunks('/audio.mp3', 24);
320
+ // 48MB / 24MB = 2 chunks
321
+ expect(result).toHaveLength(2);
322
+ expect(result[0]).toContain('_chunk0');
323
+ expect(result[1]).toContain('_chunk1');
324
+ // ffmpeg called twice for chunk extraction
325
+ expect(mockFfmpegCtor).toHaveBeenCalledTimes(2);
326
+ expect(mockFfmpegInstance.setStartTime).toHaveBeenCalledWith(0);
327
+ expect(mockFfmpegInstance.setStartTime).toHaveBeenCalledWith(300);
328
+ expect(mockFfmpegInstance.setDuration).toHaveBeenCalledWith(300);
329
+ });
330
+ it('rejects when ffprobe fails', async () => {
331
+ mockStat.mockResolvedValue({ size: 48 * 1024 * 1024 });
332
+ mockFfprobe.mockImplementation((_path, cb) => {
333
+ cb(new Error('probe fail'));
334
+ });
335
+ await expect(splitAudioIntoChunks('/audio.mp3', 24)).rejects.toThrow('ffprobe failed');
336
+ });
337
+ });
338
+ });
339
+ // ═══════════════════════════════════════════════════════════════════════════
340
+ // 4. silenceDetection.ts
341
+ // ═══════════════════════════════════════════════════════════════════════════
342
+ describe('silenceDetection', () => {
343
+ it('parses silence regions from ffmpeg stderr', async () => {
344
+ const stderrLines = [
345
+ '[silencedetect @ 0x1234] silence_start: 1.5',
346
+ '[silencedetect @ 0x1234] silence_end: 3.5 | silence_duration: 2.0',
347
+ '[silencedetect @ 0x1234] silence_start: 10.0',
348
+ '[silencedetect @ 0x1234] silence_end: 12.5 | silence_duration: 2.5',
349
+ ];
350
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
351
+ if (event === 'stderr') {
352
+ setTimeout(() => {
353
+ for (const line of stderrLines)
354
+ cb(line);
355
+ }, 0);
356
+ }
357
+ if (event === 'end') {
358
+ setTimeout(() => cb(), 10);
359
+ }
360
+ return this;
361
+ });
362
+ const result = await detectSilence('/audio.mp3', 1.0, '-30dB');
363
+ expect(result).toHaveLength(2);
364
+ expect(result[0]).toEqual({ start: 1.5, end: 3.5, duration: 2.0 });
365
+ expect(result[1]).toEqual({ start: 10.0, end: 12.5, duration: 2.5 });
366
+ expect(mockFfmpegInstance.audioFilters).toHaveBeenCalledWith('silencedetect=noise=-30dB:d=1');
367
+ expect(mockFfmpegInstance.format).toHaveBeenCalledWith('null');
368
+ });
369
+ it('handles silence starting at t=0 (no explicit silence_start)', async () => {
370
+ const stderrLines = [
371
+ '[silencedetect @ 0x1234] silence_end: 2.0 | silence_duration: 2.0',
372
+ ];
373
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
374
+ if (event === 'stderr') {
375
+ setTimeout(() => { for (const line of stderrLines)
376
+ cb(line); }, 0);
377
+ }
378
+ if (event === 'end')
379
+ setTimeout(() => cb(), 10);
380
+ return this;
381
+ });
382
+ const result = await detectSilence('/audio.mp3');
383
+ expect(result).toHaveLength(1);
384
+ expect(result[0]).toEqual({ start: 0, end: 2.0, duration: 2.0 });
385
+ });
386
+ it('filters out invalid regions (end <= start)', async () => {
387
+ const stderrLines = [
388
+ '[silencedetect @ 0x1234] silence_start: 5.0',
389
+ '[silencedetect @ 0x1234] silence_end: 5.0 | silence_duration: 0.0',
390
+ '[silencedetect @ 0x1234] silence_start: 10.0',
391
+ '[silencedetect @ 0x1234] silence_end: 12.0 | silence_duration: 2.0',
392
+ ];
393
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
394
+ if (event === 'stderr') {
395
+ setTimeout(() => { for (const line of stderrLines)
396
+ cb(line); }, 0);
397
+ }
398
+ if (event === 'end')
399
+ setTimeout(() => cb(), 10);
400
+ return this;
401
+ });
402
+ const result = await detectSilence('/audio.mp3');
403
+ expect(result).toHaveLength(1);
404
+ expect(result[0]).toEqual({ start: 10.0, end: 12.0, duration: 2.0 });
405
+ });
406
+ it('rejects when ffmpeg emits error', async () => {
407
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
408
+ if (event === 'error')
409
+ setTimeout(() => cb(new Error('detect fail')), 0);
410
+ return this;
411
+ });
412
+ await expect(detectSilence('/audio.mp3')).rejects.toThrow('Silence detection failed: detect fail');
413
+ });
414
+ it('returns empty array when no silence detected', async () => {
415
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
416
+ if (event === 'stderr') {
417
+ setTimeout(() => cb('some other ffmpeg output'), 0);
418
+ }
419
+ if (event === 'end')
420
+ setTimeout(() => cb(), 10);
421
+ return this;
422
+ });
423
+ const result = await detectSilence('/audio.mp3');
424
+ expect(result).toEqual([]);
425
+ });
426
+ });
427
+ // ═══════════════════════════════════════════════════════════════════════════
428
+ // 5. frameCapture.ts
429
+ // ═══════════════════════════════════════════════════════════════════════════
430
+ describe('frameCapture', () => {
431
+ describe('captureFrame', () => {
432
+ it('captures frame at given timestamp', async () => {
433
+ const result = await captureFrame('/video.mp4', 30.5, '/out/frame.png');
434
+ expect(mockMkdir).toHaveBeenCalledWith(expect.any(String), { recursive: true });
435
+ expect(mockFfmpegCtor).toHaveBeenCalledWith('/video.mp4');
436
+ expect(mockFfmpegInstance.seekInput).toHaveBeenCalledWith(30.5);
437
+ expect(mockFfmpegInstance.frames).toHaveBeenCalledWith(1);
438
+ expect(mockFfmpegInstance.output).toHaveBeenCalledWith('/out/frame.png');
439
+ expect(mockFfmpegInstance.run).toHaveBeenCalled();
440
+ expect(result).toBe('/out/frame.png');
441
+ });
442
+ it('rejects when ffmpeg emits error', async () => {
443
+ mockFfmpegInstance.on.mockImplementation(function (event, cb) {
444
+ if (event === 'error')
445
+ setTimeout(() => cb(new Error('frame fail')), 0);
446
+ return this;
447
+ });
448
+ await expect(captureFrame('/v.mp4', 10, '/o.png')).rejects.toThrow('Frame capture failed: frame fail');
449
+ });
450
+ });
451
+ describe('captureFrames', () => {
452
+ it('captures multiple frames with correct naming', async () => {
453
+ const result = await captureFrames('/video.mp4', [10, 30, 60], '/out/frames');
454
+ expect(mockMkdir).toHaveBeenCalled();
455
+ // captureFrame called 3 times => ffmpeg ctor called 3 times
456
+ expect(mockFfmpegCtor).toHaveBeenCalledTimes(3);
457
+ expect(result).toHaveLength(3);
458
+ expect(result[0]).toContain('snapshot-001.png');
459
+ expect(result[1]).toContain('snapshot-002.png');
460
+ expect(result[2]).toContain('snapshot-003.png');
461
+ });
462
+ });
463
+ });
464
+ //# sourceMappingURL=ffmpegTools.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffmpegTools.test.js","sourceRoot":"","sources":["../../src/__tests__/ffmpegTools.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=captionBurn.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captionBurn.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/captionBurn.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { setupFixtures, cleanupFixtures, isFFmpegAvailable, fileExistsWithSize, getVideoDuration } from './fixture.js';
3
+ import { generateSRT, generateVTT, generateStyledASS, generateStyledASSForSegment } from '../../tools/captions/captionGenerator.js';
4
+ import { burnCaptions } from '../../tools/ffmpeg/captionBurning.js';
5
+ import path from 'path';
6
+ import { promises as fs } from 'fs';
7
+ const ffmpegOk = await isFFmpegAvailable();
8
+ describe.skipIf(!ffmpegOk)('Caption Burn Integration', () => {
9
+ let fix;
10
+ let transcript;
11
+ beforeAll(async () => {
12
+ fix = await setupFixtures();
13
+ const raw = await fs.readFile(fix.transcriptPath, 'utf-8');
14
+ transcript = JSON.parse(raw);
15
+ }, 30000);
16
+ afterAll(async () => { await cleanupFixtures(); });
17
+ // ── 1. Caption Generation ──────────────────────────────────────────────
18
+ describe('Caption Generation', () => {
19
+ it('generateSRT produces valid SRT format', () => {
20
+ const srt = generateSRT(transcript);
21
+ // Numbered cues starting at 1
22
+ expect(srt).toMatch(/^1\n/);
23
+ // SRT timestamps with comma separator
24
+ expect(srt).toMatch(/\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}/);
25
+ // Has at least two cues (matches the two segments)
26
+ expect(srt).toContain('2\n');
27
+ });
28
+ it('generateVTT includes WEBVTT header', () => {
29
+ const vtt = generateVTT(transcript);
30
+ expect(vtt).toMatch(/^WEBVTT/);
31
+ // VTT timestamps with dot separator
32
+ expect(vtt).toMatch(/\d{2}:\d{2}:\d{2}\.\d{3} --> \d{2}:\d{2}:\d{2}\.\d{3}/);
33
+ });
34
+ it('generateStyledASS produces valid ASS with Montserrat font', () => {
35
+ const ass = generateStyledASS(transcript);
36
+ expect(ass).toContain('[Script Info]');
37
+ expect(ass).toContain('Montserrat');
38
+ expect(ass).toMatch(/Dialogue:/);
39
+ });
40
+ it('generateStyledASS with medium style uses smaller font sizes', () => {
41
+ const assDefault = generateStyledASS(transcript, 'shorts');
42
+ const assMedium = generateStyledASS(transcript, 'medium');
43
+ // Medium header has Fontsize 32 vs default 42
44
+ expect(assMedium).toContain('Fontsize');
45
+ // Extract base font size from Style line
46
+ const defaultStyleMatch = assDefault.match(/Style: Default,Montserrat,(\d+)/);
47
+ const mediumStyleMatch = assMedium.match(/Style: Default,Montserrat,(\d+)/);
48
+ expect(defaultStyleMatch).not.toBeNull();
49
+ expect(mediumStyleMatch).not.toBeNull();
50
+ const defaultSize = parseInt(defaultStyleMatch[1], 10);
51
+ const mediumSize = parseInt(mediumStyleMatch[1], 10);
52
+ expect(mediumSize).toBeLessThan(defaultSize);
53
+ });
54
+ });
55
+ // ── 2. Caption Burning with real FFmpeg ────────────────────────────────
56
+ describe('Caption Burning', () => {
57
+ it('burns captions into video producing valid output', async () => {
58
+ const assContent = generateStyledASS(transcript);
59
+ const assPath = path.join(fix.dir, 'real-captions.ass');
60
+ await fs.writeFile(assPath, assContent);
61
+ const outputPath = path.join(fix.dir, 'burned-output.mp4');
62
+ await burnCaptions(fix.videoPath, assPath, outputPath);
63
+ const { exists, size } = await fileExistsWithSize(outputPath);
64
+ expect(exists).toBe(true);
65
+ expect(size).toBeGreaterThan(0);
66
+ const inputDuration = await getVideoDuration(fix.videoPath);
67
+ const outputDuration = await getVideoDuration(outputPath);
68
+ expect(Math.abs(outputDuration - inputDuration)).toBeLessThan(0.5);
69
+ }, 60000);
70
+ });
71
+ // ── 3. Segment-specific captions ───────────────────────────────────────
72
+ describe('Segment-specific captions', () => {
73
+ it('generates captions only for the specified time range', () => {
74
+ const segmentASS = generateStyledASSForSegment(transcript, 0.5, 2.5);
75
+ // With default buffer=1.0, bufferedStart = max(0, 0.5-1) = 0, bufferedEnd = 3.5
76
+ // Words in [0, 3.5]: Hello(0-0.3) world(0.4-0.7) this(0.8-1.0) is(1.1-1.2) a(1.3-1.4) test(1.5-1.9) of(2.5-2.6) the(2.7-2.8) caption(2.9-3.2)
77
+ expect(segmentASS).toContain('Dialogue:');
78
+ // Words outside the buffered range should not appear
79
+ // "system" starts at 3.3, ends at 3.6 — end 3.6 > bufferedEnd 3.5, so excluded
80
+ expect(segmentASS).not.toMatch(/\bsystem\b/);
81
+ // "multiple" and "words" are also outside
82
+ expect(segmentASS).not.toMatch(/\bmultiple\b/);
83
+ expect(segmentASS).not.toMatch(/\bwords\b/);
84
+ });
85
+ it('adjusts ASS Dialogue timestamps relative to segment start', () => {
86
+ const segmentASS = generateStyledASSForSegment(transcript, 0.5, 2.5);
87
+ // bufferedStart = 0, so timestamps should start near 0:00:00.00
88
+ const dialogueLines = segmentASS.split('\n').filter(l => l.startsWith('Dialogue:'));
89
+ expect(dialogueLines.length).toBeGreaterThan(0);
90
+ // First dialogue should start at 0:00:00.00 (Hello word at 0.0 - bufferedStart 0)
91
+ const firstLine = dialogueLines[0];
92
+ expect(firstLine).toMatch(/Dialogue: 0,0:00:00\.00,/);
93
+ });
94
+ it('excludes all words when segment has no matching words', () => {
95
+ // Range 10-11s with buffer=1 → bufferedStart=9, bufferedEnd=12 — no words exist there
96
+ const emptyASS = generateStyledASSForSegment(transcript, 10, 11);
97
+ expect(emptyASS).not.toContain('Dialogue:');
98
+ // Should still have a valid header
99
+ expect(emptyASS).toContain('[Script Info]');
100
+ });
101
+ });
102
+ });
103
+ //# sourceMappingURL=captionBurn.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captionBurn.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/captionBurn.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=clipComposite.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipComposite.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/clipComposite.test.ts"],"names":[],"mappings":""}