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,406 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ // Use vi.hoisted to ensure mock fns are available before vi.mock factories
3
+ const { mockExecFile, mockMkdir, mockCopyFile, mockDetectWebcam, mockGetVideoResolution } = vi.hoisted(() => ({
4
+ mockExecFile: vi.fn(),
5
+ mockMkdir: vi.fn(),
6
+ mockCopyFile: vi.fn(),
7
+ mockDetectWebcam: vi.fn(),
8
+ mockGetVideoResolution: vi.fn(),
9
+ }));
10
+ vi.mock('child_process', () => ({
11
+ execFile: mockExecFile,
12
+ }));
13
+ vi.mock('fs', async (importOriginal) => {
14
+ const original = await importOriginal();
15
+ return {
16
+ ...original,
17
+ promises: {
18
+ ...original.promises,
19
+ mkdir: mockMkdir,
20
+ copyFile: mockCopyFile,
21
+ },
22
+ };
23
+ });
24
+ // Mock logger
25
+ vi.mock('../config/logger.js', () => ({
26
+ default: {
27
+ info: vi.fn(),
28
+ warn: vi.fn(),
29
+ error: vi.fn(),
30
+ debug: vi.fn(),
31
+ },
32
+ }));
33
+ // Mock face detection — return null (no webcam) so smart portrait falls back to center-crop
34
+ vi.mock('../tools/ffmpeg/faceDetection', () => ({
35
+ detectWebcamRegion: mockDetectWebcam,
36
+ getVideoResolution: mockGetVideoResolution,
37
+ }));
38
+ import { convertAspectRatio, convertToPortraitSmart, convertToSquareSmart, convertToFeedSmart, generatePlatformVariants, PLATFORM_RATIOS, DIMENSIONS, } from '../tools/ffmpeg/aspectRatio.js';
39
+ // ── Helpers ─────────────────────────────────────────────────────────────────
40
+ /** Make execFile succeed by default */
41
+ function execFileSucceeds() {
42
+ mockExecFile.mockImplementation((...args) => {
43
+ const cb = args[args.length - 1];
44
+ if (typeof cb === 'function')
45
+ cb(null, '', '');
46
+ });
47
+ }
48
+ /** Make execFile fail */
49
+ function execFileFails(message = 'ffmpeg error') {
50
+ mockExecFile.mockImplementation((...args) => {
51
+ const cb = args[args.length - 1];
52
+ if (typeof cb === 'function')
53
+ cb(new Error(message), '', message);
54
+ });
55
+ }
56
+ /** Extract the -vf filter argument from execFile calls */
57
+ function getCapturedFilter() {
58
+ const call = mockExecFile.mock.calls[0];
59
+ if (!call)
60
+ return undefined;
61
+ const args = call[1];
62
+ const vfIdx = args.indexOf('-vf');
63
+ return vfIdx >= 0 ? args[vfIdx + 1] : undefined;
64
+ }
65
+ beforeEach(() => {
66
+ mockExecFile.mockReset();
67
+ mockMkdir.mockReset().mockResolvedValue(undefined);
68
+ mockCopyFile.mockReset().mockResolvedValue(undefined);
69
+ mockDetectWebcam.mockReset().mockResolvedValue(null);
70
+ mockGetVideoResolution.mockReset().mockResolvedValue({ width: 1920, height: 1080 });
71
+ execFileSucceeds();
72
+ });
73
+ // ── Filter string generation ────────────────────────────────────────────────
74
+ describe('Crop filter generation (via convertAspectRatio)', () => {
75
+ it('9:16 portrait center-crop produces correct crop filter', async () => {
76
+ await convertAspectRatio('/in.mp4', '/out.mp4', '9:16');
77
+ const filter = getCapturedFilter();
78
+ expect(filter).toContain('crop=ih*9/16:ih');
79
+ expect(filter).toContain('scale=1080:1920');
80
+ });
81
+ it('1:1 square crop produces correct filter', async () => {
82
+ await convertAspectRatio('/in.mp4', '/out.mp4', '1:1');
83
+ const filter = getCapturedFilter();
84
+ expect(filter).toContain('crop=ih:ih');
85
+ expect(filter).toContain('scale=1080:1080');
86
+ });
87
+ it('4:5 Instagram feed produces correct filter', async () => {
88
+ await convertAspectRatio('/in.mp4', '/out.mp4', '4:5');
89
+ const filter = getCapturedFilter();
90
+ expect(filter).toContain('crop=ih*4/5:ih');
91
+ expect(filter).toContain('scale=1080:1350');
92
+ });
93
+ it('letterbox mode produces pad filter instead of crop', async () => {
94
+ await convertAspectRatio('/in.mp4', '/out.mp4', '9:16', { letterbox: true });
95
+ const filter = getCapturedFilter();
96
+ expect(filter).toContain('pad=1080:1920');
97
+ expect(filter).toContain('force_original_aspect_ratio=decrease');
98
+ expect(filter).not.toContain('crop=');
99
+ });
100
+ it('16:9 same-ratio copies file instead of re-encoding', async () => {
101
+ await convertAspectRatio('/in.mp4', '/out.mp4', '16:9');
102
+ expect(mockCopyFile).toHaveBeenCalledWith('/in.mp4', '/out.mp4');
103
+ expect(mockExecFile).not.toHaveBeenCalled();
104
+ });
105
+ it('16:9 with letterbox still encodes (not a copy)', async () => {
106
+ await convertAspectRatio('/in.mp4', '/out.mp4', '16:9', { letterbox: true });
107
+ expect(mockExecFile).toHaveBeenCalled();
108
+ const filter = getCapturedFilter();
109
+ expect(filter).toContain('scale=1920:1080');
110
+ });
111
+ });
112
+ // ── Platform variant generation ─────────────────────────────────────────────
113
+ describe('generatePlatformVariants', () => {
114
+ it('returns expected variants for default platforms', async () => {
115
+ const platforms = ['tiktok', 'youtube-shorts', 'instagram-reels', 'instagram-feed', 'linkedin'];
116
+ const variants = await generatePlatformVariants('/in.mp4', '/out', 'my-short', platforms);
117
+ // 9:16 platforms: tiktok, youtube-shorts, instagram-reels (one encode, 3 entries)
118
+ // 4:5: instagram-feed (1 entry)
119
+ // 1:1: linkedin (1 entry)
120
+ expect(variants).toHaveLength(5);
121
+ const portrait = variants.filter((v) => v.aspectRatio === '9:16');
122
+ expect(portrait).toHaveLength(3);
123
+ expect(portrait.map((v) => v.platform)).toEqual(expect.arrayContaining(['tiktok', 'youtube-shorts', 'instagram-reels']));
124
+ const feed = variants.filter((v) => v.aspectRatio === '4:5');
125
+ expect(feed).toHaveLength(1);
126
+ expect(feed[0].platform).toBe('instagram-feed');
127
+ const square = variants.filter((v) => v.aspectRatio === '1:1');
128
+ expect(square).toHaveLength(1);
129
+ expect(square[0].platform).toBe('linkedin');
130
+ });
131
+ it('each variant has correct dimensions for its aspect ratio', async () => {
132
+ const platforms = ['tiktok', 'instagram-feed', 'linkedin'];
133
+ const variants = await generatePlatformVariants('/in.mp4', '/out', 'clip', platforms);
134
+ for (const v of variants) {
135
+ const expectedDims = DIMENSIONS[v.aspectRatio];
136
+ expect(v.width).toBe(expectedDims.width);
137
+ expect(v.height).toBe(expectedDims.height);
138
+ }
139
+ });
140
+ it('skips 16:9 platforms (original ratio)', async () => {
141
+ const platforms = ['youtube', 'tiktok'];
142
+ const variants = await generatePlatformVariants('/in.mp4', '/out', 'clip', platforms);
143
+ // youtube is 16:9 → skipped; only tiktok (9:16) remains
144
+ expect(variants).toHaveLength(1);
145
+ expect(variants[0].platform).toBe('tiktok');
146
+ });
147
+ it('deduplicates encodes for same aspect ratio', async () => {
148
+ const platforms = ['tiktok', 'youtube-shorts'];
149
+ await generatePlatformVariants('/in.mp4', '/out', 'clip', platforms);
150
+ // Both are 9:16 → only one ffmpeg encode (via convertToPortraitSmart fallback)
151
+ // convertToPortraitSmart calls convertAspectRatio once when no webcam detected
152
+ const ffmpegCalls = mockExecFile.mock.calls.filter((c) => c[0] !== 'ffprobe');
153
+ expect(ffmpegCalls).toHaveLength(1);
154
+ });
155
+ });
156
+ // ── Constants ───────────────────────────────────────────────────────────────
157
+ describe('PLATFORM_RATIOS constant', () => {
158
+ it('maps all platforms to valid aspect ratios', () => {
159
+ const validRatios = ['16:9', '9:16', '1:1', '4:5'];
160
+ for (const [platform, ratio] of Object.entries(PLATFORM_RATIOS)) {
161
+ expect(validRatios).toContain(ratio);
162
+ }
163
+ });
164
+ it('tiktok and youtube-shorts are 9:16', () => {
165
+ expect(PLATFORM_RATIOS['tiktok']).toBe('9:16');
166
+ expect(PLATFORM_RATIOS['youtube-shorts']).toBe('9:16');
167
+ });
168
+ it('instagram-feed is 4:5', () => {
169
+ expect(PLATFORM_RATIOS['instagram-feed']).toBe('4:5');
170
+ });
171
+ });
172
+ describe('DIMENSIONS constant', () => {
173
+ it('has dimensions for all aspect ratios', () => {
174
+ expect(DIMENSIONS['16:9']).toEqual({ width: 1920, height: 1080 });
175
+ expect(DIMENSIONS['9:16']).toEqual({ width: 1080, height: 1920 });
176
+ expect(DIMENSIONS['1:1']).toEqual({ width: 1080, height: 1080 });
177
+ expect(DIMENSIONS['4:5']).toEqual({ width: 1080, height: 1350 });
178
+ });
179
+ });
180
+ // ── Error handling ──────────────────────────────────────────────────────────
181
+ describe('Error handling', () => {
182
+ it('convertAspectRatio rejects when ffmpeg fails', async () => {
183
+ execFileFails('Conversion error');
184
+ await expect(convertAspectRatio('/in.mp4', '/out.mp4', '9:16')).rejects.toThrow('Aspect ratio conversion failed');
185
+ });
186
+ it('generatePlatformVariants skips failed ratios gracefully', async () => {
187
+ execFileFails('encode failed');
188
+ const variants = await generatePlatformVariants('/in.mp4', '/out', 'clip', ['tiktok', 'linkedin']);
189
+ // All fail → empty array, no throw
190
+ expect(variants).toEqual([]);
191
+ });
192
+ });
193
+ // ── Smart portrait: screen crop & face padding ─────────────────────────────
194
+ describe('convertToPortraitSmart – screen crop & face padding', () => {
195
+ /** Extract the -filter_complex argument from execFile calls */
196
+ function getCapturedFilterComplex() {
197
+ const call = mockExecFile.mock.calls[0];
198
+ if (!call)
199
+ return undefined;
200
+ const args = call[1];
201
+ const idx = args.indexOf('-filter_complex');
202
+ return idx >= 0 ? args[idx + 1] : undefined;
203
+ }
204
+ it('screen crop excludes webcam region (bottom-right)', async () => {
205
+ mockDetectWebcam.mockResolvedValue({
206
+ x: 1440, y: 810, width: 480, height: 270,
207
+ position: 'bottom-right', confidence: 0.8,
208
+ });
209
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
210
+ await convertToPortraitSmart('/in.mp4', '/out.mp4');
211
+ const fc = getCapturedFilterComplex();
212
+ expect(fc).toBeDefined();
213
+ // Screen crop uses webcam.x directly as width (no hardcoded margin)
214
+ expect(fc).toContain('crop=1440:ih:0:0');
215
+ });
216
+ it('screen crop excludes webcam region (bottom-left)', async () => {
217
+ mockDetectWebcam.mockResolvedValue({
218
+ x: 0, y: 810, width: 480, height: 270,
219
+ position: 'bottom-left', confidence: 0.8,
220
+ });
221
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
222
+ await convertToPortraitSmart('/in.mp4', '/out.mp4');
223
+ const fc = getCapturedFilterComplex();
224
+ expect(fc).toBeDefined();
225
+ // Screen crop starts after webcam (x + width) with remaining width
226
+ expect(fc).toContain('crop=1440:ih:480:0');
227
+ });
228
+ it('face crop matches target aspect ratio and fills bottom section', async () => {
229
+ const webcamW = 480;
230
+ const webcamH = 270;
231
+ mockDetectWebcam.mockResolvedValue({
232
+ x: 1440, y: 810, width: webcamW, height: webcamH,
233
+ position: 'bottom-right', confidence: 0.8,
234
+ });
235
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
236
+ await convertToPortraitSmart('/in.mp4', '/out.mp4');
237
+ const fc = getCapturedFilterComplex();
238
+ expect(fc).toBeDefined();
239
+ // Target AR = 1080/672 = 1.607; webcam AR = 480/270 = 1.778 (wider)
240
+ // So crop height = 270, width = round(270 * 1.607) = 434
241
+ const targetAR = 1080 / 672;
242
+ const expectedW = Math.round(webcamH * targetAR); // 434
243
+ expect(fc).toContain(`crop=${expectedW}:${webcamH}`);
244
+ // Should scale directly to 1080:672 with no padding
245
+ expect(fc).toContain('scale=1080:672[cam]');
246
+ expect(fc).not.toContain('force_original_aspect_ratio=increase');
247
+ });
248
+ it('screen crop uses detected webcam.x directly with no hardcoded margin', async () => {
249
+ mockDetectWebcam.mockResolvedValue({
250
+ x: 1728, y: 810, width: 192, height: 270,
251
+ position: 'bottom-right', confidence: 0.9,
252
+ });
253
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
254
+ await convertToPortraitSmart('/in.mp4', '/out.mp4');
255
+ const fc = getCapturedFilterComplex();
256
+ expect(fc).toBeDefined();
257
+ // With precise bbox detection, crop width = webcam.x = 1728
258
+ expect(fc).toContain('crop=1728:ih:0:0');
259
+ });
260
+ it('screen crop handles webcam at far edge gracefully', async () => {
261
+ mockDetectWebcam.mockResolvedValue({
262
+ x: 20, y: 810, width: 480, height: 270,
263
+ position: 'bottom-right', confidence: 0.8,
264
+ });
265
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
266
+ await convertToPortraitSmart('/in.mp4', '/out.mp4');
267
+ const fc = getCapturedFilterComplex();
268
+ expect(fc).toBeDefined();
269
+ // webcam.x = 20, so screen crop is only 20px wide
270
+ expect(fc).toContain('crop=20:ih:0:0');
271
+ });
272
+ });
273
+ // ── Smart square: screen crop & face padding ────────────────────────────────
274
+ describe('convertToSquareSmart – screen crop & face padding', () => {
275
+ function getCapturedFilterComplex() {
276
+ const call = mockExecFile.mock.calls[0];
277
+ if (!call)
278
+ return undefined;
279
+ const args = call[1];
280
+ const idx = args.indexOf('-filter_complex');
281
+ return idx >= 0 ? args[idx + 1] : undefined;
282
+ }
283
+ it('falls back to center-crop when no webcam detected', async () => {
284
+ mockDetectWebcam.mockResolvedValue(null);
285
+ await convertToSquareSmart('/in.mp4', '/out.mp4');
286
+ const filter = getCapturedFilter();
287
+ expect(filter).toContain('crop=ih:ih');
288
+ expect(filter).toContain('scale=1080:1080');
289
+ });
290
+ it('screen crop excludes webcam region (bottom-right)', async () => {
291
+ mockDetectWebcam.mockResolvedValue({
292
+ x: 1440, y: 810, width: 480, height: 270,
293
+ position: 'bottom-right', confidence: 0.8,
294
+ });
295
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
296
+ await convertToSquareSmart('/in.mp4', '/out.mp4');
297
+ const fc = getCapturedFilterComplex();
298
+ expect(fc).toBeDefined();
299
+ expect(fc).toContain('crop=1440:ih:0:0');
300
+ });
301
+ it('face crop matches target aspect ratio and fills bottom section', async () => {
302
+ const webcamW = 480;
303
+ const webcamH = 270;
304
+ mockDetectWebcam.mockResolvedValue({
305
+ x: 1440, y: 810, width: webcamW, height: webcamH,
306
+ position: 'bottom-right', confidence: 0.8,
307
+ });
308
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
309
+ await convertToSquareSmart('/in.mp4', '/out.mp4');
310
+ const fc = getCapturedFilterComplex();
311
+ expect(fc).toBeDefined();
312
+ // Target AR = 1080/380 = 2.842; webcam AR = 480/270 = 1.778 (narrower)
313
+ // Keep full width, center-crop height: faceH = round(480 / (1080/380)) = 169
314
+ const targetAR = 1080 / 380;
315
+ const expectedH = Math.round(webcamW / targetAR);
316
+ expect(fc).toContain(`crop=${webcamW}:${expectedH}`);
317
+ // Should scale directly to 1080:380 with no padding
318
+ expect(fc).toContain('scale=1080:380[cam]');
319
+ });
320
+ it('uses split-screen layout 700+380=1080', async () => {
321
+ mockDetectWebcam.mockResolvedValue({
322
+ x: 1440, y: 810, width: 480, height: 270,
323
+ position: 'bottom-right', confidence: 0.8,
324
+ });
325
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
326
+ await convertToSquareSmart('/in.mp4', '/out.mp4');
327
+ const fc = getCapturedFilterComplex();
328
+ expect(fc).toBeDefined();
329
+ expect(fc).toContain('scale=1080:700');
330
+ expect(fc).toContain('scale=1080:380[cam]');
331
+ expect(fc).toContain('vstack');
332
+ });
333
+ });
334
+ // ── Smart feed: screen crop & face padding ──────────────────────────────────
335
+ describe('convertToFeedSmart – screen crop & face padding', () => {
336
+ function getCapturedFilterComplex() {
337
+ const call = mockExecFile.mock.calls[0];
338
+ if (!call)
339
+ return undefined;
340
+ const args = call[1];
341
+ const idx = args.indexOf('-filter_complex');
342
+ return idx >= 0 ? args[idx + 1] : undefined;
343
+ }
344
+ it('falls back to center-crop when no webcam detected', async () => {
345
+ mockDetectWebcam.mockResolvedValue(null);
346
+ await convertToFeedSmart('/in.mp4', '/out.mp4');
347
+ const filter = getCapturedFilter();
348
+ expect(filter).toContain('crop=ih*4/5:ih');
349
+ expect(filter).toContain('scale=1080:1350');
350
+ });
351
+ it('screen crop excludes webcam region (bottom-right)', async () => {
352
+ mockDetectWebcam.mockResolvedValue({
353
+ x: 1440, y: 810, width: 480, height: 270,
354
+ position: 'bottom-right', confidence: 0.8,
355
+ });
356
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
357
+ await convertToFeedSmart('/in.mp4', '/out.mp4');
358
+ const fc = getCapturedFilterComplex();
359
+ expect(fc).toBeDefined();
360
+ expect(fc).toContain('crop=1440:ih:0:0');
361
+ });
362
+ it('face crop matches target aspect ratio and fills bottom section', async () => {
363
+ const webcamW = 480;
364
+ const webcamH = 270;
365
+ mockDetectWebcam.mockResolvedValue({
366
+ x: 1440, y: 810, width: webcamW, height: webcamH,
367
+ position: 'bottom-right', confidence: 0.8,
368
+ });
369
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
370
+ await convertToFeedSmart('/in.mp4', '/out.mp4');
371
+ const fc = getCapturedFilterComplex();
372
+ expect(fc).toBeDefined();
373
+ // Target AR = 1080/472 = 2.288; webcam AR = 480/270 = 1.778 (narrower)
374
+ // Keep full width, center-crop height: faceH = round(480 / (1080/472)) = 210
375
+ const targetAR = 1080 / 472;
376
+ const expectedH = Math.round(webcamW / targetAR);
377
+ expect(fc).toContain(`crop=${webcamW}:${expectedH}`);
378
+ // Should scale directly to 1080:472 with no padding
379
+ expect(fc).toContain('scale=1080:472[cam]');
380
+ });
381
+ it('uses split-screen layout 878+472=1350', async () => {
382
+ mockDetectWebcam.mockResolvedValue({
383
+ x: 1440, y: 810, width: 480, height: 270,
384
+ position: 'bottom-right', confidence: 0.8,
385
+ });
386
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
387
+ await convertToFeedSmart('/in.mp4', '/out.mp4');
388
+ const fc = getCapturedFilterComplex();
389
+ expect(fc).toBeDefined();
390
+ expect(fc).toContain('scale=1080:878');
391
+ expect(fc).toContain('scale=1080:472[cam]');
392
+ expect(fc).toContain('vstack');
393
+ });
394
+ it('screen crop excludes webcam region (bottom-left)', async () => {
395
+ mockDetectWebcam.mockResolvedValue({
396
+ x: 0, y: 810, width: 480, height: 270,
397
+ position: 'bottom-left', confidence: 0.8,
398
+ });
399
+ mockGetVideoResolution.mockResolvedValue({ width: 1920, height: 1080 });
400
+ await convertToFeedSmart('/in.mp4', '/out.mp4');
401
+ const fc = getCapturedFilterComplex();
402
+ expect(fc).toBeDefined();
403
+ expect(fc).toContain('crop=1440:ih:480:0');
404
+ });
405
+ });
406
+ //# sourceMappingURL=aspectRatio.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspectRatio.test.js","sourceRoot":"","sources":["../../src/__tests__/aspectRatio.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=captionGenerator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captionGenerator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/captionGenerator.test.ts"],"names":[],"mappings":""}