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,226 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { execFile } from 'child_process';
3
+ import { promisify } from 'util';
4
+ import { promises as fs } from 'fs';
5
+ import path from 'path';
6
+ import os from 'os';
7
+ import 'dotenv/config';
8
+ import { isFFmpegAvailable, getVideoDuration, fileExistsWithSize } from './fixture.js';
9
+ import { generateSRT, generateVTT, generateStyledASS, generateStyledASSForSegment, } from '../../tools/captions/captionGenerator.js';
10
+ import { burnCaptions } from '../../tools/ffmpeg/captionBurning.js';
11
+ const execFileAsync = promisify(execFile);
12
+ const ffprobePath = process.env.FFPROBE_PATH || 'ffprobe';
13
+ const ffmpegOk = await isFFmpegAvailable();
14
+ async function getStreams(videoPath) {
15
+ const { stdout } = await execFileAsync(ffprobePath, [
16
+ '-v', 'error', '-show_entries', 'stream=codec_type', '-of', 'csv=p=0', videoPath,
17
+ ]);
18
+ const types = stdout.trim().split('\n').map((t) => t.trim());
19
+ return { video: types.includes('video'), audio: types.includes('audio') };
20
+ }
21
+ describe.skipIf(!ffmpegOk)('Real Video Caption Tests', () => {
22
+ let tempDir;
23
+ let transcript;
24
+ const fixtureDir = path.join(import.meta.dirname, 'fixtures');
25
+ const videoPath = path.join(fixtureDir, 'sample-speech.mp4');
26
+ beforeAll(async () => {
27
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'vantr-realcap-'));
28
+ const raw = await fs.readFile(path.join(fixtureDir, 'sample-speech-transcript.json'), 'utf-8');
29
+ transcript = JSON.parse(raw);
30
+ }, 30000);
31
+ afterAll(async () => {
32
+ await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
33
+ });
34
+ // ── 1. Caption File Generation ──────────────────────────────────────────
35
+ describe('Caption file generation', () => {
36
+ let srtPath;
37
+ let vttPath;
38
+ let assPath;
39
+ beforeAll(async () => {
40
+ srtPath = path.join(tempDir, 'captions.srt');
41
+ vttPath = path.join(tempDir, 'captions.vtt');
42
+ assPath = path.join(tempDir, 'captions.ass');
43
+ await fs.writeFile(srtPath, generateSRT(transcript));
44
+ await fs.writeFile(vttPath, generateVTT(transcript));
45
+ await fs.writeFile(assPath, generateStyledASS(transcript));
46
+ });
47
+ it('generates SRT, VTT, and ASS files with non-zero size', async () => {
48
+ for (const fp of [srtPath, vttPath, assPath]) {
49
+ const { exists, size } = await fileExistsWithSize(fp);
50
+ expect(exists).toBe(true);
51
+ expect(size).toBeGreaterThan(0);
52
+ }
53
+ });
54
+ it('ASS file contains karaoke \\k tags via active-word font-size overrides', async () => {
55
+ const ass = await fs.readFile(assPath, 'utf-8');
56
+ // Active-word highlighting uses \fs (font-size) and \c (color) overrides
57
+ expect(ass).toContain('\\fs54');
58
+ expect(ass).toContain('\\c&H00FFFF&'); // yellow active color
59
+ });
60
+ it('ASS file has multi-line \\N separator', async () => {
61
+ const ass = await fs.readFile(assPath, 'utf-8');
62
+ expect(ass).toContain('\\N');
63
+ });
64
+ it('word count in ASS roughly matches transcript word count', async () => {
65
+ const ass = await fs.readFile(assPath, 'utf-8');
66
+ const dialogueLines = ass.split('\n').filter((l) => l.startsWith('Dialogue:'));
67
+ // Each dialogue line shows all words in the group but highlights one;
68
+ // total unique words referenced should be close to transcript.words.length (81)
69
+ // Count unique words by looking at the last dialogue for each group
70
+ // Simpler: total dialogue lines >= transcript word count (one per word-state)
71
+ expect(dialogueLines.length).toBeGreaterThanOrEqual(transcript.words.length);
72
+ });
73
+ it('caption timestamps span from near 0s to near 31s', async () => {
74
+ const ass = await fs.readFile(assPath, 'utf-8');
75
+ const dialogueLines = ass.split('\n').filter((l) => l.startsWith('Dialogue:'));
76
+ // Parse first dialogue start time
77
+ const firstMatch = dialogueLines[0].match(/Dialogue: 0,(\d+:\d{2}:\d{2}\.\d{2}),/);
78
+ expect(firstMatch).not.toBeNull();
79
+ const firstSeconds = parseAssTimestamp(firstMatch[1]);
80
+ expect(firstSeconds).toBeLessThan(1); // starts near 0s
81
+ // Parse last dialogue end time
82
+ const lastLine = dialogueLines[dialogueLines.length - 1];
83
+ const lastMatch = lastLine.match(/Dialogue: 0,\d+:\d{2}:\d{2}\.\d{2},(\d+:\d{2}:\d{2}\.\d{2}),/);
84
+ expect(lastMatch).not.toBeNull();
85
+ const lastSeconds = parseAssTimestamp(lastMatch[1]);
86
+ expect(lastSeconds).toBeGreaterThan(29); // ends near 31s
87
+ });
88
+ });
89
+ // ── 2. Caption Style Variants ───────────────────────────────────────────
90
+ describe('Caption style variants', () => {
91
+ it('shorts style uses larger font sizes (54 / 42)', () => {
92
+ const ass = generateStyledASS(transcript, 'shorts');
93
+ expect(ass).toMatch(/Style: Default,Montserrat,42/);
94
+ expect(ass).toContain('\\fs54');
95
+ expect(ass).toContain('\\fs42');
96
+ });
97
+ it('medium style uses smaller font sizes (40 / 32)', () => {
98
+ const ass = generateStyledASS(transcript, 'medium');
99
+ expect(ass).toMatch(/Style: Default,Montserrat,32/);
100
+ expect(ass).toContain('\\fs40');
101
+ expect(ass).toContain('\\fs32');
102
+ });
103
+ });
104
+ // ── 3. Speech Gap Detection ─────────────────────────────────────────────
105
+ describe('Speech gap detection', () => {
106
+ it('ASS has multiple Dialogue entries (not one giant block)', () => {
107
+ const ass = generateStyledASS(transcript);
108
+ const dialogueLines = ass.split('\n').filter((l) => l.startsWith('Dialogue:'));
109
+ expect(dialogueLines.length).toBeGreaterThan(10);
110
+ });
111
+ it('no caption events span across detected speech gaps (>0.8s)', () => {
112
+ const ass = generateStyledASS(transcript);
113
+ const dialogueLines = ass.split('\n').filter((l) => l.startsWith('Dialogue:'));
114
+ // Find gaps >0.8s between consecutive words in transcript
115
+ const gaps = [];
116
+ for (let i = 0; i < transcript.words.length - 1; i++) {
117
+ const gap = transcript.words[i + 1].start - transcript.words[i].end;
118
+ if (gap > 0.8) {
119
+ gaps.push({
120
+ gapStart: transcript.words[i].end,
121
+ gapEnd: transcript.words[i + 1].start,
122
+ });
123
+ }
124
+ }
125
+ expect(gaps.length).toBeGreaterThan(0); // sanity: real speech has gaps
126
+ // No dialogue event should span entirely across a gap
127
+ for (const { gapStart, gapEnd } of gaps) {
128
+ for (const line of dialogueLines) {
129
+ const m = line.match(/Dialogue: 0,(\d+:\d{2}:\d{2}\.\d{2}),(\d+:\d{2}:\d{2}\.\d{2}),/);
130
+ if (!m)
131
+ continue;
132
+ const start = parseAssTimestamp(m[1]);
133
+ const end = parseAssTimestamp(m[2]);
134
+ // A dialogue event should not start before the gap and end after it
135
+ const spansGap = start < gapStart + 0.01 && end > gapEnd - 0.01;
136
+ expect(spansGap).toBe(false);
137
+ }
138
+ }
139
+ });
140
+ });
141
+ // ── 4. Real Caption Burning ─────────────────────────────────────────────
142
+ describe('Real caption burning', () => {
143
+ it('burns captions onto real video with valid output', async () => {
144
+ const assContent = generateStyledASS(transcript);
145
+ const assPath = path.join(tempDir, 'burn-captions.ass');
146
+ await fs.writeFile(assPath, assContent);
147
+ const outputPath = path.join(tempDir, 'burned-output.mp4');
148
+ await burnCaptions(videoPath, assPath, outputPath);
149
+ // Output exists with non-zero size
150
+ const { exists, size } = await fileExistsWithSize(outputPath);
151
+ expect(exists).toBe(true);
152
+ expect(size).toBeGreaterThan(0);
153
+ // Duration matches input (±0.5s)
154
+ const inputDuration = await getVideoDuration(videoPath);
155
+ const outputDuration = await getVideoDuration(outputPath);
156
+ expect(Math.abs(outputDuration - inputDuration)).toBeLessThan(0.5);
157
+ // Has both video and audio streams
158
+ const { video, audio } = await getStreams(outputPath);
159
+ expect(video).toBe(true);
160
+ expect(audio).toBe(true);
161
+ }, 90000);
162
+ });
163
+ // ── 5. Segment-specific Caption Generation ──────────────────────────────
164
+ describe('Segment-specific caption generation', () => {
165
+ it('generates captions only for the 5s–15s time range', () => {
166
+ const fullASS = generateStyledASS(transcript);
167
+ const segmentASS = generateStyledASSForSegment(transcript, 5, 15);
168
+ const fullDialogues = fullASS.split('\n').filter((l) => l.startsWith('Dialogue:'));
169
+ const segDialogues = segmentASS.split('\n').filter((l) => l.startsWith('Dialogue:'));
170
+ // Segment captions should have fewer dialogue lines than full
171
+ expect(segDialogues.length).toBeGreaterThan(0);
172
+ expect(segDialogues.length).toBeLessThan(fullDialogues.length);
173
+ // All timestamps in segment ASS should be within the buffered range
174
+ // buffer=1.0 → words from 4s to 16s, timestamps rebased to 0
175
+ for (const line of segDialogues) {
176
+ const m = line.match(/Dialogue: 0,(\d+:\d{2}:\d{2}\.\d{2}),(\d+:\d{2}:\d{2}\.\d{2}),/);
177
+ if (!m)
178
+ continue;
179
+ const start = parseAssTimestamp(m[1]);
180
+ const end = parseAssTimestamp(m[2]);
181
+ // Rebased timestamps: max duration = (15+1) - (5-1) = 12s
182
+ expect(start).toBeGreaterThanOrEqual(0);
183
+ expect(end).toBeLessThanOrEqual(13);
184
+ }
185
+ });
186
+ it('contains fewer words than full-video captions', () => {
187
+ const fullASS = generateStyledASS(transcript);
188
+ const segmentASS = generateStyledASSForSegment(transcript, 5, 15);
189
+ // Count unique word text tokens in dialogue lines
190
+ const countWords = (ass) => {
191
+ const dialogues = ass.split('\n').filter((l) => l.startsWith('Dialogue:'));
192
+ return dialogues.length; // one dialogue per word-state
193
+ };
194
+ expect(countWords(segmentASS)).toBeLessThan(countWords(fullASS));
195
+ });
196
+ });
197
+ // ── 6. Portrait Captions on Real Video ──────────────────────────────────
198
+ describe('Portrait caption burning', () => {
199
+ it('burns portrait captions onto real video with valid output', async () => {
200
+ const assContent = generateStyledASS(transcript, 'portrait');
201
+ const assPath = path.join(tempDir, 'portrait-captions.ass');
202
+ await fs.writeFile(assPath, assContent);
203
+ // Verify portrait header properties
204
+ expect(assContent).toContain('PlayResX: 1080');
205
+ expect(assContent).toContain('PlayResY: 1920');
206
+ expect(assContent).toContain('\\c&H00FF00&'); // green active
207
+ const outputPath = path.join(tempDir, 'portrait-burned-output.mp4');
208
+ await burnCaptions(videoPath, assPath, outputPath);
209
+ // Output exists with non-zero size
210
+ const { exists, size } = await fileExistsWithSize(outputPath);
211
+ expect(exists).toBe(true);
212
+ expect(size).toBeGreaterThan(0);
213
+ // Duration matches input (±0.5s)
214
+ const inputDuration = await getVideoDuration(videoPath);
215
+ const outputDuration = await getVideoDuration(outputPath);
216
+ expect(Math.abs(outputDuration - inputDuration)).toBeLessThan(0.5);
217
+ }, 90000);
218
+ });
219
+ });
220
+ /** Parse ASS timestamp "H:MM:SS.cc" → seconds */
221
+ function parseAssTimestamp(ts) {
222
+ const [h, m, rest] = ts.split(':');
223
+ const [s, cs] = rest.split('.');
224
+ return parseInt(h) * 3600 + parseInt(m) * 60 + parseInt(s) + parseInt(cs) / 100;
225
+ }
226
+ //# sourceMappingURL=realCaptions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realCaptions.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/realCaptions.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import 'dotenv/config';
2
+ //# sourceMappingURL=realPipeline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realPipeline.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/realPipeline.test.ts"],"names":[],"mappings":"AAMA,OAAO,eAAe,CAAC"}
@@ -0,0 +1,210 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { execFile } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import 'dotenv/config';
8
+ import { isFFmpegAvailable, getVideoDuration, fileExistsWithSize } from './fixture.js';
9
+ import { extractAudio } from '../../tools/ffmpeg/audioExtraction.js';
10
+ import { detectSilence } from '../../tools/ffmpeg/silenceDetection.js';
11
+ import { extractClip, extractCompositeClip } from '../../tools/ffmpeg/clipExtraction.js';
12
+ import { captureFrame } from '../../tools/ffmpeg/frameCapture.js';
13
+ import { singlePassEdit, singlePassEditAndCaption } from '../../tools/ffmpeg/singlePassEdit.js';
14
+ import { generateStyledASS } from '../../tools/captions/captionGenerator.js';
15
+ const execFileAsync = promisify(execFile);
16
+ const ffprobePath = process.env.FFPROBE_PATH || 'ffprobe';
17
+ const ffmpegOk = await isFFmpegAvailable();
18
+ async function getStreamInfo(filePath) {
19
+ const { stdout } = await execFileAsync(ffprobePath, [
20
+ '-v', 'error', '-show_entries', 'stream=codec_type', '-of', 'csv=p=0', filePath,
21
+ ]);
22
+ const types = stdout.trim().split('\n').map(s => s.trim());
23
+ return { hasVideo: types.includes('video'), hasAudio: types.includes('audio') };
24
+ }
25
+ async function getAudioChannels(filePath) {
26
+ const { stdout } = await execFileAsync(ffprobePath, [
27
+ '-v', 'error', '-select_streams', 'a:0',
28
+ '-show_entries', 'stream=channels', '-of', 'csv=p=0', filePath,
29
+ ]);
30
+ return parseInt(stdout.trim(), 10);
31
+ }
32
+ describe.skipIf(!ffmpegOk)('Real Video Pipeline Tests', () => {
33
+ let tempDir;
34
+ const fixtureDir = path.join(import.meta.dirname, 'fixtures');
35
+ const videoPath = path.join(fixtureDir, 'sample-speech.mp4');
36
+ const transcriptPath = path.join(fixtureDir, 'sample-speech-transcript.json');
37
+ let transcript;
38
+ beforeAll(async () => {
39
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'vantr-realpipe-'));
40
+ const raw = await fs.readFile(transcriptPath, 'utf-8');
41
+ transcript = JSON.parse(raw);
42
+ }, 30000);
43
+ afterAll(async () => {
44
+ await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
45
+ });
46
+ // ── Audio Extraction ──────────────────────────────────────────────
47
+ describe('Audio Extraction', () => {
48
+ it('extracts MP3 with non-zero size', async () => {
49
+ const out = path.join(tempDir, 'speech-audio.mp3');
50
+ await extractAudio(videoPath, out);
51
+ const { exists, size } = await fileExistsWithSize(out);
52
+ expect(exists).toBe(true);
53
+ expect(size).toBeGreaterThan(0);
54
+ }, 90000);
55
+ it('audio duration matches video duration (±1s)', async () => {
56
+ const out = path.join(tempDir, 'speech-audio-dur.mp3');
57
+ await extractAudio(videoPath, out);
58
+ const videoDur = await getVideoDuration(videoPath);
59
+ const audioDur = await getVideoDuration(out);
60
+ expect(audioDur).toBeGreaterThanOrEqual(videoDur - 1);
61
+ expect(audioDur).toBeLessThanOrEqual(videoDur + 1);
62
+ }, 90000);
63
+ it('audio is mono (1 channel)', async () => {
64
+ const out = path.join(tempDir, 'speech-audio-mono.mp3');
65
+ await extractAudio(videoPath, out);
66
+ const channels = await getAudioChannels(out);
67
+ expect(channels).toBe(1);
68
+ }, 90000);
69
+ });
70
+ // ── Silence Detection ─────────────────────────────────────────────
71
+ describe('Silence Detection', () => {
72
+ let audioPath;
73
+ beforeAll(async () => {
74
+ audioPath = path.join(tempDir, 'silence-detect-audio.mp3');
75
+ await extractAudio(videoPath, audioPath);
76
+ }, 90000);
77
+ it('returns an array of SilenceRegion objects', async () => {
78
+ const regions = await detectSilence(audioPath, 0.3, '-30dB');
79
+ expect(Array.isArray(regions)).toBe(true);
80
+ }, 90000);
81
+ it('each region has valid start < end and duration > 0', async () => {
82
+ const regions = await detectSilence(audioPath, 0.3, '-30dB');
83
+ for (const r of regions) {
84
+ expect(r.start).toBeLessThan(r.end);
85
+ expect(r.duration).toBeGreaterThan(0);
86
+ }
87
+ }, 90000);
88
+ it('regions are within the audio duration range', async () => {
89
+ const duration = await getVideoDuration(audioPath);
90
+ const regions = await detectSilence(audioPath, 0.3, '-30dB');
91
+ for (const r of regions) {
92
+ expect(r.start).toBeGreaterThanOrEqual(0);
93
+ expect(r.end).toBeLessThanOrEqual(duration + 0.5);
94
+ }
95
+ }, 90000);
96
+ it('speech audio has at least some silence between sentences', async () => {
97
+ const regions = await detectSilence(audioPath, 0.3, '-30dB');
98
+ expect(regions.length).toBeGreaterThanOrEqual(1);
99
+ }, 90000);
100
+ it('higher threshold detects more silence than lower threshold', async () => {
101
+ const lowRegions = await detectSilence(audioPath, 0.3, '-40dB');
102
+ const highRegions = await detectSilence(audioPath, 0.3, '-20dB');
103
+ expect(highRegions.length).toBeGreaterThanOrEqual(lowRegions.length);
104
+ }, 90000);
105
+ });
106
+ // ── Clip Extraction ───────────────────────────────────────────────
107
+ describe('Clip Extraction', () => {
108
+ it('extracts a 5-second clip from the middle', async () => {
109
+ const out = path.join(tempDir, 'clip-5s.mp4');
110
+ await extractClip(videoPath, 10, 15, out, 0);
111
+ const { exists, size } = await fileExistsWithSize(out);
112
+ expect(exists).toBe(true);
113
+ expect(size).toBeGreaterThan(0);
114
+ // Stream copy (-c copy) snaps to keyframes, so duration may exceed requested range
115
+ const dur = await getVideoDuration(out);
116
+ expect(dur).toBeGreaterThanOrEqual(4.5);
117
+ expect(dur).toBeLessThanOrEqual(7.0);
118
+ const streams = await getStreamInfo(out);
119
+ expect(streams.hasVideo).toBe(true);
120
+ expect(streams.hasAudio).toBe(true);
121
+ }, 90000);
122
+ });
123
+ // ── Composite Clip ────────────────────────────────────────────────
124
+ describe('Composite Clip Extraction', () => {
125
+ it('extracts 2 non-contiguous segments and concats', async () => {
126
+ const out = path.join(tempDir, 'composite-clip.mp4');
127
+ const segments = [
128
+ { start: 2, end: 6, description: 'First segment' },
129
+ { start: 14, end: 18, description: 'Second segment' },
130
+ ];
131
+ await extractCompositeClip(videoPath, segments, out, 0);
132
+ const { exists, size } = await fileExistsWithSize(out);
133
+ expect(exists).toBe(true);
134
+ expect(size).toBeGreaterThan(0);
135
+ const dur = await getVideoDuration(out);
136
+ const expectedDur = (6 - 2) + (18 - 14); // 8s total
137
+ expect(dur).toBeGreaterThanOrEqual(expectedDur - 1);
138
+ expect(dur).toBeLessThanOrEqual(expectedDur + 1);
139
+ const streams = await getStreamInfo(out);
140
+ expect(streams.hasVideo).toBe(true);
141
+ expect(streams.hasAudio).toBe(true);
142
+ }, 90000);
143
+ });
144
+ // ── Frame Capture ─────────────────────────────────────────────────
145
+ describe('Frame Capture', () => {
146
+ it('captures frames at multiple timestamps as valid PNGs', async () => {
147
+ const timestamps = [1.0, 10.0, 25.0];
148
+ const PNG_HEADER = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
149
+ for (let i = 0; i < timestamps.length; i++) {
150
+ const out = path.join(tempDir, `frame-${i}.png`);
151
+ await captureFrame(videoPath, timestamps[i], out);
152
+ const { exists, size } = await fileExistsWithSize(out);
153
+ expect(exists).toBe(true);
154
+ expect(size).toBeGreaterThan(0);
155
+ const header = Buffer.alloc(4);
156
+ const fh = await fs.open(out, 'r');
157
+ await fh.read(header, 0, 4, 0);
158
+ await fh.close();
159
+ expect(header.subarray(0, 4).equals(PNG_HEADER)).toBe(true);
160
+ }
161
+ }, 90000);
162
+ });
163
+ // ── Single-Pass Edit ──────────────────────────────────────────────
164
+ describe('Single-Pass Edit', () => {
165
+ it('keeps only 2 segments with correct duration', async () => {
166
+ const out = path.join(tempDir, 'singlepass-edit.mp4');
167
+ const segments = [
168
+ { start: 0, end: 5 },
169
+ { start: 15, end: 20 },
170
+ ];
171
+ await singlePassEdit(videoPath, segments, out);
172
+ const { exists, size } = await fileExistsWithSize(out);
173
+ expect(exists).toBe(true);
174
+ expect(size).toBeGreaterThan(0);
175
+ const dur = await getVideoDuration(out);
176
+ const expectedDur = (5 - 0) + (20 - 15); // 10s
177
+ expect(dur).toBeGreaterThanOrEqual(expectedDur - 0.5);
178
+ expect(dur).toBeLessThanOrEqual(expectedDur + 0.5);
179
+ const streams = await getStreamInfo(out);
180
+ expect(streams.hasVideo).toBe(true);
181
+ expect(streams.hasAudio).toBe(true);
182
+ }, 90000);
183
+ });
184
+ // ── Single-Pass Edit with Captions ────────────────────────────────
185
+ describe('Single-Pass Edit with Captions', () => {
186
+ it('trims and burns captions with matching duration', async () => {
187
+ const segments = [
188
+ { start: 0, end: 5 },
189
+ { start: 15, end: 20 },
190
+ ];
191
+ // Generate ASS captions from the real transcript
192
+ const assContent = generateStyledASS(transcript);
193
+ const assPath = path.join(tempDir, 'real-captions.ass');
194
+ await fs.writeFile(assPath, assContent);
195
+ const out = path.join(tempDir, 'singlepass-captioned.mp4');
196
+ await singlePassEditAndCaption(videoPath, segments, assPath, out);
197
+ const { exists, size } = await fileExistsWithSize(out);
198
+ expect(exists).toBe(true);
199
+ expect(size).toBeGreaterThan(0);
200
+ const dur = await getVideoDuration(out);
201
+ const expectedDur = (5 - 0) + (20 - 15); // 10s
202
+ expect(dur).toBeGreaterThanOrEqual(expectedDur - 0.5);
203
+ expect(dur).toBeLessThanOrEqual(expectedDur + 0.5);
204
+ const streams = await getStreamInfo(out);
205
+ expect(streams.hasVideo).toBe(true);
206
+ expect(streams.hasAudio).toBe(true);
207
+ }, 90000);
208
+ });
209
+ });
210
+ //# sourceMappingURL=realPipeline.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realPipeline.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/realPipeline.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=silenceRemoval.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silenceRemoval.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/silenceRemoval.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { execFile } from 'child_process';
3
+ import { promisify } from 'util';
4
+ import path from 'path';
5
+ import { setupFixtures, cleanupFixtures, isFFmpegAvailable, fileExistsWithSize, getVideoDuration, } from './fixture.js';
6
+ import { singlePassEdit } from '../../tools/ffmpeg/singlePassEdit.js';
7
+ import { singlePassEditAndCaption } from '../../tools/ffmpeg/singlePassEdit.js';
8
+ const execFileAsync = promisify(execFile);
9
+ const ffprobePath = process.env.FFPROBE_PATH || 'ffprobe';
10
+ const ffmpegOk = await isFFmpegAvailable();
11
+ async function getStreams(videoPath) {
12
+ const { stdout } = await execFileAsync(ffprobePath, [
13
+ '-v', 'error', '-show_entries', 'stream=codec_type', '-of', 'csv=p=0', videoPath,
14
+ ]);
15
+ const types = stdout.trim().split('\n').map((t) => t.trim());
16
+ return { video: types.includes('video'), audio: types.includes('audio') };
17
+ }
18
+ async function getCodecs(videoPath) {
19
+ const { stdout } = await execFileAsync(ffprobePath, [
20
+ '-v', 'error', '-show_entries', 'stream=codec_name,codec_type', '-of', 'csv=p=0', videoPath,
21
+ ]);
22
+ const lines = stdout.trim().split('\n').map((l) => l.trim());
23
+ let videoCodec = '';
24
+ let audioCodec = '';
25
+ for (const line of lines) {
26
+ const [codec, type] = line.split(',');
27
+ if (type === 'video')
28
+ videoCodec = codec;
29
+ if (type === 'audio')
30
+ audioCodec = codec;
31
+ }
32
+ return { videoCodec, audioCodec };
33
+ }
34
+ describe.skipIf(!ffmpegOk)('Silence Removal Integration', () => {
35
+ let fix;
36
+ const outputs = [];
37
+ beforeAll(async () => {
38
+ fix = await setupFixtures();
39
+ }, 30000);
40
+ afterAll(async () => {
41
+ await cleanupFixtures();
42
+ });
43
+ it('singlePassEdit with keep segments removes middle section', async () => {
44
+ const outputPath = path.join(fix.dir, 'trimmed-skip-middle.mp4');
45
+ outputs.push(outputPath);
46
+ const segments = [
47
+ { start: 0, end: 2 },
48
+ { start: 3, end: 5 },
49
+ ];
50
+ await singlePassEdit(fix.videoPath, segments, outputPath);
51
+ const { exists, size } = await fileExistsWithSize(outputPath);
52
+ expect(exists).toBe(true);
53
+ expect(size).toBeGreaterThan(0);
54
+ const duration = await getVideoDuration(outputPath);
55
+ expect(duration).toBeCloseTo(4, 0);
56
+ }, 120000);
57
+ it('singlePassEdit with single segment keeps full video', async () => {
58
+ const outputPath = path.join(fix.dir, 'trimmed-full.mp4');
59
+ outputs.push(outputPath);
60
+ const segments = [{ start: 0, end: 5 }];
61
+ await singlePassEdit(fix.videoPath, segments, outputPath);
62
+ const { exists, size } = await fileExistsWithSize(outputPath);
63
+ expect(exists).toBe(true);
64
+ expect(size).toBeGreaterThan(0);
65
+ const duration = await getVideoDuration(outputPath);
66
+ expect(duration).toBeCloseTo(5, 0);
67
+ }, 120000);
68
+ it('singlePassEditAndCaption trims and burns captions', async () => {
69
+ const outputPath = path.join(fix.dir, 'trimmed-captioned.mp4');
70
+ outputs.push(outputPath);
71
+ const segments = [
72
+ { start: 0, end: 2.5 },
73
+ { start: 3, end: 5 },
74
+ ];
75
+ await singlePassEditAndCaption(fix.videoPath, segments, fix.assPath, outputPath);
76
+ const { exists, size } = await fileExistsWithSize(outputPath);
77
+ expect(exists).toBe(true);
78
+ expect(size).toBeGreaterThan(0);
79
+ const duration = await getVideoDuration(outputPath);
80
+ expect(duration).toBeCloseTo(4.5, 0);
81
+ }, 120000);
82
+ it('all outputs are valid h264/aac video files', async () => {
83
+ for (const videoPath of outputs) {
84
+ const { video, audio } = await getStreams(videoPath);
85
+ expect(video).toBe(true);
86
+ expect(audio).toBe(true);
87
+ const { videoCodec, audioCodec } = await getCodecs(videoPath);
88
+ expect(videoCodec).toBe('h264');
89
+ expect(audioCodec).toBe('aac');
90
+ }
91
+ }, 120000);
92
+ });
93
+ //# sourceMappingURL=silenceRemoval.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silenceRemoval.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/silenceRemoval.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pipeline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pipeline.test.ts"],"names":[],"mappings":""}