@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.
@@ -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,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";
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 };