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.
- package/README.md +243 -0
- package/assets/fonts/Montserrat-Bold.ttf +0 -0
- package/assets/fonts/Montserrat-Regular.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/dist/__tests__/agents.test.d.ts +2 -0
- package/dist/__tests__/agents.test.d.ts.map +1 -0
- package/dist/__tests__/agents.test.js +434 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/aspectRatio.test.d.ts +2 -0
- package/dist/__tests__/aspectRatio.test.d.ts.map +1 -0
- package/dist/__tests__/aspectRatio.test.js +406 -0
- package/dist/__tests__/aspectRatio.test.js.map +1 -0
- package/dist/__tests__/captionGenerator.test.d.ts +2 -0
- package/dist/__tests__/captionGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/captionGenerator.test.js +435 -0
- package/dist/__tests__/captionGenerator.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +81 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/faceDetection.test.d.ts +2 -0
- package/dist/__tests__/faceDetection.test.d.ts.map +1 -0
- package/dist/__tests__/faceDetection.test.js +372 -0
- package/dist/__tests__/faceDetection.test.js.map +1 -0
- package/dist/__tests__/ffmpegTools.test.d.ts +2 -0
- package/dist/__tests__/ffmpegTools.test.d.ts.map +1 -0
- package/dist/__tests__/ffmpegTools.test.js +464 -0
- package/dist/__tests__/ffmpegTools.test.js.map +1 -0
- package/dist/__tests__/integration/captionBurn.test.d.ts +2 -0
- package/dist/__tests__/integration/captionBurn.test.d.ts.map +1 -0
- package/dist/__tests__/integration/captionBurn.test.js +103 -0
- package/dist/__tests__/integration/captionBurn.test.js.map +1 -0
- package/dist/__tests__/integration/clipComposite.test.d.ts +2 -0
- package/dist/__tests__/integration/clipComposite.test.d.ts.map +1 -0
- package/dist/__tests__/integration/clipComposite.test.js +56 -0
- package/dist/__tests__/integration/clipComposite.test.js.map +1 -0
- package/dist/__tests__/integration/faceDetection.test.d.ts +2 -0
- package/dist/__tests__/integration/faceDetection.test.d.ts.map +1 -0
- package/dist/__tests__/integration/faceDetection.test.js +85 -0
- package/dist/__tests__/integration/faceDetection.test.js.map +1 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.d.ts +2 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.d.ts.map +1 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.js +88 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.js.map +1 -0
- package/dist/__tests__/integration/fixture.d.ts +19 -0
- package/dist/__tests__/integration/fixture.d.ts.map +1 -0
- package/dist/__tests__/integration/fixture.js +112 -0
- package/dist/__tests__/integration/fixture.js.map +1 -0
- package/dist/__tests__/integration/fixture.test.d.ts +2 -0
- package/dist/__tests__/integration/fixture.test.d.ts.map +1 -0
- package/dist/__tests__/integration/fixture.test.js +27 -0
- package/dist/__tests__/integration/fixture.test.js.map +1 -0
- package/dist/__tests__/integration/realCaptions.test.d.ts +2 -0
- package/dist/__tests__/integration/realCaptions.test.d.ts.map +1 -0
- package/dist/__tests__/integration/realCaptions.test.js +226 -0
- package/dist/__tests__/integration/realCaptions.test.js.map +1 -0
- package/dist/__tests__/integration/realPipeline.test.d.ts +2 -0
- package/dist/__tests__/integration/realPipeline.test.d.ts.map +1 -0
- package/dist/__tests__/integration/realPipeline.test.js +210 -0
- package/dist/__tests__/integration/realPipeline.test.js.map +1 -0
- package/dist/__tests__/integration/silenceRemoval.test.d.ts +2 -0
- package/dist/__tests__/integration/silenceRemoval.test.d.ts.map +1 -0
- package/dist/__tests__/integration/silenceRemoval.test.js +93 -0
- package/dist/__tests__/integration/silenceRemoval.test.js.map +1 -0
- package/dist/__tests__/pipeline.test.d.ts +2 -0
- package/dist/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline.test.js +434 -0
- package/dist/__tests__/pipeline.test.js.map +1 -0
- package/dist/__tests__/services.test.d.ts +2 -0
- package/dist/__tests__/services.test.d.ts.map +1 -0
- package/dist/__tests__/services.test.js +655 -0
- package/dist/__tests__/services.test.js.map +1 -0
- package/dist/__tests__/silenceRemoval.test.d.ts +2 -0
- package/dist/__tests__/silenceRemoval.test.d.ts.map +1 -0
- package/dist/__tests__/silenceRemoval.test.js +266 -0
- package/dist/__tests__/silenceRemoval.test.js.map +1 -0
- package/dist/__tests__/singlePassEdit.test.d.ts +2 -0
- package/dist/__tests__/singlePassEdit.test.d.ts.map +1 -0
- package/dist/__tests__/singlePassEdit.test.js +321 -0
- package/dist/__tests__/singlePassEdit.test.js.map +1 -0
- package/dist/__tests__/smoke.test.d.ts +2 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +8 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/__tests__/utilities.test.d.ts +2 -0
- package/dist/__tests__/utilities.test.d.ts.map +1 -0
- package/dist/__tests__/utilities.test.js +268 -0
- package/dist/__tests__/utilities.test.js.map +1 -0
- package/dist/agents/BaseAgent.d.ts +52 -0
- package/dist/agents/BaseAgent.d.ts.map +1 -0
- package/dist/agents/BaseAgent.js +108 -0
- package/dist/agents/BaseAgent.js.map +1 -0
- package/dist/agents/BlogAgent.d.ts +3 -0
- package/dist/agents/BlogAgent.d.ts.map +1 -0
- package/dist/agents/BlogAgent.js +163 -0
- package/dist/agents/BlogAgent.js.map +1 -0
- package/dist/agents/ChapterAgent.d.ts +11 -0
- package/dist/agents/ChapterAgent.d.ts.map +1 -0
- package/dist/agents/ChapterAgent.js +191 -0
- package/dist/agents/ChapterAgent.js.map +1 -0
- package/dist/agents/MediumVideoAgent.d.ts +3 -0
- package/dist/agents/MediumVideoAgent.d.ts.map +1 -0
- package/dist/agents/MediumVideoAgent.js +219 -0
- package/dist/agents/MediumVideoAgent.js.map +1 -0
- package/dist/agents/ShortsAgent.d.ts +3 -0
- package/dist/agents/ShortsAgent.d.ts.map +1 -0
- package/dist/agents/ShortsAgent.js +243 -0
- package/dist/agents/ShortsAgent.js.map +1 -0
- package/dist/agents/SilenceRemovalAgent.d.ts +9 -0
- package/dist/agents/SilenceRemovalAgent.d.ts.map +1 -0
- package/dist/agents/SilenceRemovalAgent.js +208 -0
- package/dist/agents/SilenceRemovalAgent.js.map +1 -0
- package/dist/agents/SocialMediaAgent.d.ts +4 -0
- package/dist/agents/SocialMediaAgent.d.ts.map +1 -0
- package/dist/agents/SocialMediaAgent.js +248 -0
- package/dist/agents/SocialMediaAgent.js.map +1 -0
- package/dist/agents/SummaryAgent.d.ts +11 -0
- package/dist/agents/SummaryAgent.d.ts.map +1 -0
- package/dist/agents/SummaryAgent.js +333 -0
- package/dist/agents/SummaryAgent.js.map +1 -0
- package/dist/config/brand.d.ts +29 -0
- package/dist/config/brand.d.ts.map +1 -0
- package/dist/config/brand.js +83 -0
- package/dist/config/brand.js.map +1 -0
- package/dist/config/environment.d.ts +36 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +44 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/logger.d.ts +5 -0
- package/dist/config/logger.d.ts.map +1 -0
- package/dist/config/logger.js +13 -0
- package/dist/config/logger.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline.d.ts +57 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +287 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/services/captionGeneration.d.ts +7 -0
- package/dist/services/captionGeneration.d.ts.map +1 -0
- package/dist/services/captionGeneration.js +29 -0
- package/dist/services/captionGeneration.js.map +1 -0
- package/dist/services/fileWatcher.d.ts +19 -0
- package/dist/services/fileWatcher.d.ts.map +1 -0
- package/dist/services/fileWatcher.js +120 -0
- package/dist/services/fileWatcher.js.map +1 -0
- package/dist/services/gitOperations.d.ts +3 -0
- package/dist/services/gitOperations.d.ts.map +1 -0
- package/dist/services/gitOperations.js +43 -0
- package/dist/services/gitOperations.js.map +1 -0
- package/dist/services/socialPosting.d.ts +38 -0
- package/dist/services/socialPosting.d.ts.map +1 -0
- package/dist/services/socialPosting.js +102 -0
- package/dist/services/socialPosting.js.map +1 -0
- package/dist/services/transcription.d.ts +3 -0
- package/dist/services/transcription.d.ts.map +1 -0
- package/dist/services/transcription.js +100 -0
- package/dist/services/transcription.js.map +1 -0
- package/dist/services/videoIngestion.d.ts +3 -0
- package/dist/services/videoIngestion.d.ts.map +1 -0
- package/dist/services/videoIngestion.js +103 -0
- package/dist/services/videoIngestion.js.map +1 -0
- package/dist/tools/captions/captionGenerator.d.ts +84 -0
- package/dist/tools/captions/captionGenerator.d.ts.map +1 -0
- package/dist/tools/captions/captionGenerator.js +390 -0
- package/dist/tools/captions/captionGenerator.js.map +1 -0
- package/dist/tools/ffmpeg/aspectRatio.d.ts +101 -0
- package/dist/tools/ffmpeg/aspectRatio.d.ts.map +1 -0
- package/dist/tools/ffmpeg/aspectRatio.js +338 -0
- package/dist/tools/ffmpeg/aspectRatio.js.map +1 -0
- package/dist/tools/ffmpeg/audioExtraction.d.ts +16 -0
- package/dist/tools/ffmpeg/audioExtraction.d.ts.map +1 -0
- package/dist/tools/ffmpeg/audioExtraction.js +86 -0
- package/dist/tools/ffmpeg/audioExtraction.js.map +1 -0
- package/dist/tools/ffmpeg/captionBurning.d.ts +8 -0
- package/dist/tools/ffmpeg/captionBurning.d.ts.map +1 -0
- package/dist/tools/ffmpeg/captionBurning.js +71 -0
- package/dist/tools/ffmpeg/captionBurning.js.map +1 -0
- package/dist/tools/ffmpeg/clipExtraction.d.ts +23 -0
- package/dist/tools/ffmpeg/clipExtraction.d.ts.map +1 -0
- package/dist/tools/ffmpeg/clipExtraction.js +178 -0
- package/dist/tools/ffmpeg/clipExtraction.js.map +1 -0
- package/dist/tools/ffmpeg/faceDetection.d.ts +127 -0
- package/dist/tools/ffmpeg/faceDetection.d.ts.map +1 -0
- package/dist/tools/ffmpeg/faceDetection.js +500 -0
- package/dist/tools/ffmpeg/faceDetection.js.map +1 -0
- package/dist/tools/ffmpeg/frameCapture.d.ts +10 -0
- package/dist/tools/ffmpeg/frameCapture.d.ts.map +1 -0
- package/dist/tools/ffmpeg/frameCapture.js +48 -0
- package/dist/tools/ffmpeg/frameCapture.js.map +1 -0
- package/dist/tools/ffmpeg/silenceDetection.d.ts +10 -0
- package/dist/tools/ffmpeg/silenceDetection.d.ts.map +1 -0
- package/dist/tools/ffmpeg/silenceDetection.js +55 -0
- package/dist/tools/ffmpeg/silenceDetection.js.map +1 -0
- package/dist/tools/ffmpeg/singlePassEdit.d.ts +25 -0
- package/dist/tools/ffmpeg/singlePassEdit.d.ts.map +1 -0
- package/dist/tools/ffmpeg/singlePassEdit.js +123 -0
- package/dist/tools/ffmpeg/singlePassEdit.js.map +1 -0
- package/dist/tools/search/exaClient.d.ts +8 -0
- package/dist/tools/search/exaClient.d.ts.map +1 -0
- package/dist/tools/search/exaClient.js +38 -0
- package/dist/tools/search/exaClient.js.map +1 -0
- package/dist/tools/whisper/whisperClient.d.ts +3 -0
- package/dist/tools/whisper/whisperClient.d.ts.map +1 -0
- package/dist/tools/whisper/whisperClient.js +77 -0
- package/dist/tools/whisper/whisperClient.js.map +1 -0
- package/dist/types/index.d.ts +305 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +44 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caption generator for the Advanced SubStation Alpha (ASS) subtitle format.
|
|
3
|
+
*
|
|
4
|
+
* ### Why ASS instead of SRT/VTT?
|
|
5
|
+
* ASS supports inline style overrides — font size, color, and animation per
|
|
6
|
+
* character/word — which enables the "active word pop" karaoke effect used
|
|
7
|
+
* in modern short-form video (TikTok, Reels). SRT and VTT only support
|
|
8
|
+
* plain text or basic HTML tags with no per-word timing control.
|
|
9
|
+
*
|
|
10
|
+
* ### Karaoke word highlighting approach
|
|
11
|
+
* Instead of ASS's native `\k` karaoke tags (which highlight left-to-right
|
|
12
|
+
* within a line), we generate **one Dialogue line per word-state**. Each line
|
|
13
|
+
* renders the entire caption group but with the currently-spoken word in a
|
|
14
|
+
* different color and size. Contiguous end/start timestamps between
|
|
15
|
+
* word-states prevent flicker. This gives us full control over the visual
|
|
16
|
+
* treatment (color, font-size, scale animations) without the limitations
|
|
17
|
+
* of the `\k` tag.
|
|
18
|
+
*
|
|
19
|
+
* @module captionGenerator
|
|
20
|
+
*/
|
|
21
|
+
import { Transcript, CaptionStyle } from '../../types';
|
|
22
|
+
export declare function generateSRT(transcript: Transcript): string;
|
|
23
|
+
export declare function generateVTT(transcript: Transcript): string;
|
|
24
|
+
/**
|
|
25
|
+
* Generate premium ASS captions with active-word-pop highlighting.
|
|
26
|
+
*
|
|
27
|
+
* ### How it works
|
|
28
|
+
* Words are grouped by speech bursts (split on silence gaps > 0.8s or after
|
|
29
|
+
* 8 words). Within each group, one Dialogue line is emitted per word — the
|
|
30
|
+
* full group is shown each time, but the "active" word gets a different color
|
|
31
|
+
* and larger font size. This creates a karaoke-style bounce effect.
|
|
32
|
+
*
|
|
33
|
+
* @param transcript - Full transcript with word-level timestamps
|
|
34
|
+
* @param style - Visual style: 'shorts' (large centered), 'medium' (small bottom),
|
|
35
|
+
* or 'portrait' (Opus Clips style with green highlight + scale animation)
|
|
36
|
+
* @returns Complete ASS file content (header + dialogue lines)
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateStyledASS(transcript: Transcript, style?: CaptionStyle): string;
|
|
39
|
+
/**
|
|
40
|
+
* Generate premium ASS captions for a single contiguous segment.
|
|
41
|
+
* Filters words within [startTime, endTime] (plus buffer), adjusts timestamps
|
|
42
|
+
* relative to the clip's buffered start so they align with the extracted video.
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateStyledASSForSegment(transcript: Transcript, startTime: number, endTime: number, buffer?: number, style?: CaptionStyle): string;
|
|
45
|
+
/**
|
|
46
|
+
* Generate premium ASS captions for a composite clip made of multiple segments.
|
|
47
|
+
* Each segment's words are extracted and remapped to the concatenated timeline,
|
|
48
|
+
* accounting for the buffer added during clip extraction.
|
|
49
|
+
*/
|
|
50
|
+
export declare function generateStyledASSForComposite(transcript: Transcript, segments: {
|
|
51
|
+
start: number;
|
|
52
|
+
end: number;
|
|
53
|
+
}[], buffer?: number, style?: CaptionStyle): string;
|
|
54
|
+
/**
|
|
55
|
+
* Generate ASS dialogue lines for a hook text overlay at the top of the video.
|
|
56
|
+
*
|
|
57
|
+
* The hook is a short attention-grabbing phrase (e.g. "Here's why you should
|
|
58
|
+
* learn TypeScript") displayed as a translucent pill/badge at the top of a
|
|
59
|
+
* portrait video for the first few seconds.
|
|
60
|
+
*
|
|
61
|
+
* Uses the `Hook` style defined in {@link ASS_HEADER_PORTRAIT} which has
|
|
62
|
+
* `BorderStyle: 3` (opaque box background) and `Alignment: 8` (top-center).
|
|
63
|
+
*
|
|
64
|
+
* The `\fad(300,500)` tag creates a 300ms fade-in and 500ms fade-out so the
|
|
65
|
+
* hook doesn't appear/disappear abruptly.
|
|
66
|
+
*
|
|
67
|
+
* @param hookText - The attention-grabbing phrase (truncated to 60 chars)
|
|
68
|
+
* @param displayDuration - How long to show the hook in seconds (default: 4s)
|
|
69
|
+
* @param _style - Caption style (currently only 'portrait' uses hooks)
|
|
70
|
+
* @returns A single ASS Dialogue line to append to the Events section
|
|
71
|
+
*/
|
|
72
|
+
export declare function generateHookOverlay(hookText: string, displayDuration?: number, _style?: CaptionStyle): string;
|
|
73
|
+
/**
|
|
74
|
+
* Generate a complete portrait ASS file with captions AND hook text overlay.
|
|
75
|
+
*/
|
|
76
|
+
export declare function generatePortraitASSWithHook(transcript: Transcript, hookText: string, startTime: number, endTime: number, buffer?: number): string;
|
|
77
|
+
/**
|
|
78
|
+
* Generate a complete portrait ASS file for a composite clip with captions AND hook text overlay.
|
|
79
|
+
*/
|
|
80
|
+
export declare function generatePortraitASSWithHookComposite(transcript: Transcript, segments: {
|
|
81
|
+
start: number;
|
|
82
|
+
end: number;
|
|
83
|
+
}[], hookText: string, buffer?: number): string;
|
|
84
|
+
//# sourceMappingURL=captionGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captionGenerator.d.ts","sourceRoot":"","sources":["../../../src/tools/captions/captionGenerator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,UAAU,EAAiB,YAAY,EAAE,MAAM,aAAa,CAAA;AA+ErE,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAW1D;AAMD,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAW1D;AA6LD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,GAAE,YAAuB,GAAG,MAAM,CAMhG;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAY,EACpB,KAAK,GAAE,YAAuB,GAC7B,MAAM,CAiBR;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,EAC1C,MAAM,GAAE,MAAY,EACpB,KAAK,GAAE,YAAuB,GAC7B,MAAM,CA4BR;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,eAAe,GAAE,MAAY,EAC7B,MAAM,GAAE,YAAyB,GAChC,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,EAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAIR"}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caption generator for the Advanced SubStation Alpha (ASS) subtitle format.
|
|
3
|
+
*
|
|
4
|
+
* ### Why ASS instead of SRT/VTT?
|
|
5
|
+
* ASS supports inline style overrides — font size, color, and animation per
|
|
6
|
+
* character/word — which enables the "active word pop" karaoke effect used
|
|
7
|
+
* in modern short-form video (TikTok, Reels). SRT and VTT only support
|
|
8
|
+
* plain text or basic HTML tags with no per-word timing control.
|
|
9
|
+
*
|
|
10
|
+
* ### Karaoke word highlighting approach
|
|
11
|
+
* Instead of ASS's native `\k` karaoke tags (which highlight left-to-right
|
|
12
|
+
* within a line), we generate **one Dialogue line per word-state**. Each line
|
|
13
|
+
* renders the entire caption group but with the currently-spoken word in a
|
|
14
|
+
* different color and size. Contiguous end/start timestamps between
|
|
15
|
+
* word-states prevent flicker. This gives us full control over the visual
|
|
16
|
+
* treatment (color, font-size, scale animations) without the limitations
|
|
17
|
+
* of the `\k` tag.
|
|
18
|
+
*
|
|
19
|
+
* @module captionGenerator
|
|
20
|
+
*/
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Helpers
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/** Pad a number to a fixed width with leading zeros. */
|
|
25
|
+
function pad(n, width) {
|
|
26
|
+
return String(n).padStart(width, '0');
|
|
27
|
+
}
|
|
28
|
+
/** Convert seconds → SRT timestamp "HH:MM:SS,mmm" */
|
|
29
|
+
function toSRT(seconds) {
|
|
30
|
+
const h = Math.floor(seconds / 3600);
|
|
31
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
32
|
+
const s = Math.floor(seconds % 60);
|
|
33
|
+
const ms = Math.round((seconds - Math.floor(seconds)) * 1000);
|
|
34
|
+
return `${pad(h, 2)}:${pad(m, 2)}:${pad(s, 2)},${pad(ms, 3)}`;
|
|
35
|
+
}
|
|
36
|
+
/** Convert seconds → VTT timestamp "HH:MM:SS.mmm" */
|
|
37
|
+
function toVTT(seconds) {
|
|
38
|
+
return toSRT(seconds).replace(',', '.');
|
|
39
|
+
}
|
|
40
|
+
/** Convert seconds → ASS timestamp "H:MM:SS.cc" */
|
|
41
|
+
function toASS(seconds) {
|
|
42
|
+
const h = Math.floor(seconds / 3600);
|
|
43
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
44
|
+
const s = Math.floor(seconds % 60);
|
|
45
|
+
const cs = Math.round((seconds - Math.floor(seconds)) * 100);
|
|
46
|
+
return `${h}:${pad(m, 2)}:${pad(s, 2)}.${pad(cs, 2)}`;
|
|
47
|
+
}
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Premium caption constants
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
/** Silence gap threshold in seconds – gaps longer than this split caption groups. */
|
|
52
|
+
const SILENCE_GAP_THRESHOLD = 0.8;
|
|
53
|
+
/** Maximum words displayed simultaneously in a caption group. */
|
|
54
|
+
const MAX_WORDS_PER_GROUP = 8;
|
|
55
|
+
/** Target words per display line within a group (splits into 2 lines above this). */
|
|
56
|
+
const WORDS_PER_LINE = 4;
|
|
57
|
+
/** ASS BGR color for the active (currently-spoken) word – yellow. */
|
|
58
|
+
const ACTIVE_COLOR = '\\c&H00FFFF&';
|
|
59
|
+
/** ASS BGR color for inactive words – white. */
|
|
60
|
+
const BASE_COLOR = '\\c&HFFFFFF&';
|
|
61
|
+
/** Font size for the active word. */
|
|
62
|
+
const ACTIVE_FONT_SIZE = 54;
|
|
63
|
+
/** Font size for inactive words (matches style default). */
|
|
64
|
+
const BASE_FONT_SIZE = 42;
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Medium caption constants (smaller, bottom-positioned for longer content)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
/** Font size for the active word in medium style. */
|
|
69
|
+
const MEDIUM_ACTIVE_FONT_SIZE = 40;
|
|
70
|
+
/** Font size for inactive words in medium style. */
|
|
71
|
+
const MEDIUM_BASE_FONT_SIZE = 32;
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Portrait caption constants (Opus Clips style)
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
/** Font size for the active word in portrait style. */
|
|
76
|
+
const PORTRAIT_ACTIVE_FONT_SIZE = 78;
|
|
77
|
+
/** Font size for inactive words in portrait style. */
|
|
78
|
+
const PORTRAIT_BASE_FONT_SIZE = 66;
|
|
79
|
+
/** ASS BGR color for the active word in portrait style – green. */
|
|
80
|
+
const PORTRAIT_ACTIVE_COLOR = '\\c&H00FF00&';
|
|
81
|
+
/** ASS BGR color for inactive words in portrait style – white. */
|
|
82
|
+
const PORTRAIT_BASE_COLOR = '\\c&HFFFFFF&';
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// SRT (segment-level)
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
export function generateSRT(transcript) {
|
|
87
|
+
return transcript.segments
|
|
88
|
+
.map((seg, i) => {
|
|
89
|
+
const idx = i + 1;
|
|
90
|
+
const start = toSRT(seg.start);
|
|
91
|
+
const end = toSRT(seg.end);
|
|
92
|
+
const text = seg.text.trim();
|
|
93
|
+
return `${idx}\n${start} --> ${end}\n${text}`;
|
|
94
|
+
})
|
|
95
|
+
.join('\n\n')
|
|
96
|
+
.concat('\n');
|
|
97
|
+
}
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// VTT (segment-level)
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
export function generateVTT(transcript) {
|
|
102
|
+
const cues = transcript.segments
|
|
103
|
+
.map((seg) => {
|
|
104
|
+
const start = toVTT(seg.start);
|
|
105
|
+
const end = toVTT(seg.end);
|
|
106
|
+
const text = seg.text.trim();
|
|
107
|
+
return `${start} --> ${end}\n${text}`;
|
|
108
|
+
})
|
|
109
|
+
.join('\n\n');
|
|
110
|
+
return `WEBVTT\n\n${cues}\n`;
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// ASS – Premium active-word-pop captions
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
/**
|
|
116
|
+
* ASS header for landscape (16:9, 1920×1080) captions.
|
|
117
|
+
*
|
|
118
|
+
* ### Style fields explained (comma-separated in the Style line):
|
|
119
|
+
* - `Fontname: Montserrat` — bundled with the project; FFmpeg's `ass` filter
|
|
120
|
+
* uses `fontsdir=.` so libass finds the .ttf files next to the .ass file.
|
|
121
|
+
* - `Fontsize: 42` — base size for inactive words
|
|
122
|
+
* - `PrimaryColour: &H00FFFFFF` — white (ASS uses `&HAABBGGRR` — alpha, blue, green, red)
|
|
123
|
+
* - `OutlineColour: &H00000000` — black outline for readability on any background
|
|
124
|
+
* - `BackColour: &H80000000` — 50% transparent black shadow
|
|
125
|
+
* - `Bold: 1` — bold for better readability at small sizes
|
|
126
|
+
* - `BorderStyle: 1` — outline + drop shadow (not opaque box)
|
|
127
|
+
* - `Outline: 3` — 3px outline thickness
|
|
128
|
+
* - `Shadow: 1` — 1px drop shadow
|
|
129
|
+
* - `Alignment: 2` — bottom-center (SSA alignment: 1=left, 2=center, 3=right;
|
|
130
|
+
* add 4 for top, 8 for middle — so 2 = bottom-center)
|
|
131
|
+
* - `MarginV: 40` — 40px above the bottom edge
|
|
132
|
+
* - `WrapStyle: 0` — smart word wrap
|
|
133
|
+
*/
|
|
134
|
+
const ASS_HEADER = `[Script Info]
|
|
135
|
+
Title: Auto-generated captions
|
|
136
|
+
ScriptType: v4.00+
|
|
137
|
+
PlayResX: 1920
|
|
138
|
+
PlayResY: 1080
|
|
139
|
+
WrapStyle: 0
|
|
140
|
+
|
|
141
|
+
[V4+ Styles]
|
|
142
|
+
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
|
143
|
+
Style: Default,Montserrat,42,&H00FFFFFF,&H0000FFFF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,3,1,2,20,20,40,1
|
|
144
|
+
|
|
145
|
+
[Events]
|
|
146
|
+
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|
147
|
+
`;
|
|
148
|
+
/**
|
|
149
|
+
* ASS header for portrait (9:16, 1080×1920) captions — used for shorts.
|
|
150
|
+
*
|
|
151
|
+
* Key differences from the landscape header:
|
|
152
|
+
* - `PlayResX/Y: 1080×1920` — matches portrait video dimensions
|
|
153
|
+
* - `Fontsize: 78` — larger base font for vertical video viewing (small screens)
|
|
154
|
+
* - `MarginV: 700` — pushes captions to the center-bottom area, leaving room
|
|
155
|
+
* for the hook overlay at the top
|
|
156
|
+
* - Includes a `Hook` style: semi-transparent pill/badge background
|
|
157
|
+
* (`BorderStyle: 3` = opaque box) for the opening hook text overlay
|
|
158
|
+
*/
|
|
159
|
+
const ASS_HEADER_PORTRAIT = `[Script Info]
|
|
160
|
+
Title: Auto-generated captions
|
|
161
|
+
ScriptType: v4.00+
|
|
162
|
+
PlayResX: 1080
|
|
163
|
+
PlayResY: 1920
|
|
164
|
+
WrapStyle: 0
|
|
165
|
+
|
|
166
|
+
[V4+ Styles]
|
|
167
|
+
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
|
168
|
+
Style: Default,Montserrat,78,&H00FFFFFF,&H0000FFFF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,3,1,2,30,30,700,1
|
|
169
|
+
Style: Hook,Montserrat,56,&H00333333,&H00333333,&H60D0D0D0,&H60E0E0E0,1,0,0,0,100,100,2,0,3,18,2,8,80,80,60,1
|
|
170
|
+
|
|
171
|
+
[Events]
|
|
172
|
+
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|
173
|
+
`;
|
|
174
|
+
/**
|
|
175
|
+
* ASS header for medium-style captions (1920×1080 but smaller font).
|
|
176
|
+
*
|
|
177
|
+
* Used for longer clips where large captions would be distracting.
|
|
178
|
+
* - `Fontsize: 32` — smaller than the shorts style
|
|
179
|
+
* - `Alignment: 2` — bottom-center
|
|
180
|
+
* - `MarginV: 60` — slightly higher from the bottom edge to avoid UI overlaps
|
|
181
|
+
*/
|
|
182
|
+
const ASS_HEADER_MEDIUM = `[Script Info]
|
|
183
|
+
Title: Auto-generated captions
|
|
184
|
+
ScriptType: v4.00+
|
|
185
|
+
PlayResX: 1920
|
|
186
|
+
PlayResY: 1080
|
|
187
|
+
WrapStyle: 0
|
|
188
|
+
|
|
189
|
+
[V4+ Styles]
|
|
190
|
+
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
|
191
|
+
Style: Default,Montserrat,32,&H00FFFFFF,&H0000FFFF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,2,1,2,20,20,60,1
|
|
192
|
+
|
|
193
|
+
[Events]
|
|
194
|
+
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|
195
|
+
`;
|
|
196
|
+
/**
|
|
197
|
+
* Group words into caption groups split on silence gaps and max word count.
|
|
198
|
+
* All words within a group are displayed simultaneously; captions disappear
|
|
199
|
+
* entirely during gaps longer than SILENCE_GAP_THRESHOLD.
|
|
200
|
+
*/
|
|
201
|
+
function groupWordsBySpeech(words) {
|
|
202
|
+
if (words.length === 0)
|
|
203
|
+
return [];
|
|
204
|
+
const groups = [];
|
|
205
|
+
let current = [];
|
|
206
|
+
for (let i = 0; i < words.length; i++) {
|
|
207
|
+
current.push(words[i]);
|
|
208
|
+
const isLast = i === words.length - 1;
|
|
209
|
+
const hasGap = !isLast && words[i + 1].start - words[i].end > SILENCE_GAP_THRESHOLD;
|
|
210
|
+
const atMax = current.length >= MAX_WORDS_PER_GROUP;
|
|
211
|
+
if (isLast || hasGap || atMax) {
|
|
212
|
+
groups.push(current);
|
|
213
|
+
current = [];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return groups;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Split a caption group into 1–2 display lines.
|
|
220
|
+
* Groups with ≤ WORDS_PER_LINE words stay on one line; larger groups
|
|
221
|
+
* are split at the midpoint into two lines joined with \\N.
|
|
222
|
+
*/
|
|
223
|
+
function splitGroupIntoLines(group) {
|
|
224
|
+
if (group.length <= WORDS_PER_LINE)
|
|
225
|
+
return [group];
|
|
226
|
+
const mid = Math.ceil(group.length / 2);
|
|
227
|
+
return [group.slice(0, mid), group.slice(mid)];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Build premium ASS dialogue lines with active-word highlighting.
|
|
231
|
+
* Generates one Dialogue line per word-state: the full caption group is
|
|
232
|
+
* rendered with the currently-spoken word in yellow at a larger size while
|
|
233
|
+
* all other words stay white at the base size. Contiguous end/start times
|
|
234
|
+
* between word-states prevent flicker.
|
|
235
|
+
*/
|
|
236
|
+
function buildPremiumDialogueLines(words, style = 'shorts') {
|
|
237
|
+
const activeFontSize = style === 'portrait' ? PORTRAIT_ACTIVE_FONT_SIZE
|
|
238
|
+
: style === 'medium' ? MEDIUM_ACTIVE_FONT_SIZE : ACTIVE_FONT_SIZE;
|
|
239
|
+
const baseFontSize = style === 'portrait' ? PORTRAIT_BASE_FONT_SIZE
|
|
240
|
+
: style === 'medium' ? MEDIUM_BASE_FONT_SIZE : BASE_FONT_SIZE;
|
|
241
|
+
const groups = groupWordsBySpeech(words);
|
|
242
|
+
const dialogues = [];
|
|
243
|
+
for (const group of groups) {
|
|
244
|
+
const displayLines = splitGroupIntoLines(group);
|
|
245
|
+
for (let activeIdx = 0; activeIdx < group.length; activeIdx++) {
|
|
246
|
+
const activeWord = group[activeIdx];
|
|
247
|
+
// Contiguous timing: end = next word's start, or this word's own end
|
|
248
|
+
const endTime = activeIdx < group.length - 1
|
|
249
|
+
? group[activeIdx + 1].start
|
|
250
|
+
: activeWord.end;
|
|
251
|
+
// Render all words across display lines with the active word highlighted
|
|
252
|
+
const renderedLines = [];
|
|
253
|
+
let globalIdx = 0;
|
|
254
|
+
for (const line of displayLines) {
|
|
255
|
+
const rendered = line.map((w) => {
|
|
256
|
+
const idx = globalIdx++;
|
|
257
|
+
const text = w.word.trim();
|
|
258
|
+
if (idx === activeIdx) {
|
|
259
|
+
if (style === 'portrait') {
|
|
260
|
+
// Opus Clips style: green color + scale pop animation
|
|
261
|
+
return `{${PORTRAIT_ACTIVE_COLOR}\\fs${activeFontSize}\\fscx130\\fscy130\\t(0,150,\\fscx100\\fscy100)}${text}`;
|
|
262
|
+
}
|
|
263
|
+
return `{${ACTIVE_COLOR}\\fs${activeFontSize}}${text}`;
|
|
264
|
+
}
|
|
265
|
+
if (style === 'portrait') {
|
|
266
|
+
return `{${PORTRAIT_BASE_COLOR}\\fs${baseFontSize}}${text}`;
|
|
267
|
+
}
|
|
268
|
+
return `{${BASE_COLOR}\\fs${baseFontSize}}${text}`;
|
|
269
|
+
});
|
|
270
|
+
renderedLines.push(rendered.join(' '));
|
|
271
|
+
}
|
|
272
|
+
const text = renderedLines.join('\\N');
|
|
273
|
+
dialogues.push(`Dialogue: 0,${toASS(activeWord.start)},${toASS(endTime)},Default,,0,0,0,,${text}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return dialogues;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Generate premium ASS captions with active-word-pop highlighting.
|
|
280
|
+
*
|
|
281
|
+
* ### How it works
|
|
282
|
+
* Words are grouped by speech bursts (split on silence gaps > 0.8s or after
|
|
283
|
+
* 8 words). Within each group, one Dialogue line is emitted per word — the
|
|
284
|
+
* full group is shown each time, but the "active" word gets a different color
|
|
285
|
+
* and larger font size. This creates a karaoke-style bounce effect.
|
|
286
|
+
*
|
|
287
|
+
* @param transcript - Full transcript with word-level timestamps
|
|
288
|
+
* @param style - Visual style: 'shorts' (large centered), 'medium' (small bottom),
|
|
289
|
+
* or 'portrait' (Opus Clips style with green highlight + scale animation)
|
|
290
|
+
* @returns Complete ASS file content (header + dialogue lines)
|
|
291
|
+
*/
|
|
292
|
+
export function generateStyledASS(transcript, style = 'shorts') {
|
|
293
|
+
const header = style === 'portrait' ? ASS_HEADER_PORTRAIT : style === 'medium' ? ASS_HEADER_MEDIUM : ASS_HEADER;
|
|
294
|
+
const allWords = transcript.words;
|
|
295
|
+
if (allWords.length === 0)
|
|
296
|
+
return header;
|
|
297
|
+
return header + buildPremiumDialogueLines(allWords, style).join('\n') + '\n';
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Generate premium ASS captions for a single contiguous segment.
|
|
301
|
+
* Filters words within [startTime, endTime] (plus buffer), adjusts timestamps
|
|
302
|
+
* relative to the clip's buffered start so they align with the extracted video.
|
|
303
|
+
*/
|
|
304
|
+
export function generateStyledASSForSegment(transcript, startTime, endTime, buffer = 1.0, style = 'shorts') {
|
|
305
|
+
const header = style === 'portrait' ? ASS_HEADER_PORTRAIT : style === 'medium' ? ASS_HEADER_MEDIUM : ASS_HEADER;
|
|
306
|
+
const bufferedStart = Math.max(0, startTime - buffer);
|
|
307
|
+
const bufferedEnd = endTime + buffer;
|
|
308
|
+
const words = transcript.words.filter((w) => w.start >= bufferedStart && w.end <= bufferedEnd);
|
|
309
|
+
if (words.length === 0)
|
|
310
|
+
return header;
|
|
311
|
+
const adjusted = words.map((w) => ({
|
|
312
|
+
word: w.word,
|
|
313
|
+
start: w.start - bufferedStart,
|
|
314
|
+
end: w.end - bufferedStart,
|
|
315
|
+
}));
|
|
316
|
+
return header + buildPremiumDialogueLines(adjusted, style).join('\n') + '\n';
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Generate premium ASS captions for a composite clip made of multiple segments.
|
|
320
|
+
* Each segment's words are extracted and remapped to the concatenated timeline,
|
|
321
|
+
* accounting for the buffer added during clip extraction.
|
|
322
|
+
*/
|
|
323
|
+
export function generateStyledASSForComposite(transcript, segments, buffer = 1.0, style = 'shorts') {
|
|
324
|
+
const header = style === 'portrait' ? ASS_HEADER_PORTRAIT : style === 'medium' ? ASS_HEADER_MEDIUM : ASS_HEADER;
|
|
325
|
+
const allAdjusted = [];
|
|
326
|
+
let runningOffset = 0;
|
|
327
|
+
for (const seg of segments) {
|
|
328
|
+
const bufferedStart = Math.max(0, seg.start - buffer);
|
|
329
|
+
const bufferedEnd = seg.end + buffer;
|
|
330
|
+
const segDuration = bufferedEnd - bufferedStart;
|
|
331
|
+
const words = transcript.words.filter((w) => w.start >= bufferedStart && w.end <= bufferedEnd);
|
|
332
|
+
for (const w of words) {
|
|
333
|
+
allAdjusted.push({
|
|
334
|
+
word: w.word,
|
|
335
|
+
start: w.start - bufferedStart + runningOffset,
|
|
336
|
+
end: w.end - bufferedStart + runningOffset,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
runningOffset += segDuration;
|
|
340
|
+
}
|
|
341
|
+
if (allAdjusted.length === 0)
|
|
342
|
+
return header;
|
|
343
|
+
return header + buildPremiumDialogueLines(allAdjusted, style).join('\n') + '\n';
|
|
344
|
+
}
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
// Hook text overlay for portrait shorts
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
/** Maximum characters for hook text before truncation. */
|
|
349
|
+
const HOOK_TEXT_MAX_LENGTH = 60;
|
|
350
|
+
/**
|
|
351
|
+
* Generate ASS dialogue lines for a hook text overlay at the top of the video.
|
|
352
|
+
*
|
|
353
|
+
* The hook is a short attention-grabbing phrase (e.g. "Here's why you should
|
|
354
|
+
* learn TypeScript") displayed as a translucent pill/badge at the top of a
|
|
355
|
+
* portrait video for the first few seconds.
|
|
356
|
+
*
|
|
357
|
+
* Uses the `Hook` style defined in {@link ASS_HEADER_PORTRAIT} which has
|
|
358
|
+
* `BorderStyle: 3` (opaque box background) and `Alignment: 8` (top-center).
|
|
359
|
+
*
|
|
360
|
+
* The `\fad(300,500)` tag creates a 300ms fade-in and 500ms fade-out so the
|
|
361
|
+
* hook doesn't appear/disappear abruptly.
|
|
362
|
+
*
|
|
363
|
+
* @param hookText - The attention-grabbing phrase (truncated to 60 chars)
|
|
364
|
+
* @param displayDuration - How long to show the hook in seconds (default: 4s)
|
|
365
|
+
* @param _style - Caption style (currently only 'portrait' uses hooks)
|
|
366
|
+
* @returns A single ASS Dialogue line to append to the Events section
|
|
367
|
+
*/
|
|
368
|
+
export function generateHookOverlay(hookText, displayDuration = 4.0, _style = 'portrait') {
|
|
369
|
+
const text = hookText.length > HOOK_TEXT_MAX_LENGTH
|
|
370
|
+
? hookText.slice(0, HOOK_TEXT_MAX_LENGTH - 3) + '...'
|
|
371
|
+
: hookText;
|
|
372
|
+
return `Dialogue: 1,${toASS(0)},${toASS(displayDuration)},Hook,,0,0,0,,{\\fad(300,500)}${text}`;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Generate a complete portrait ASS file with captions AND hook text overlay.
|
|
376
|
+
*/
|
|
377
|
+
export function generatePortraitASSWithHook(transcript, hookText, startTime, endTime, buffer) {
|
|
378
|
+
const baseASS = generateStyledASSForSegment(transcript, startTime, endTime, buffer, 'portrait');
|
|
379
|
+
const hookLine = generateHookOverlay(hookText, 4.0, 'portrait');
|
|
380
|
+
return baseASS + hookLine + '\n';
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Generate a complete portrait ASS file for a composite clip with captions AND hook text overlay.
|
|
384
|
+
*/
|
|
385
|
+
export function generatePortraitASSWithHookComposite(transcript, segments, hookText, buffer) {
|
|
386
|
+
const baseASS = generateStyledASSForComposite(transcript, segments, buffer, 'portrait');
|
|
387
|
+
const hookLine = generateHookOverlay(hookText, 4.0, 'portrait');
|
|
388
|
+
return baseASS + hookLine + '\n';
|
|
389
|
+
}
|
|
390
|
+
//# sourceMappingURL=captionGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captionGenerator.js","sourceRoot":"","sources":["../../../src/tools/captions/captionGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported output aspect ratios.
|
|
3
|
+
* - `16:9` — standard landscape (YouTube, desktop)
|
|
4
|
+
* - `9:16` — portrait / vertical (TikTok, Reels, Shorts)
|
|
5
|
+
* - `1:1` — square (LinkedIn, Twitter)
|
|
6
|
+
* - `4:5` — tall feed (Instagram feed)
|
|
7
|
+
*/
|
|
8
|
+
export type AspectRatio = '16:9' | '9:16' | '1:1' | '4:5';
|
|
9
|
+
/** Social-media platforms we generate video variants for. */
|
|
10
|
+
export type Platform = 'tiktok' | 'youtube-shorts' | 'instagram-reels' | 'instagram-feed' | 'linkedin' | 'youtube' | 'twitter';
|
|
11
|
+
/**
|
|
12
|
+
* Maps each platform to its preferred aspect ratio.
|
|
13
|
+
* Multiple platforms may share a ratio (e.g. TikTok + Reels both use 9:16),
|
|
14
|
+
* which lets {@link generatePlatformVariants} deduplicate encodes.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PLATFORM_RATIOS: Record<Platform, AspectRatio>;
|
|
17
|
+
/**
|
|
18
|
+
* Canonical pixel dimensions for each aspect ratio.
|
|
19
|
+
* Width is always 1080 px for non-landscape ratios (the standard vertical
|
|
20
|
+
* video width); landscape stays at 1920×1080 for full HD.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DIMENSIONS: Record<AspectRatio, {
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}>;
|
|
26
|
+
export interface ConvertOptions {
|
|
27
|
+
/** Fallback to letterbox/pillarbox instead of cropping (default: false) */
|
|
28
|
+
letterbox?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert a video's aspect ratio using FFmpeg center-crop.
|
|
32
|
+
*
|
|
33
|
+
* - 16:9 → 9:16: crops the center column to portrait
|
|
34
|
+
* - 16:9 → 1:1: crops to a center square
|
|
35
|
+
* - Same ratio: stream-copies without re-encoding
|
|
36
|
+
*
|
|
37
|
+
* @returns The output path on success
|
|
38
|
+
*/
|
|
39
|
+
export declare function convertAspectRatio(inputPath: string, outputPath: string, targetRatio: AspectRatio, options?: ConvertOptions): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Smart portrait (9:16) conversion → 1080×1920.
|
|
42
|
+
*
|
|
43
|
+
* Screen panel: 1080×1248 (65%), Webcam panel: 1080×672 (35%).
|
|
44
|
+
* Total: 1080×1920 — standard TikTok / Reels / Shorts dimensions.
|
|
45
|
+
*
|
|
46
|
+
* Falls back to center-crop 9:16 if no webcam is detected.
|
|
47
|
+
*
|
|
48
|
+
* @param inputPath - Source landscape video
|
|
49
|
+
* @param outputPath - Destination path for the portrait video
|
|
50
|
+
*/
|
|
51
|
+
export declare function convertToPortraitSmart(inputPath: string, outputPath: string): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Smart square (1:1) conversion → 1080×1080.
|
|
54
|
+
*
|
|
55
|
+
* Screen panel: 1080×700 (65%), Webcam panel: 1080×380 (35%).
|
|
56
|
+
* Total: 1080×1080 — standard LinkedIn / Twitter square format.
|
|
57
|
+
*
|
|
58
|
+
* Falls back to center-crop 1:1 if no webcam is detected.
|
|
59
|
+
*
|
|
60
|
+
* @param inputPath - Source landscape video
|
|
61
|
+
* @param outputPath - Destination path for the square video
|
|
62
|
+
*/
|
|
63
|
+
export declare function convertToSquareSmart(inputPath: string, outputPath: string): Promise<string>;
|
|
64
|
+
/**
|
|
65
|
+
* Smart feed (4:5) conversion → 1080×1350.
|
|
66
|
+
*
|
|
67
|
+
* Screen panel: 1080×878 (65%), Webcam panel: 1080×472 (35%).
|
|
68
|
+
* Total: 1080×1350 — Instagram feed's preferred tall format.
|
|
69
|
+
*
|
|
70
|
+
* Falls back to center-crop 4:5 if no webcam is detected.
|
|
71
|
+
*
|
|
72
|
+
* @param inputPath - Source landscape video
|
|
73
|
+
* @param outputPath - Destination path for the 4:5 video
|
|
74
|
+
*/
|
|
75
|
+
export declare function convertToFeedSmart(inputPath: string, outputPath: string): Promise<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Generate platform-specific aspect-ratio variants of a short clip.
|
|
78
|
+
*
|
|
79
|
+
* ### Routing logic
|
|
80
|
+
* 1. Maps each requested platform to its aspect ratio via {@link PLATFORM_RATIOS}.
|
|
81
|
+
* 2. **Deduplicates by ratio** — if TikTok and Reels both need 9:16, only one
|
|
82
|
+
* encode is performed and both platforms reference the same output file.
|
|
83
|
+
* 3. Skips 16:9 entirely since the source is already landscape.
|
|
84
|
+
* 4. Routes each ratio to its smart converter (portrait / square / feed) for
|
|
85
|
+
* split-screen layout, falling back to {@link convertAspectRatio} for any
|
|
86
|
+
* ratio without a smart converter.
|
|
87
|
+
*
|
|
88
|
+
* @param inputPath - Source video (16:9 landscape)
|
|
89
|
+
* @param outputDir - Directory to write variant files into
|
|
90
|
+
* @param slug - Base filename slug (e.g. "my-video-short-1")
|
|
91
|
+
* @param platforms - Platforms to generate for (default: tiktok + linkedin)
|
|
92
|
+
* @returns Array of variant metadata (one entry per platform, deduplicated files)
|
|
93
|
+
*/
|
|
94
|
+
export declare function generatePlatformVariants(inputPath: string, outputDir: string, slug: string, platforms?: Platform[]): Promise<{
|
|
95
|
+
platform: Platform;
|
|
96
|
+
aspectRatio: AspectRatio;
|
|
97
|
+
path: string;
|
|
98
|
+
width: number;
|
|
99
|
+
height: number;
|
|
100
|
+
}[]>;
|
|
101
|
+
//# sourceMappingURL=aspectRatio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aspectRatio.d.ts","sourceRoot":"","sources":["../../../src/tools/ffmpeg/aspectRatio.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAA;AAEzD,6DAA6D;AAC7D,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,UAAU,GACV,SAAS,GACT,SAAS,CAAA;AAEb;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAQzD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAK7E,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAiDD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAuCjB;AAiJD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,QAAQ,EAA2B,GAC7C,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAwC1G"}
|