@spark-apps/quickpeek 1.1.1 → 1.1.3
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/dist/index.d.mts +308 -0
- package/dist/index.mjs +1857 -0
- package/dist/qp.js +636 -364
- package/dist/voices-BtYZey4Y.d.mts +287 -0
- package/dist/web.d.mts +102 -0
- package/dist/web.mjs +1025 -0
- package/package.json +13 -1
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { U as UserTier, C as Config } from './voices-BtYZey4Y.mjs';
|
|
2
|
+
export { A as AIError, a as AIResponse, b as AIResult, c as CONFIG_FILE, d as CaptionStyle, e as CaptionWordStyle, D as DEFAULT_CAPTIONS, f as DEFAULT_CONFIG, E as EXCLUDED_LINK_PATTERNS, g as ElementInfo, h as ElementType, F as FEATURES, H as HighlightMode, I as INTERACTIVE_SELECTORS, L as LANGUAGE_VOICES, P as Plan, R as RateLimitInfo, S as SparkStatus, i as SparkSubscription, j as SparkTrial, k as Step, l as SuggestedAction, T as Tier, V as VERSION, m as buildSystemPrompt, n as buildUserPrompt, o as callAI, p as crawlPage, q as getErrorMessage, r as getLanguageName, s as getStatus, t as getStatusCached, u as getTierByEmail, v as hasAccess, w as hasTrialRemaining, x as hexToASS, y as isCanceling, z as isExcludedLink, B as isPaid, G as isVerified, J as normalizeUrl, K as parseAIPlanResponse, M as parsePartial, N as pricingUrl, O as rgbaToHex, Q as shouldSkipLink, W as showPaywall, X as toTitleCase, Y as windowsPathToWSL } from './voices-BtYZey4Y.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Billing utilities — watermark decisions and upgrade URL generation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if watermark should be applied
|
|
10
|
+
* Watermark applies to free tier videos longer than 20 seconds
|
|
11
|
+
*/
|
|
12
|
+
declare function shouldWatermark(tier: UserTier, videoDurationSeconds: number): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Get upgrade URL for pricing page
|
|
15
|
+
*/
|
|
16
|
+
declare function getUpgradeUrl(userEmail?: string): string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Cursor PNG as base64 data URI for browser injection
|
|
20
|
+
* Source: src/imgs/cursor.png
|
|
21
|
+
*/
|
|
22
|
+
declare const CURSOR_PNG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAEsBAMAAAB01OGNAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAtUExURUdwTAAAAAAAAAAAAAAAAAAAAAAAAB0dHVZWVtvb2/X19f///6mpqXV1df////vm1ewAAAAGdFJOUwAltTfpcLO208EAAAABYktHRA5vvTBPAAAAB3RJTUUH6QwFFREKplBWTAAABqdJREFUeNq93c+OFFUUx/EeCHud6H7A4HpCOu6NiWsWZh5Ax8YFbAdNWBPxBcCo+yGjD+CAW1ywh0Rexu6uulX3Vp269/c7f7o3Bpjhk6+H6TrU0LdXR6sDPI4/OgCyvn0I5NsDpKw3B0hZbw6Qst4cIGWLxKdskfiUHRKeskPCU/ZIdMoeiU7pkOCUDglO6ZHYlB6JTUlIaEpCQlMGJDJlQCJTRiQwZUQCUzIkLiVD4lJyJCwlR8JSCiQqpUCiUkokKKVEglImSEzKBIlJmSIhKT3yY2hKjzx7GpnSI7+8iExJyN+RKQl5FZkyIJEpAxKZMiKBKSMSmJIhcSkZEpeSI2EpORKWUiBRKQUSlVIiQSklEpQyQWJSJkhMyhQJSZkiISkzJCJlhkSkzJGAlDkSkCIg/ikC4p8iIe4pEuKeIiLeKSLinSIjziky4pyygPimLCC+KUuIa8oS4pqyiHimLCKeKcuIY8oy4phSQfxSKohfSg1xS6khbilVxCulinil1BGnlDrilNJAfFIaiE9KC3FJaSEuKU3EI6WJeKS0EYeUNuKQAiD2FACxpyCIOQVBzCkQYk2BEGsKhhhTMMSYAiK2FBCxpaCIKQVFTCkwYkmBEUsKjhhScMSQQiD6FALRpzCIOoVB1CkUok2hEG0KhyhTOESZQiK6FBLRpbCIKoVFVCk0okmhEU0KjyhSeESRokD4FAXCp2gQOkWD0CkqhE1RIWyKDiFTdAiZokS4FCXCpWgRKkWLUClqhElRI0yKHiFS9AiRYkDwFAOCp1gQOMWCwCkmBE0xIWiKDQFTbAiYYkSwFCOCpVgRKMWKQClmBEkxI0iKHQFS7AiQ4oC0UxyQdooH0kzxQJopLkgrxQVppfggjRQfpJHihNRTnJB6ihdSTfFCqiluSC3FDaml+CGVFD+kkuKILKc4IsspnshiiieymOKKLKW4Iq8u5RRf5FpO8UUWpuKMyCnOiJzijYgp3oiY4o5IKe6IlEIhL38FHr9fzFK47/0On195PL6YpVDIX4CRP1IKhbxGSvLHbQUy/snhUrg/XW9IpE/hkOcs0qVwyD8s0qWQ349nJ9+lcAg9+S6FfFqhJ79PIZFLGtmlkMifPLJNYf/5wgWvfAYg11f5j951H39+B3/cBZA/3uY/+nccJ/5oItdPi19Lkz91RV5snuQ/fN0jJ57I9svvkTT5rzyR7RX7wW/C5L93RPbPI8Xknysmj7zWQZz8fTeke0J8aJ088vqTR1czl5t8Fel/w2Ly12/6oXghaRn8IEz+nJh8DRkuUc/yn02XYGLy0Ou0ysn3X44+/75rvNb+IE3+OxdkXM/Lr3l+8stIvjSIkz91QMaQzeYn2+QXkWL7KSafnogd/jlcHiJPHn8iXkLKNe7BW9Pk26/73T/+y3/tkp38AjLdR4vJp+XrxIhMQuRL8Jc2ZLZYF5OnL8Gt18ff6f9bTJ5dvkQku6vw6Ub4AHb5ap1ZcPS5MHn2Eiwhxe2Rs8rk0Utw8xyJ/gNMy5eAlPd5bkiTJ5ev5tkeN7+uTP6+FpncsEqTt6zd7fNWpMmTy9cMmd15+0SYPLl8tc/AuVWZPLh8TZH5LURx8unOFzb59rlEafKG5WuCSPdC0+SvhMljyxdwVtSx9DVPTb5ExPvTafIfpMmf8oh4pz1NXr92F8jCtz+kyVPLF3Km2jfdz+mXrxxZCLFPHjnnTpw8s3xlyFLIMHn18gWdPVibPLJ8jchiyPBB5eTf4ZOHzoNMky+eiInlCzrZMk1eWruR5Qs6o/NImjxxCcZOGz2rTB64BGPnpkqTJ5Yv7ARY4/LVIz9XQ4bJK5cv7FRe49oNni/cT165fIEnJdfW7vYTMXjm8w3pax6ePHh6tW35As/hltfui8ZniUglvDb55vKFno0urd3wJRg95b26dp8SSO3/rTh5dPlCT94XJ48uX/B7CKTJa5Yv+N0QLJOH39fBsnavwRBx7X75vv/kExBpftn2k38oGOAraYBnuS+6DxwvwaPRmvwaDZmt3ZkBvlwHuFBPJp8breVrjYZM1u7SaEx+jYaUk58Y58gLj5i/lO0mPzE2d4GZYPetxrWbNDoEu68wrN2ssUfAG3Bp7X7CGnsE/R7F2UZ6tI0dwt5Dpo3dJ8LfbDlWGivm3dpuKY0V875z6RLMGtw76J3pDO69ANc6Y3WPCBn+wkUaq48JY3giJg3ukU8+ysgnH2ek5SvUGCYfaaTJhxr95GONbvLRxm7y4cb2iTjeWN08gLFaaYz/AWAIeJtzdhugAAAAAElFTkSuQmCC";
|
|
23
|
+
|
|
24
|
+
interface StepTransition {
|
|
25
|
+
timestamp: number;
|
|
26
|
+
type: 'fade' | 'blend';
|
|
27
|
+
}
|
|
28
|
+
/** Step sync data for time-stretching video segments to match audio */
|
|
29
|
+
interface StepSync {
|
|
30
|
+
actualStart: number;
|
|
31
|
+
actualEnd: number;
|
|
32
|
+
targetDuration: number;
|
|
33
|
+
}
|
|
34
|
+
interface ComposeOptions {
|
|
35
|
+
videoPath: string;
|
|
36
|
+
assPath: string;
|
|
37
|
+
outputPath: string;
|
|
38
|
+
voiceoverPath?: string | undefined;
|
|
39
|
+
musicPath?: string | undefined;
|
|
40
|
+
musicVolume?: number | undefined;
|
|
41
|
+
musicFadeIn?: number | undefined;
|
|
42
|
+
musicFadeOut?: number | undefined;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
fps: number;
|
|
46
|
+
preset: string;
|
|
47
|
+
quality: number;
|
|
48
|
+
fadeIn: number;
|
|
49
|
+
fadeOut: number;
|
|
50
|
+
videoDuration: number;
|
|
51
|
+
audioBitrate?: string | undefined;
|
|
52
|
+
contrast?: number | undefined;
|
|
53
|
+
trimStart?: number | undefined;
|
|
54
|
+
userTier?: UserTier | undefined;
|
|
55
|
+
mainContentDuration?: number | undefined;
|
|
56
|
+
stepSync?: StepSync[] | undefined;
|
|
57
|
+
stepTransitions?: StepTransition[] | undefined;
|
|
58
|
+
}
|
|
59
|
+
interface ConcatVideoOptions {
|
|
60
|
+
inputPath: string;
|
|
61
|
+
outroPath: string;
|
|
62
|
+
outputPath: string;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
preset: string;
|
|
66
|
+
}
|
|
67
|
+
interface VideoOverlay {
|
|
68
|
+
path: string;
|
|
69
|
+
startTime: number;
|
|
70
|
+
duration: number;
|
|
71
|
+
mode: 'stretch' | 'center';
|
|
72
|
+
trimStart?: number;
|
|
73
|
+
trimEnd?: number;
|
|
74
|
+
}
|
|
75
|
+
interface SpliceVideosOptions {
|
|
76
|
+
inputPath: string;
|
|
77
|
+
outputPath: string;
|
|
78
|
+
overlays: VideoOverlay[];
|
|
79
|
+
width: number;
|
|
80
|
+
height: number;
|
|
81
|
+
preset: string;
|
|
82
|
+
}
|
|
83
|
+
interface SpeedUpOptions {
|
|
84
|
+
inputPath: string;
|
|
85
|
+
outputPath: string;
|
|
86
|
+
factor: number;
|
|
87
|
+
preset: string;
|
|
88
|
+
}
|
|
89
|
+
interface BlendTransition {
|
|
90
|
+
timestamp: number;
|
|
91
|
+
type: 'fade' | 'blend';
|
|
92
|
+
duration?: number;
|
|
93
|
+
}
|
|
94
|
+
interface ApplyTransitionsOptions {
|
|
95
|
+
inputVideo: string;
|
|
96
|
+
outputVideo: string;
|
|
97
|
+
transitions: BlendTransition[];
|
|
98
|
+
videoDuration: number;
|
|
99
|
+
preset?: string;
|
|
100
|
+
}
|
|
101
|
+
/** A time segment with start and end positions in seconds */
|
|
102
|
+
interface TimeSegment {
|
|
103
|
+
start: number;
|
|
104
|
+
end: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Compose final video with subtitles, voiceover, and music
|
|
109
|
+
*/
|
|
110
|
+
declare function composeVideo(opts: ComposeOptions): Promise<void>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build segments to keep around action timestamps
|
|
114
|
+
* Merges overlapping windows automatically
|
|
115
|
+
*/
|
|
116
|
+
declare function buildKeepSegments(eventTimestampsSeconds: number[], totalDurationSeconds: number, preWindow?: number, postWindow?: number): TimeSegment[];
|
|
117
|
+
/**
|
|
118
|
+
* Map original event timestamps to their positions in the compressed video
|
|
119
|
+
*/
|
|
120
|
+
declare function mapTimestampsAfterCompression(originalTimestampsSeconds: number[], segments: TimeSegment[]): number[];
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Run ffprobe to get media duration in milliseconds
|
|
124
|
+
*/
|
|
125
|
+
declare function getMediaDuration(filePath: string): Promise<number>;
|
|
126
|
+
/**
|
|
127
|
+
* Concatenate audio files using ffmpeg concat demuxer
|
|
128
|
+
*/
|
|
129
|
+
declare function concatMedia(listPath: string, output: string): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Generate silence audio file of specified duration
|
|
132
|
+
*/
|
|
133
|
+
declare function generateSilence(outputPath: string, durationMs: number): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* Speed up video by a factor
|
|
136
|
+
*/
|
|
137
|
+
declare function speedUpVideo(opts: SpeedUpOptions): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* Concatenate main video with outro (video streams only, no audio processing)
|
|
140
|
+
* The outro is scaled to fit the main video dimensions and centered
|
|
141
|
+
* Returns the combined video for further processing (composition with audio)
|
|
142
|
+
*/
|
|
143
|
+
declare function concatVideoWithOutro(opts: ConcatVideoOptions): Promise<void>;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Splice video files into the recording at specific timestamps
|
|
147
|
+
* Replaces dark placeholder sections with actual video content
|
|
148
|
+
*/
|
|
149
|
+
declare function spliceVideos(opts: SpliceVideosOptions): Promise<void>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Build step transitions array from steps and durations
|
|
153
|
+
* @param steps - Array of steps with optional transition field
|
|
154
|
+
* @param stepDurations - Duration of each step in milliseconds
|
|
155
|
+
* @param defaultTransition - Config-level default (applied to all steps except first)
|
|
156
|
+
*/
|
|
157
|
+
declare function buildStepTransitions(steps: {
|
|
158
|
+
transition?: 'fade' | 'blend';
|
|
159
|
+
}[], stepDurations: number[], defaultTransition?: 'none' | 'fade' | 'blend'): StepTransition[];
|
|
160
|
+
/**
|
|
161
|
+
* Apply fast step transitions using FFmpeg xfade filter
|
|
162
|
+
*
|
|
163
|
+
* Uses single-pass filter graph:
|
|
164
|
+
* 1. Split input into segments at transition points using trim
|
|
165
|
+
* 2. Chain segments with xfade for smooth transitions
|
|
166
|
+
* 3. Single encode pass - fast and efficient
|
|
167
|
+
*
|
|
168
|
+
* Transition types:
|
|
169
|
+
* - 'blend': Cross-dissolve between frames (xfade=fade)
|
|
170
|
+
* - 'fade': Dip to black and back (xfade=fadeblack)
|
|
171
|
+
*/
|
|
172
|
+
declare function applyTransitions(opts: ApplyTransitionsOptions): Promise<void>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* CSS and ASS styling for QuickPeek
|
|
176
|
+
*/
|
|
177
|
+
declare const CURSOR_CSS = "\n #qp-cursor {\n position: fixed;\n width: 32px;\n height: 38px;\n background: url(\"data:image/png;base64,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\") no-repeat;\n background-size: contain;\n pointer-events: none;\n z-index: 2147483647;\n transform: translate(-3px, -3px);\n transition: left 800ms ease-out, top 800ms ease-out, transform 0.08s ease-out;\n filter: drop-shadow(1px 1px 2px rgba(0,0,0,0.3));\n }\n #qp-cursor.clicking {\n transform: translate(-3px, -3px) scale(0.9);\n }\n";
|
|
178
|
+
/**
|
|
179
|
+
* JavaScript to inject for cursor tracking
|
|
180
|
+
*/
|
|
181
|
+
declare const CURSOR_SCRIPT = "\n (function() {\n if (document.getElementById('qp-cursor')) return;\n const cursor = document.createElement('div');\n cursor.id = 'qp-cursor';\n document.body.appendChild(cursor);\n\n document.addEventListener('mousemove', (e) => {\n cursor.style.left = e.clientX + 'px';\n cursor.style.top = e.clientY + 'px';\n });\n\n document.addEventListener('mousedown', () => cursor.classList.add('clicking'));\n document.addEventListener('mouseup', () => cursor.classList.remove('clicking'));\n\n // Start at center\n cursor.style.left = window.innerWidth / 2 + 'px';\n cursor.style.top = window.innerHeight / 2 + 'px';\n })();\n";
|
|
182
|
+
/**
|
|
183
|
+
* Generate ASS subtitle file header
|
|
184
|
+
*/
|
|
185
|
+
declare function generateASSHeader(width: number, height: number, font: string, size: number, primaryColor: string, secondaryColor: string, outlineColor: string, backColor: string, bold: boolean, outlineWidth: number, shadow: number, marginBottom: number): string;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Subscription and user registration client
|
|
189
|
+
* CLI-only headless registration fallback. Tier/verification checks
|
|
190
|
+
* are now in the SparkStripe gating module (getTierByEmail, isVerified).
|
|
191
|
+
*/
|
|
192
|
+
interface RegistrationResult {
|
|
193
|
+
success: boolean;
|
|
194
|
+
pendingVerification?: boolean;
|
|
195
|
+
alreadyVerified?: boolean;
|
|
196
|
+
error?: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Register user for free plan via SparkStripe
|
|
200
|
+
*/
|
|
201
|
+
declare function registerFreeUser(email: string): Promise<RegistrationResult>;
|
|
202
|
+
|
|
203
|
+
/** Minimal step shape needed by TTS modules — compatible with full Step from plan.ts */
|
|
204
|
+
interface TTSStep {
|
|
205
|
+
caption: string;
|
|
206
|
+
action?: string;
|
|
207
|
+
target?: string;
|
|
208
|
+
value?: string;
|
|
209
|
+
partial?: string;
|
|
210
|
+
duration?: number;
|
|
211
|
+
}
|
|
212
|
+
interface WordTiming {
|
|
213
|
+
word: string;
|
|
214
|
+
start: number;
|
|
215
|
+
end: number;
|
|
216
|
+
}
|
|
217
|
+
interface StepAudio {
|
|
218
|
+
duration: number;
|
|
219
|
+
words: WordTiming[];
|
|
220
|
+
}
|
|
221
|
+
interface AudioResult {
|
|
222
|
+
voiceoverPath: string;
|
|
223
|
+
stepAudio: Map<number, StepAudio>;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Metadata stored alongside voiceover for reuse detection
|
|
227
|
+
*/
|
|
228
|
+
interface VoiceoverMeta {
|
|
229
|
+
captionsHash: string;
|
|
230
|
+
totalCaptionLength: number;
|
|
231
|
+
stepCount: number;
|
|
232
|
+
voice: string;
|
|
233
|
+
stepDurations: Record<number, number>;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Load existing voiceover metadata if available
|
|
238
|
+
*/
|
|
239
|
+
declare function loadVoiceoverMeta(outputDir: string): Promise<VoiceoverMeta | null>;
|
|
240
|
+
|
|
241
|
+
interface Step {
|
|
242
|
+
caption: string;
|
|
243
|
+
action?: string;
|
|
244
|
+
target?: string;
|
|
245
|
+
value?: string;
|
|
246
|
+
partial?: string;
|
|
247
|
+
duration?: number;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Check if existing voiceover can be reused
|
|
251
|
+
*/
|
|
252
|
+
declare function canReuseVoiceover(steps: Step[], voice: string, existingMeta: VoiceoverMeta | null): {
|
|
253
|
+
canReuse: boolean;
|
|
254
|
+
reason?: string;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Load existing voiceover and reconstruct stepAudio from metadata
|
|
258
|
+
*/
|
|
259
|
+
declare function loadExistingVoiceover(steps: Step[], outputDir: string, meta: VoiceoverMeta, config: {
|
|
260
|
+
timing: {
|
|
261
|
+
minStepDuration: number;
|
|
262
|
+
};
|
|
263
|
+
}): Promise<{
|
|
264
|
+
voiceoverPath: string;
|
|
265
|
+
stepAudio: Map<number, {
|
|
266
|
+
duration: number;
|
|
267
|
+
words: {
|
|
268
|
+
word: string;
|
|
269
|
+
start: number;
|
|
270
|
+
end: number;
|
|
271
|
+
}[];
|
|
272
|
+
}>;
|
|
273
|
+
}>;
|
|
274
|
+
|
|
275
|
+
declare function resolveVoice(config: Config): string | null;
|
|
276
|
+
declare function generateVoiceover(steps: TTSStep[], config: Config, workDir: string, outputDir: string, logError: (context: string, error: unknown) => Promise<void>, task: (text: string) => {
|
|
277
|
+
succeed: (text?: string) => void;
|
|
278
|
+
}, info: (text: string) => void): Promise<AudioResult>;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Synthesize speech using Python edge-tts (more reliable than Node.js msedge-tts)
|
|
282
|
+
* Falls back to Node.js msedge-tts if Python is unavailable
|
|
283
|
+
*/
|
|
284
|
+
declare function synthesizeSpeech(text: string, outputPath: string, voice: string): Promise<void>;
|
|
285
|
+
|
|
286
|
+
declare function estimateWordTimings(caption: string, duration: number): WordTiming[];
|
|
287
|
+
/**
|
|
288
|
+
* Align voiceover to actual recording durations
|
|
289
|
+
* If any step's recording took longer than its voiceover, pad with silence
|
|
290
|
+
* Returns updated stepAudio with aligned durations
|
|
291
|
+
*/
|
|
292
|
+
declare function alignVoiceoverToRecording(stepAudio: Map<number, StepAudio>, actualRecordingDurations: number[], workDir: string, outputDir: string): Promise<Map<number, StepAudio>>;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* FFmpeg watermark filter generation for QuickPeek
|
|
296
|
+
* Applies watermarks to free tier videos longer than 20 seconds
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Build FFmpeg drawtext filters for watermarks
|
|
301
|
+
* Returns empty string if no watermark needed (pro tier or short video)
|
|
302
|
+
*/
|
|
303
|
+
declare function buildWatermarkFilters(opts: {
|
|
304
|
+
tier: UserTier;
|
|
305
|
+
videoDuration: number;
|
|
306
|
+
}): string;
|
|
307
|
+
|
|
308
|
+
export { type ApplyTransitionsOptions, type AudioResult, type BlendTransition, CURSOR_CSS, CURSOR_PNG, CURSOR_SCRIPT, type ComposeOptions, type ConcatVideoOptions, Config, type SpeedUpOptions, type SpliceVideosOptions, type StepAudio, type StepSync, type StepTransition, type TTSStep, type TimeSegment, UserTier, type VideoOverlay, type VoiceoverMeta, type WordTiming, alignVoiceoverToRecording, applyTransitions, buildKeepSegments, buildStepTransitions, buildWatermarkFilters, canReuseVoiceover, composeVideo, concatMedia, concatVideoWithOutro, estimateWordTimings, generateASSHeader, generateSilence, generateVoiceover, getMediaDuration, getUpgradeUrl, loadExistingVoiceover, loadVoiceoverMeta, mapTimestampsAfterCompression, registerFreeUser, resolveVoice, shouldWatermark, speedUpVideo, spliceVideos, synthesizeSpeech };
|