@phont-ai/subtitles-core 0.1.10 → 0.2.2
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 +51 -0
- package/dist/browser.d.mts +140 -0
- package/dist/browser.d.ts +140 -0
- package/dist/browser.js +1 -0
- package/dist/browser.mjs +1 -0
- package/dist/index.d.mts +523 -403
- package/dist/index.d.ts +1463 -7
- package/dist/index.js +3 -5414
- package/dist/index.mjs +3 -5347
- package/dist/phont-subtitles.global.js +2 -0
- package/package.json +37 -14
- package/dist/client/PhontClient.d.ts +0 -16
- package/dist/types/registration.d.ts +0 -46
- package/dist/utils/video-handling.d.ts +0 -33
- package/dist/validation/registration.d.ts +0 -64
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,1466 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Core
|
|
2
|
+
* Core types for PHONT expressive subtitles
|
|
3
|
+
* Framework-agnostic, pure TypeScript
|
|
4
|
+
*/
|
|
5
|
+
type Emotion = "joy" | "anger" | "sadness" | "fear" | "disgust" | "surprise" | "none";
|
|
6
|
+
interface SubtitleWord {
|
|
7
|
+
text: string;
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
emotion?: Emotion;
|
|
11
|
+
emphasis?: number;
|
|
12
|
+
}
|
|
13
|
+
interface SubtitleLine {
|
|
14
|
+
speaker?: string;
|
|
15
|
+
start: number;
|
|
16
|
+
end: number;
|
|
17
|
+
words: SubtitleWord[];
|
|
18
|
+
emotion?: Emotion;
|
|
19
|
+
emotion_intensity?: number;
|
|
20
|
+
}
|
|
21
|
+
interface SubtitleTrack {
|
|
22
|
+
vodId: string;
|
|
23
|
+
language: string;
|
|
24
|
+
lines: SubtitleLine[];
|
|
25
|
+
}
|
|
26
|
+
interface EmotionStyle {
|
|
27
|
+
fontWeight?: number;
|
|
28
|
+
skew?: number;
|
|
29
|
+
jitter?: number;
|
|
30
|
+
color?: string;
|
|
31
|
+
glow?: boolean;
|
|
32
|
+
scale?: number;
|
|
33
|
+
}
|
|
34
|
+
interface SubtitleEngineState {
|
|
35
|
+
activeLine: SubtitleLine | null;
|
|
36
|
+
activeWords: SubtitleWord[];
|
|
37
|
+
currentWordIndex: number;
|
|
38
|
+
emotion: Emotion;
|
|
39
|
+
emotionIntensity: number;
|
|
40
|
+
}
|
|
41
|
+
interface PhontClientOptions {
|
|
42
|
+
apiKey?: string;
|
|
43
|
+
authToken?: string;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
apiBaseUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Animation Types
|
|
50
|
+
*
|
|
51
|
+
* Type definitions for animation parameter states, curves, and animation state
|
|
52
|
+
*/
|
|
53
|
+
interface CurvePoint {
|
|
54
|
+
time: number;
|
|
55
|
+
value: number | string;
|
|
56
|
+
}
|
|
57
|
+
interface ParameterState {
|
|
58
|
+
points: CurvePoint[];
|
|
59
|
+
currentValue?: number | string;
|
|
60
|
+
}
|
|
61
|
+
interface ParameterStates {
|
|
62
|
+
[key: string]: ParameterState;
|
|
63
|
+
}
|
|
64
|
+
interface AnimationParameterStates {
|
|
65
|
+
parameterStates: ParameterStates;
|
|
66
|
+
duration?: number;
|
|
67
|
+
format?: 'normalized' | 'absolute';
|
|
68
|
+
fontData?: {
|
|
69
|
+
style?: {
|
|
70
|
+
fontFamily?: string;
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
};
|
|
73
|
+
config?: unknown;
|
|
74
|
+
fontId?: string | null;
|
|
75
|
+
fontName?: string | null;
|
|
76
|
+
};
|
|
77
|
+
text?: string;
|
|
78
|
+
name?: string;
|
|
79
|
+
}
|
|
80
|
+
interface AnimationState {
|
|
81
|
+
fontSize: number;
|
|
82
|
+
scale: number;
|
|
83
|
+
opacity: number;
|
|
84
|
+
slant: number;
|
|
85
|
+
weight: number;
|
|
86
|
+
width: number;
|
|
87
|
+
textColor: string;
|
|
88
|
+
translateX: number;
|
|
89
|
+
translateY: number;
|
|
90
|
+
stretchY: number;
|
|
91
|
+
rotation?: number;
|
|
92
|
+
skewX?: number;
|
|
93
|
+
fontVariationAxes?: Record<string, number>;
|
|
94
|
+
letterShake?: number;
|
|
95
|
+
shadowColor?: string;
|
|
96
|
+
}
|
|
97
|
+
interface SentenceAnimationState extends AnimationState {
|
|
98
|
+
text: string;
|
|
99
|
+
emotion: string;
|
|
100
|
+
emotionIntensity: number;
|
|
101
|
+
fontFamily: string;
|
|
102
|
+
startTime: number;
|
|
103
|
+
endTime: number;
|
|
104
|
+
duration: number;
|
|
105
|
+
isActive: boolean;
|
|
106
|
+
}
|
|
107
|
+
interface WordAnimationState {
|
|
108
|
+
text: string;
|
|
109
|
+
words: Array<{
|
|
110
|
+
word: string;
|
|
111
|
+
start: number;
|
|
112
|
+
end: number;
|
|
113
|
+
animationEnd: number;
|
|
114
|
+
isActive: boolean;
|
|
115
|
+
emotion: string;
|
|
116
|
+
emotionIntensity: number;
|
|
117
|
+
originalEmotionIntensity?: number;
|
|
118
|
+
fontFamily: string;
|
|
119
|
+
emphasis: number;
|
|
120
|
+
emphasisEffect?: any;
|
|
121
|
+
strongEmphasis?: any;
|
|
122
|
+
animationParameters: AnimationState;
|
|
123
|
+
joyAnimationMode?: 'letter' | 'letter-bulge' | 'word' | null;
|
|
124
|
+
joyProgress?: number | null;
|
|
125
|
+
}>;
|
|
126
|
+
currentWordIndex: number;
|
|
127
|
+
emotion: string;
|
|
128
|
+
emotionIntensity: number;
|
|
129
|
+
fontFamily: string;
|
|
130
|
+
startTime: number;
|
|
131
|
+
endTime: number;
|
|
132
|
+
isActive: boolean;
|
|
133
|
+
}
|
|
134
|
+
interface SubtitleSegment {
|
|
135
|
+
start_time: number;
|
|
136
|
+
end_time: number;
|
|
137
|
+
text?: string;
|
|
138
|
+
subtitle?: string;
|
|
139
|
+
emotion?: string;
|
|
140
|
+
emotion_intensity?: number;
|
|
141
|
+
animation_url?: string;
|
|
142
|
+
/** Per-cue expressiveness (0–1); combined with global GTS slider at playback. */
|
|
143
|
+
gts?: number;
|
|
144
|
+
words?: Array<{
|
|
145
|
+
word: string;
|
|
146
|
+
start: number;
|
|
147
|
+
end: number;
|
|
148
|
+
emphasis?: number;
|
|
149
|
+
emotion?: string;
|
|
150
|
+
emotion_intensity?: number;
|
|
151
|
+
animation_url?: string;
|
|
152
|
+
animations?: Record<string, number>;
|
|
153
|
+
gts?: number;
|
|
154
|
+
}>;
|
|
155
|
+
animations?: Record<string, number>;
|
|
156
|
+
volume?: number;
|
|
157
|
+
speech_rate?: number;
|
|
158
|
+
merged_subtitles?: SubtitleSegment[];
|
|
159
|
+
}
|
|
160
|
+
interface AnimationOptions {
|
|
161
|
+
maxExpression?: number;
|
|
162
|
+
threshold?: number;
|
|
163
|
+
isRTL?: boolean;
|
|
164
|
+
volume?: number;
|
|
165
|
+
speechRate?: number;
|
|
166
|
+
nextSegmentStartTime?: number;
|
|
167
|
+
extendedEndTime?: number;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* ============================================
|
|
172
|
+
* SUBTITLE STATE MANAGER
|
|
173
|
+
* ============================================
|
|
174
|
+
* Ported from app/components/subtitle/state/SubtitleStateManager.js
|
|
175
|
+
*/
|
|
176
|
+
interface SubtitleState$1 {
|
|
177
|
+
videoTimeSeed: number | null;
|
|
178
|
+
isVisible: boolean;
|
|
179
|
+
isKaraokeMode: boolean;
|
|
180
|
+
isSpeaker: boolean;
|
|
181
|
+
animationMode: "Sentence" | "Word";
|
|
182
|
+
currentSubtitle: string;
|
|
183
|
+
currentSequence: any | null;
|
|
184
|
+
subtitles: any[];
|
|
185
|
+
currentSubtitleData: any | null;
|
|
186
|
+
lastEndTime: number;
|
|
187
|
+
processedSequences: Set<number>;
|
|
188
|
+
initialSequenceOffset: number | null;
|
|
189
|
+
segmentDurations: Map<number, number>;
|
|
190
|
+
soundByteAll: any[];
|
|
191
|
+
soundByteImportant: any[];
|
|
192
|
+
soundByteFiltered: any[];
|
|
193
|
+
showAllSoundbites: boolean;
|
|
194
|
+
showImportantSoundbites: boolean;
|
|
195
|
+
currentTime: number;
|
|
196
|
+
liveStartTime: number | null;
|
|
197
|
+
liveCurrentTime: number;
|
|
198
|
+
soundbiteAnimations: any[];
|
|
199
|
+
showSoundbiteAnimations: boolean;
|
|
200
|
+
isClosedCaptionsEnabled: boolean;
|
|
201
|
+
}
|
|
202
|
+
interface VodState$1 {
|
|
203
|
+
subtitles: any[] | null;
|
|
204
|
+
soundByteAll: any[];
|
|
205
|
+
soundByteImportant: any[];
|
|
206
|
+
soundByteFiltered: any[];
|
|
207
|
+
currentLanguage: string | null;
|
|
208
|
+
videoId: string | null;
|
|
209
|
+
}
|
|
210
|
+
type StateCallback = (state: SubtitleState$1) => void;
|
|
211
|
+
declare class SubtitleStateManager {
|
|
212
|
+
subscribers: Set<StateCallback>;
|
|
213
|
+
state: SubtitleState$1;
|
|
214
|
+
vodState: VodState$1;
|
|
215
|
+
constructor();
|
|
216
|
+
/**
|
|
217
|
+
* Subscribe to state changes
|
|
218
|
+
*/
|
|
219
|
+
subscribe(callback: StateCallback): () => void;
|
|
220
|
+
/**
|
|
221
|
+
* Notify all subscribers of state changes
|
|
222
|
+
*/
|
|
223
|
+
notify(): void;
|
|
224
|
+
/**
|
|
225
|
+
* Get current state (immutable copy)
|
|
226
|
+
*/
|
|
227
|
+
getState(): SubtitleState$1;
|
|
228
|
+
/**
|
|
229
|
+
* Get VOD state (immutable copy)
|
|
230
|
+
*/
|
|
231
|
+
getVodState(): VodState$1;
|
|
232
|
+
toggleSubtitles(isVisible: boolean): void;
|
|
233
|
+
toggleClosedCaptions(isEnabled: boolean): void;
|
|
234
|
+
toggleKaraoke(isKaraokeMode: boolean): void;
|
|
235
|
+
toggleSpeaker(isSpeaker: boolean): void;
|
|
236
|
+
toggleSoundbites(type: "all" | "important", isVisible: boolean): void;
|
|
237
|
+
toggleSoundbiteAnimations(isVisible: boolean): void;
|
|
238
|
+
setAnimationMode(mode: "Sentence" | "Word"): void;
|
|
239
|
+
updateSubtitle(text: string): void;
|
|
240
|
+
updateCurrentTime(time: number): void;
|
|
241
|
+
updateSubtitles(subtitles: any[]): void;
|
|
242
|
+
updateCurrentSubtitleData(data: any): void;
|
|
243
|
+
updateSoundbites(all?: any[], important?: any[], filtered?: any[]): void;
|
|
244
|
+
updateSoundbiteAnimations(animations: any[]): void;
|
|
245
|
+
updateVodSubtitles(subtitles: any[]): void;
|
|
246
|
+
updateVodLanguage(language: string | null): void;
|
|
247
|
+
updateVodVideoId(videoId: string | null): void;
|
|
248
|
+
updateVodSoundbites(all?: any[], important?: any[], filtered?: any[]): void;
|
|
249
|
+
updateLiveStartTime(time: number | null): void;
|
|
250
|
+
updateLiveCurrentTime(time: number): void;
|
|
251
|
+
updateVideoTimeSeed(seed: number | null): void;
|
|
252
|
+
markSequenceProcessed(sequenceNumber: number): void;
|
|
253
|
+
isSequenceProcessed(sequenceNumber: number): boolean;
|
|
254
|
+
setInitialSequenceOffset(offset: number): void;
|
|
255
|
+
setSegmentDuration(sequenceNumber: number, duration: number): void;
|
|
256
|
+
getSegmentDuration(sequenceNumber: number): number | undefined;
|
|
257
|
+
updateCurrentSequence(sequence: any): void;
|
|
258
|
+
reset(): void;
|
|
259
|
+
resetControls(): void;
|
|
260
|
+
clearLiveState(): void;
|
|
261
|
+
clearVodState(): void;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* ============================================
|
|
266
|
+
* SUBTITLE MANAGER (Orchestrator)
|
|
267
|
+
* ============================================
|
|
268
|
+
*
|
|
269
|
+
* PURPOSE:
|
|
270
|
+
* Central coordinator for subtitle system. Delegates to specialized classes.
|
|
271
|
+
*
|
|
272
|
+
* USED BY:
|
|
273
|
+
* - PhontSubtitles.jsx: Gets subtitle state and data
|
|
274
|
+
* - Player components: Controls playback and timing
|
|
275
|
+
* - PhontControls.jsx: UI toggles and settings
|
|
276
|
+
*
|
|
277
|
+
* DELEGATES TO:
|
|
278
|
+
* - SubtitleStateManager: State management and subscriptions
|
|
279
|
+
* - SubtitleCache: Language caching and detection
|
|
280
|
+
* - SubtitleFetcher: API requests & data normalization
|
|
281
|
+
* - SoundbiteProcessor: Soundbite/animation processing & laughter merging
|
|
282
|
+
* - VodDataProcessor: VOD data processing, deduplication, soundbite coordination
|
|
283
|
+
* - LiveDataProcessor: LIVE data processing, timing offsets, soundbite coordination
|
|
284
|
+
* - LiveStreamCumulativeTimeGenerator: Generates cumulative time for LIVE streams
|
|
285
|
+
* - CurrentSubtitle: Gets subtitle for time (BOTH VOD & LIVE) + karaoke
|
|
286
|
+
*
|
|
287
|
+
* STATUS: Phase 2 Complete - All 8 classes extracted, clean architecture, no duplication
|
|
288
|
+
*/
|
|
289
|
+
type AnimationMode$1 = "Sentence" | "Word" | string;
|
|
290
|
+
interface SubtitleState {
|
|
291
|
+
isVisible: boolean;
|
|
292
|
+
isKaraokeMode: boolean;
|
|
293
|
+
isSpeaker: boolean;
|
|
294
|
+
animationMode: AnimationMode$1;
|
|
295
|
+
currentSubtitle: string;
|
|
296
|
+
currentSequence: number | null;
|
|
297
|
+
subtitles: any[];
|
|
298
|
+
currentSubtitleData: any | null;
|
|
299
|
+
videoTimeSeed: number | null;
|
|
300
|
+
currentTime: number;
|
|
301
|
+
liveStartTime: number | null;
|
|
302
|
+
liveCurrentTime: number;
|
|
303
|
+
lastEndTime: number;
|
|
304
|
+
processedSequences: Set<number>;
|
|
305
|
+
initialSequenceOffset: number | null;
|
|
306
|
+
segmentDurations: Map<number, number>;
|
|
307
|
+
soundByteAll: any[];
|
|
308
|
+
soundByteImportant: any[];
|
|
309
|
+
soundByteFiltered: any[];
|
|
310
|
+
showAllSoundbites: boolean;
|
|
311
|
+
showImportantSoundbites: boolean;
|
|
312
|
+
soundbiteAnimations: any[];
|
|
313
|
+
showSoundbiteAnimations: boolean;
|
|
314
|
+
isClosedCaptionsEnabled: boolean;
|
|
315
|
+
isBackgroundEffectEnabled: boolean;
|
|
316
|
+
backgroundEffect: string;
|
|
317
|
+
backgroundOpacity: number;
|
|
318
|
+
transition: boolean;
|
|
319
|
+
[key: string]: any;
|
|
320
|
+
}
|
|
321
|
+
interface VodState {
|
|
322
|
+
subtitles: any[] | null;
|
|
323
|
+
url: string | null;
|
|
324
|
+
currentSubtitle: string;
|
|
325
|
+
currentSubtitleData: any | null;
|
|
326
|
+
currentSequence: number | null;
|
|
327
|
+
soundByteAll: any[];
|
|
328
|
+
soundByteImportant: any[];
|
|
329
|
+
soundByteFiltered: any[];
|
|
330
|
+
currentLanguage?: string | null;
|
|
331
|
+
videoId?: string | null;
|
|
332
|
+
translation_provider?: string | null;
|
|
333
|
+
translation_model?: string | null;
|
|
334
|
+
}
|
|
335
|
+
type SubtitleSubscriber = (state: SubtitleState) => void;
|
|
336
|
+
declare class SubtitleManager {
|
|
337
|
+
private stateManager;
|
|
338
|
+
private cache;
|
|
339
|
+
private fetcher;
|
|
340
|
+
private soundbiteProcessor;
|
|
341
|
+
private vodDataProcessor;
|
|
342
|
+
private liveTimeGenerator;
|
|
343
|
+
private liveDataProcessor;
|
|
344
|
+
private currentSubtitle;
|
|
345
|
+
constructor();
|
|
346
|
+
/**
|
|
347
|
+
* Set PhontClient for authenticated API requests
|
|
348
|
+
* This will be passed to SubtitleFetcher for use in API calls
|
|
349
|
+
*/
|
|
350
|
+
setPhontClient(phontClient: any): void;
|
|
351
|
+
get state(): SubtitleState;
|
|
352
|
+
set state(value: SubtitleState);
|
|
353
|
+
get vodState(): VodState;
|
|
354
|
+
set vodState(value: VodState);
|
|
355
|
+
get subscribers(): Set<SubtitleSubscriber>;
|
|
356
|
+
subscribe(callback: SubtitleSubscriber): () => void;
|
|
357
|
+
notify(): void;
|
|
358
|
+
toggleSubtitles(isVisible: boolean): void;
|
|
359
|
+
toggleClosedCaptions(isEnabled: boolean): void;
|
|
360
|
+
toggleKaraoke(isKaraokeMode: boolean): void;
|
|
361
|
+
toggleSpeaker(isSpeaker: boolean): void;
|
|
362
|
+
toggleBackgroundEffect(isEnabled: boolean, effect?: string): void;
|
|
363
|
+
toggleSoundbites(type: "all" | "important", isVisible: boolean): void;
|
|
364
|
+
toggleSoundbiteAnimations(isVisible: boolean): void;
|
|
365
|
+
mapEventLabelToAnimation(eventLabel: string): string | null;
|
|
366
|
+
processSoundbiteAnimations(soundbites: any[]): any[];
|
|
367
|
+
setAnimationMode(mode: AnimationMode$1): void;
|
|
368
|
+
toggleTransition(enabled: boolean): void;
|
|
369
|
+
updateSubtitle(text: string): void;
|
|
370
|
+
updateLiveTime(videoTime?: number | null): number;
|
|
371
|
+
startLiveTiming(): void;
|
|
372
|
+
stopLiveTiming(): void;
|
|
373
|
+
pauseLiveTiming(): void;
|
|
374
|
+
resumeLiveTiming(): void;
|
|
375
|
+
resetLiveStream(videoElement: HTMLVideoElement): void;
|
|
376
|
+
getLiveTime(): number;
|
|
377
|
+
getState(): SubtitleState;
|
|
378
|
+
reset(): void;
|
|
379
|
+
resetControls(): void;
|
|
380
|
+
getCumulativeDuration(upToSequenceNumber: number): number;
|
|
381
|
+
normalizeSubtitles(data: any): any;
|
|
382
|
+
normalizeSoundbites(arr: any[]): any[];
|
|
383
|
+
getAudioSegmentDuration(sequenceNumber: number): Promise<number>;
|
|
384
|
+
getInitialLiveSequenceNumber(): Promise<number>;
|
|
385
|
+
fetchSubtitles(sequenceNumber: number): Promise<any | null>;
|
|
386
|
+
loadVodSubtitles(urlOrVodId: string, languageCode?: string | null): Promise<void>;
|
|
387
|
+
_processVodData(data: any, url: string, languageCode: string | null, originalSubtitles?: any[]): void;
|
|
388
|
+
getSubtitleForTime(time: number): any;
|
|
389
|
+
getLiveSubtitleForTime(): any;
|
|
390
|
+
getCurrentSubtitleData(): any | null;
|
|
391
|
+
updateBackgroundOpacity(opacity: number): void;
|
|
392
|
+
clearOldSubtitles(currentTime: number, bufferSeconds?: number): void;
|
|
393
|
+
getQueueStatus(): {
|
|
394
|
+
totalSubtitles: number;
|
|
395
|
+
lastEndTime: number;
|
|
396
|
+
processedSequences: number[];
|
|
397
|
+
currentSequence: number | null;
|
|
398
|
+
};
|
|
399
|
+
setCurrentTime(time: number): void;
|
|
400
|
+
getActiveSoundbites(isVodMode?: boolean, vodTime?: number | null): any[];
|
|
401
|
+
getAllSoundbiteData(isVodMode?: boolean): any;
|
|
402
|
+
getActiveSoundbiteAnimations(isVodMode?: boolean): any[];
|
|
403
|
+
hasActiveAnimations(): boolean;
|
|
404
|
+
clearSoundbiteAnimations(): void;
|
|
405
|
+
clearOldAnimations(currentTime: number, bufferSeconds?: number): void;
|
|
406
|
+
getCurrentSpeaker(isVodMode?: boolean, vodTime?: number | null): string | null;
|
|
407
|
+
fetchSubtitleData(url: string, languageCode?: string | null, model?: string | null): Promise<void>;
|
|
408
|
+
switchSubtitleLanguage(languageCode: string | null, videoId: string, model?: string | null): Promise<void>;
|
|
409
|
+
_loadCachedSubtitles(data: any, url: string, languageCode: string | null): void;
|
|
410
|
+
getCurrentSubtitleLanguage(): string | null;
|
|
411
|
+
clearSubtitleCache(): void;
|
|
412
|
+
clearAudioPlaylistCache(): void;
|
|
413
|
+
updateState(): void;
|
|
414
|
+
}
|
|
415
|
+
declare const subtitleManager: SubtitleManager;
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* URL Decoder for Animation Parameters
|
|
419
|
+
*
|
|
420
|
+
* Extracted from app/playground/utils/urlEncoding.js
|
|
421
|
+
* Decodes animation URLs into parameter states
|
|
422
|
+
*
|
|
423
|
+
* Supports both full URLs and query strings:
|
|
424
|
+
* - Full URL: https://stage-dev.phont.ai/playground/shared?name=...&scale=0,1|1,1.2
|
|
425
|
+
* - Query string: ?name=...&scale=0,1|1,1.2
|
|
426
|
+
*/
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Decode animation data from URL or query string
|
|
430
|
+
* @param urlOrQuery - Full URL (https://...) or query string (?name=... or name=...)
|
|
431
|
+
* @returns Decoded animation data object with parameterStates
|
|
432
|
+
*/
|
|
433
|
+
declare function decodeAnimationUrl(urlOrQuery: string): AnimationParameterStates;
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Animation parameter calculation -- the unified rendering pipeline.
|
|
437
|
+
* TypeScript port of app/utils/animation/animationParameters.js.
|
|
438
|
+
*
|
|
439
|
+
* Samples authored URL curves with linear interpolation, then scales deviations
|
|
440
|
+
* proportionally from a reference point (REFERENCE_INTENSITY × REFERENCE_GTS).
|
|
441
|
+
* At the reference point the authored curve is rendered 1:1.
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Exponential variance curve (hockey stick)
|
|
446
|
+
*
|
|
447
|
+
* Creates an exponential curve that accelerates dramatically after 0.7 intensity.
|
|
448
|
+
* Low intensities (0.0-0.7): Subtle effects (gradual rise)
|
|
449
|
+
* High intensities (0.7-1.0): Dramatic effects (exponential acceleration)
|
|
450
|
+
*
|
|
451
|
+
* @param i - Input intensity (0-1)
|
|
452
|
+
* @returns Transformed intensity (0-1) with exponential variance
|
|
453
|
+
*/
|
|
454
|
+
declare function hockeyStickIntensity(i: number): number;
|
|
455
|
+
/**
|
|
456
|
+
* Sample a parameter curve at a given time
|
|
457
|
+
*
|
|
458
|
+
* Interpolates between keyframe points to get the value at a specific time.
|
|
459
|
+
* Supports both normalized (0-1) and absolute time formats.
|
|
460
|
+
* For color parameters, uses RGB interpolation for smooth color transitions.
|
|
461
|
+
*
|
|
462
|
+
* @param points - Array of {time, value} points or [time, value] arrays
|
|
463
|
+
* @param t - Normalized time (0-1)
|
|
464
|
+
* @param duration - Animation duration in seconds
|
|
465
|
+
* @param isNormalized - Whether points are in normalized (0-1) or absolute time
|
|
466
|
+
* @param parameterName - Name of the parameter (e.g., 'color', 'shadowColor', 'outlineColor') for color interpolation
|
|
467
|
+
* @returns Sampled value or null if no points
|
|
468
|
+
*/
|
|
469
|
+
declare function sampleParameterCurve(points: Array<{
|
|
470
|
+
time?: number;
|
|
471
|
+
value?: number | string;
|
|
472
|
+
} | [number, number | string]> | undefined | null, t: number, duration: number, isNormalized?: boolean, parameterName?: string | null): number | string | null;
|
|
473
|
+
interface AnimationParameterState {
|
|
474
|
+
rawIntensity: number;
|
|
475
|
+
scaleFactor: number;
|
|
476
|
+
scale: number;
|
|
477
|
+
slant: number;
|
|
478
|
+
stretchY: number;
|
|
479
|
+
translateX: number;
|
|
480
|
+
translateY: number;
|
|
481
|
+
weight: number;
|
|
482
|
+
width: number;
|
|
483
|
+
fontFamily: string;
|
|
484
|
+
textColor: string;
|
|
485
|
+
opacity: number;
|
|
486
|
+
textShadow: string;
|
|
487
|
+
blur: number;
|
|
488
|
+
outlineWidth: number;
|
|
489
|
+
outlineColor: string;
|
|
490
|
+
outline2Width?: number;
|
|
491
|
+
outline2Color?: string;
|
|
492
|
+
outline3Width?: number;
|
|
493
|
+
outline3Color?: string;
|
|
494
|
+
outlineShadowWidth?: number;
|
|
495
|
+
outlineShadowColor?: string;
|
|
496
|
+
outlineShadowBlur?: number;
|
|
497
|
+
glowIntensity?: number;
|
|
498
|
+
glowColor?: string;
|
|
499
|
+
snakeWave?: number;
|
|
500
|
+
karaoke?: number;
|
|
501
|
+
stretchX?: number;
|
|
502
|
+
letterSpacing: string;
|
|
503
|
+
fontVariationAxes: Record<string, number>;
|
|
504
|
+
text: string;
|
|
505
|
+
joy: number;
|
|
506
|
+
fear: number;
|
|
507
|
+
standardEmphasis: number;
|
|
508
|
+
strongEmphasis: number;
|
|
509
|
+
joyWaveProgress: number;
|
|
510
|
+
}
|
|
511
|
+
declare const REFERENCE_INTENSITY = 0.7;
|
|
512
|
+
declare const REFERENCE_GTS = 0.7;
|
|
513
|
+
/** Play authored URL curves 1:1 — matches main playground preview (no intensity/GTS scaling). */
|
|
514
|
+
/** Playground / converter preview only — bypasses GTS scaling (1:1 at reference). */
|
|
515
|
+
declare const AUTHORED_PLAYBACK_OPTIONS: {
|
|
516
|
+
rawMode: boolean;
|
|
517
|
+
};
|
|
518
|
+
/**
|
|
519
|
+
* Sample all animation parameters at time `t` with proportional intensity scaling.
|
|
520
|
+
*
|
|
521
|
+
* The authored curve represents the animation at REFERENCE_INTENSITY × REFERENCE_GTS.
|
|
522
|
+
* At those values you see exactly what was authored. Other intensity × GTS values
|
|
523
|
+
* scale linearly from there.
|
|
524
|
+
*/
|
|
525
|
+
declare function calculateAnimationParameters(animationData: AnimationParameterStates | null, rawIntensity: number, t: number, gts: number, subtitleDuration?: number | null, { rawMode }?: {
|
|
526
|
+
rawMode?: boolean;
|
|
527
|
+
}): AnimationParameterState | null;
|
|
528
|
+
|
|
529
|
+
type AnimationMode = 'sentence' | 'word';
|
|
530
|
+
type AnimationStateOptions = AnimationOptions & {
|
|
531
|
+
transparentUntilSpoken?: boolean;
|
|
532
|
+
};
|
|
533
|
+
declare function calculateAnimationState(segment: SubtitleSegment | null, currentTime: number, modeOrOptions?: AnimationMode | AnimationStateOptions, maybeOptions?: AnimationStateOptions): SentenceAnimationState | WordAnimationState;
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Emotion Animation URLs
|
|
537
|
+
*
|
|
538
|
+
* Config mapping emotion → animation URL
|
|
539
|
+
* Uses full playground URLs from app/gallery/config/animations.js
|
|
540
|
+
*
|
|
541
|
+
* SYNCED FROM: app/gallery/config/emotionMapping.js
|
|
542
|
+
*
|
|
543
|
+
* To update these URLs:
|
|
544
|
+
* 1. Update app/gallery/config/animations.js with new URLs
|
|
545
|
+
* 2. Update app/gallery/config/emotionMapping.js to map emotions to indices
|
|
546
|
+
* 3. Run: node scripts/sync-animation-urls.js
|
|
547
|
+
* OR manually copy the URLs based on the mapping
|
|
548
|
+
*
|
|
549
|
+
* The decodeAnimationUrl() function now supports both full URLs and query strings.
|
|
550
|
+
*/
|
|
551
|
+
declare const ANIMATION_URLS: string[];
|
|
552
|
+
/**
|
|
553
|
+
* Emotion to Animation URL mapping
|
|
554
|
+
* Based on app/gallery/config/emotionMapping.js
|
|
555
|
+
*
|
|
556
|
+
* Maps each emotion to an index in ANIMATION_URLS array
|
|
557
|
+
*/
|
|
558
|
+
declare const EMOTION_ANIMATION_URLS: Record<string, string>;
|
|
559
|
+
/**
|
|
560
|
+
* Get animation URL for an emotion
|
|
561
|
+
* @param emotion - Emotion name (case-insensitive)
|
|
562
|
+
* @returns Full animation URL or query string fallback
|
|
563
|
+
*/
|
|
564
|
+
declare function getAnimationUrlForEmotion(emotion: string): string;
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Published Animation Resolver (framework-agnostic)
|
|
568
|
+
*
|
|
569
|
+
* Fetches the active published animation URL for each emotion from the backend,
|
|
570
|
+
* caches the results, and provides sync/async accessors.
|
|
571
|
+
*
|
|
572
|
+
* Any consumer (app, SDK, extension) creates an instance by supplying a fetcher
|
|
573
|
+
* that returns an array of animation objects for a given stylesheet.
|
|
574
|
+
*
|
|
575
|
+
* USAGE:
|
|
576
|
+
* import { createPublishedAnimationResolver } from '@phont-ai/subtitles-core';
|
|
577
|
+
*
|
|
578
|
+
* const resolver = createPublishedAnimationResolver(
|
|
579
|
+
* async (stylesheet) => fetch(`/animations?stylesheet_name=${stylesheet}&is_active=true`).then(r => r.json())
|
|
580
|
+
* );
|
|
581
|
+
*
|
|
582
|
+
* await resolver.prefetch('default'); // warm the cache
|
|
583
|
+
* const url = resolver.getSync('lust'); // sync (returns null if not cached)
|
|
584
|
+
* const url = await resolver.get('lust'); // async (fetches if needed)
|
|
585
|
+
* resolver.invalidate(); // bust after publishing
|
|
586
|
+
*/
|
|
587
|
+
interface AnimationRecord {
|
|
588
|
+
emotion: string;
|
|
589
|
+
url: string;
|
|
590
|
+
is_active: boolean;
|
|
591
|
+
[key: string]: unknown;
|
|
592
|
+
}
|
|
593
|
+
type AnimationFetcher = (stylesheetName: string) => Promise<AnimationRecord[]>;
|
|
594
|
+
interface PublishedAnimationResolver {
|
|
595
|
+
prefetch: (stylesheetName?: string) => Promise<Map<string, string>>;
|
|
596
|
+
get: (emotion: string, stylesheetName?: string) => Promise<string | null>;
|
|
597
|
+
getSync: (emotion: string, stylesheetName?: string) => string | null;
|
|
598
|
+
invalidate: () => void;
|
|
599
|
+
}
|
|
600
|
+
declare function createPublishedAnimationResolver(fetcher: AnimationFetcher, { ttlMs }?: {
|
|
601
|
+
ttlMs?: number;
|
|
602
|
+
}): PublishedAnimationResolver;
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Emotion Selector
|
|
606
|
+
*
|
|
607
|
+
* Pure function version of useEmotionSelector (no React)
|
|
608
|
+
* Selects the highest-value emotion from backend animations object
|
|
609
|
+
* Core emotions (anger, fear, sadness, joy) receive ×1.5 importance multiplier
|
|
610
|
+
*/
|
|
611
|
+
interface EmotionSelectionResult {
|
|
612
|
+
emotion: string;
|
|
613
|
+
intensity: number;
|
|
614
|
+
allEmotions: Array<[string, number, number, number, boolean]>;
|
|
615
|
+
importanceFactor: number;
|
|
616
|
+
isCore: boolean;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Find the highest emotion value from animations object
|
|
620
|
+
*
|
|
621
|
+
* Core emotions (anger, fear, sadness, joy, disgust) are multiplied by 1.5 before selection
|
|
622
|
+
* to prioritize fundamental emotions over secondary ones.
|
|
623
|
+
*
|
|
624
|
+
* @param animationsObject - Object with emotion keys and values (0-1)
|
|
625
|
+
* @param threshold - Minimum value to consider (GTS-controlled: 0.7 at GTS=0, 0.2 at GTS=1)
|
|
626
|
+
* @returns Emotion selection result
|
|
627
|
+
*/
|
|
628
|
+
declare function selectEmotion(animationsObject: Record<string, number> | null | undefined, threshold?: number): EmotionSelectionResult;
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Timing Utilities
|
|
632
|
+
*
|
|
633
|
+
* Pure functions for calculating subtitle timing
|
|
634
|
+
*/
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Get subtitle timing information
|
|
638
|
+
*/
|
|
639
|
+
declare function getSubtitleTiming(subtitle: SubtitleSegment | null): {
|
|
640
|
+
startTime: number;
|
|
641
|
+
endTime: number;
|
|
642
|
+
duration: number;
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* Calculate normalized time (t: 0→1) through subtitle duration
|
|
646
|
+
*/
|
|
647
|
+
declare function calculateNormalizedTime(currentTime: number, startTime: number, duration: number): number;
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Animation Defaults - Visual Styling Layer
|
|
651
|
+
*
|
|
652
|
+
* All subtitle animation VISUAL defaults in one place
|
|
653
|
+
* This is where you edit colors, shadows, font names, and CSS values
|
|
654
|
+
*
|
|
655
|
+
* 🎯 HOW TO CONFIGURE:
|
|
656
|
+
* - Change default font size: Edit ANIMATION_STYLING.fontSize
|
|
657
|
+
* - Change default colors/shadows: Edit ANIMATION_STYLING.textColor, shadow, etc.
|
|
658
|
+
*
|
|
659
|
+
* 🔧 FOR BEHAVIOR/TIMING:
|
|
660
|
+
* - Change when Rakkas (angry) font appears: Edit RENDERING_CONTROL.emotion.angryFontThreshold
|
|
661
|
+
* - Change effect timing/intensity: Edit renderingControl.ts
|
|
662
|
+
*/
|
|
663
|
+
declare const ANIMATION_STYLING: {
|
|
664
|
+
fontSize: number;
|
|
665
|
+
opacity: number;
|
|
666
|
+
weight: number;
|
|
667
|
+
slant: number;
|
|
668
|
+
textColor: string;
|
|
669
|
+
translateX: number;
|
|
670
|
+
translateY: number;
|
|
671
|
+
defaultFont: string;
|
|
672
|
+
angryFont: string;
|
|
673
|
+
joyFont: string;
|
|
674
|
+
sadnessFont: string;
|
|
675
|
+
shadow: {
|
|
676
|
+
offsetX: number;
|
|
677
|
+
offsetY: number;
|
|
678
|
+
blur: number;
|
|
679
|
+
color: string;
|
|
680
|
+
};
|
|
681
|
+
angryFontStyle: {
|
|
682
|
+
color: string;
|
|
683
|
+
strokeColor: string;
|
|
684
|
+
strokeWidth: number;
|
|
685
|
+
shadowOffsetX: number;
|
|
686
|
+
shadowOffsetY: number;
|
|
687
|
+
shadowBlur: number;
|
|
688
|
+
shadowBlurMin: number;
|
|
689
|
+
shadowBlurMax: number;
|
|
690
|
+
shadowOpacityMin: number;
|
|
691
|
+
shadowOpacityMax: number;
|
|
692
|
+
shadowColor: string;
|
|
693
|
+
};
|
|
694
|
+
disgustEmotion: {
|
|
695
|
+
shadowOffsetX: number;
|
|
696
|
+
shadowOffsetY: number;
|
|
697
|
+
shadowBlur: number;
|
|
698
|
+
shadowColor: string;
|
|
699
|
+
};
|
|
700
|
+
transition: {
|
|
701
|
+
duration: number;
|
|
702
|
+
easing: string;
|
|
703
|
+
};
|
|
704
|
+
lineHeight: number;
|
|
705
|
+
letterSpacing: string;
|
|
706
|
+
wordSpacing: {
|
|
707
|
+
min: string;
|
|
708
|
+
max: string;
|
|
709
|
+
};
|
|
710
|
+
fontSpacingAdjustments: {
|
|
711
|
+
'Roboto Flex': {
|
|
712
|
+
letterSpacing: string;
|
|
713
|
+
wordSpacingMultiplier: number;
|
|
714
|
+
};
|
|
715
|
+
Parkinsans: {
|
|
716
|
+
letterSpacing: string;
|
|
717
|
+
wordSpacingMultiplier: number;
|
|
718
|
+
fontSizeOffset: number;
|
|
719
|
+
};
|
|
720
|
+
'Bricolage Grotesque': {
|
|
721
|
+
letterSpacing: string;
|
|
722
|
+
wordSpacingMultiplier: number;
|
|
723
|
+
fontSizeOffset: number;
|
|
724
|
+
};
|
|
725
|
+
Rakkas: {
|
|
726
|
+
letterSpacing: string;
|
|
727
|
+
wordSpacingMultiplier: number;
|
|
728
|
+
};
|
|
729
|
+
Cormorant: {
|
|
730
|
+
letterSpacing: string;
|
|
731
|
+
wordSpacingMultiplier: number;
|
|
732
|
+
};
|
|
733
|
+
};
|
|
734
|
+
position: {
|
|
735
|
+
bottom: string;
|
|
736
|
+
horizontalAlign: string;
|
|
737
|
+
};
|
|
738
|
+
wordAnimation: {
|
|
739
|
+
minDisplayTime: number;
|
|
740
|
+
allowOverlap: boolean;
|
|
741
|
+
};
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Rendering Configuration
|
|
746
|
+
*
|
|
747
|
+
* Central configuration for animation system timing, thresholds, and behavior
|
|
748
|
+
* This is the single source of truth for non-visual animation parameters
|
|
749
|
+
*/
|
|
750
|
+
declare const RENDERING_CONTROL: {
|
|
751
|
+
modes: {
|
|
752
|
+
word: string;
|
|
753
|
+
sentence: string;
|
|
754
|
+
};
|
|
755
|
+
timing: {
|
|
756
|
+
minWordDisplayTime: number;
|
|
757
|
+
breathingRoomDuration: number;
|
|
758
|
+
breathingRoomMinGap: number;
|
|
759
|
+
breathingRoomLargeGap: number;
|
|
760
|
+
breathingRoomSafetyBuffer: number;
|
|
761
|
+
breathingRoomNoNextHold: number;
|
|
762
|
+
fadeOutDuration: number;
|
|
763
|
+
timeRangeTolerance: number;
|
|
764
|
+
};
|
|
765
|
+
emotion: {
|
|
766
|
+
defaultThreshold: number;
|
|
767
|
+
maxThreshold: number;
|
|
768
|
+
angryFontThreshold: number;
|
|
769
|
+
angryEmotions: string[];
|
|
770
|
+
joyFontThreshold: number;
|
|
771
|
+
joyEmotions: string[];
|
|
772
|
+
sadnessFontThreshold: number;
|
|
773
|
+
sadnessEmotions: string[];
|
|
774
|
+
};
|
|
775
|
+
emphasis: {
|
|
776
|
+
minThreshold: number;
|
|
777
|
+
maxThreshold: number;
|
|
778
|
+
scaleMultiplier: number;
|
|
779
|
+
scaleUpFrames: number;
|
|
780
|
+
scaleDownFrames: number;
|
|
781
|
+
fps: number;
|
|
782
|
+
};
|
|
783
|
+
strongEmphasis: {
|
|
784
|
+
minThreshold: number;
|
|
785
|
+
maxScale: number;
|
|
786
|
+
maxRotation: number;
|
|
787
|
+
maxTranslateY: number;
|
|
788
|
+
maxTranslateX: number;
|
|
789
|
+
maxSkew: number;
|
|
790
|
+
maxBrightness: number;
|
|
791
|
+
maxContrast: number;
|
|
792
|
+
maxShadowBlur: number;
|
|
793
|
+
shadowOpacity: number;
|
|
794
|
+
shadowOffsetX: number;
|
|
795
|
+
shadowOffsetY: number;
|
|
796
|
+
maxZIndex: number;
|
|
797
|
+
outwardPushTranslateX: number;
|
|
798
|
+
outwardPushRotation: number;
|
|
799
|
+
outwardPushSkew: number;
|
|
800
|
+
fadeOutTailDuration: number;
|
|
801
|
+
};
|
|
802
|
+
fear: {};
|
|
803
|
+
joy: {
|
|
804
|
+
maxWordsForLetterAnimation: number;
|
|
805
|
+
minWordsForWordAnimation: number;
|
|
806
|
+
letterWave: {
|
|
807
|
+
innerWidthRatio: number;
|
|
808
|
+
outerWidthRatio: number;
|
|
809
|
+
maxScale: number;
|
|
810
|
+
maxRotation: number;
|
|
811
|
+
maxTranslateY: number;
|
|
812
|
+
maxTranslateX: number;
|
|
813
|
+
maxSkew: number;
|
|
814
|
+
maxBrightness: number;
|
|
815
|
+
maxContrast: number;
|
|
816
|
+
maxShadowBlur: number;
|
|
817
|
+
shadowOpacity: number;
|
|
818
|
+
maxZIndex: number;
|
|
819
|
+
};
|
|
820
|
+
letterBulge: {
|
|
821
|
+
innerWidthRatio: number;
|
|
822
|
+
outerWidthRatio: number;
|
|
823
|
+
maxScale: number;
|
|
824
|
+
maxStretchY: number;
|
|
825
|
+
maxCompressionX: number;
|
|
826
|
+
maxTranslateY: number;
|
|
827
|
+
maxTranslateX: number;
|
|
828
|
+
maxSkew: number;
|
|
829
|
+
maxRotation: number;
|
|
830
|
+
maxBrightness: number;
|
|
831
|
+
maxContrast: number;
|
|
832
|
+
maxShadowBlur: number;
|
|
833
|
+
shadowOpacity: number;
|
|
834
|
+
maxZIndex: number;
|
|
835
|
+
};
|
|
836
|
+
wordLevel: {
|
|
837
|
+
duration: number;
|
|
838
|
+
easeType: string;
|
|
839
|
+
maxScale: number;
|
|
840
|
+
maxRotation: number;
|
|
841
|
+
maxTranslateY: number;
|
|
842
|
+
maxTranslateX: number;
|
|
843
|
+
maxSkew: number;
|
|
844
|
+
maxBrightness: number;
|
|
845
|
+
maxContrast: number;
|
|
846
|
+
maxShadowBlur: number;
|
|
847
|
+
shadowOpacity: number;
|
|
848
|
+
maxZIndex: number;
|
|
849
|
+
transitionDuration: number;
|
|
850
|
+
};
|
|
851
|
+
};
|
|
852
|
+
debug: {
|
|
853
|
+
showBorders: boolean;
|
|
854
|
+
showTimings: boolean;
|
|
855
|
+
logRendering: boolean;
|
|
856
|
+
};
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Shadow Generator
|
|
861
|
+
*
|
|
862
|
+
* Generates CSS text-shadow strings from shadow colors
|
|
863
|
+
* All emotion-specific shadows are now controlled by URL animations
|
|
864
|
+
*/
|
|
865
|
+
/**
|
|
866
|
+
* Generate CSS text-shadow string from shadow color
|
|
867
|
+
*
|
|
868
|
+
* @param shadowColor - Shadow color in hex format (e.g., '#ff3232')
|
|
869
|
+
* @param _fontFamily - Unused (kept for API compatibility)
|
|
870
|
+
* @param _emotion - Unused (kept for API compatibility)
|
|
871
|
+
* @param _intensity - Unused (kept for API compatibility)
|
|
872
|
+
* @returns CSS text-shadow value or empty string if no shadow
|
|
873
|
+
*/
|
|
874
|
+
declare function generateTextShadow(shadowColor: string | undefined, _fontFamily?: string, _emotion?: string, _intensity?: number): string;
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Dynamic Word Spacing Calculator
|
|
878
|
+
*
|
|
879
|
+
* Calculates word spacing that grows with animation scale and GTS intensity
|
|
880
|
+
* Ported from app/config/animation/styling.js
|
|
881
|
+
*/
|
|
882
|
+
/**
|
|
883
|
+
* Calculate dynamic word spacing that grows with scale
|
|
884
|
+
*
|
|
885
|
+
* @param currentScale - Current scale value from animation (e.g., 1.0 to 1.5)
|
|
886
|
+
* @param fontFamily - Font family name for spacing adjustments
|
|
887
|
+
* @param gtsIntensity - GTS intensity (0-1) - currently unused for layout preservation
|
|
888
|
+
* @param emotion - Current emotion type (for anger-specific spacing)
|
|
889
|
+
* @returns CSS em value for marginRight
|
|
890
|
+
*/
|
|
891
|
+
declare function calculateDynamicWordSpacing(currentScale?: number, fontFamily?: string, gtsIntensity?: number, emotion?: string): string;
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Prosody Weight — Natural Voice Intonation for Animations
|
|
895
|
+
*
|
|
896
|
+
* Only ~20% of words get motion animation, mimicking how natural speech
|
|
897
|
+
* stresses only a few key words per phrase. Font styling (color, weight,
|
|
898
|
+
* slant) is unaffected — this only gates movement.
|
|
899
|
+
*
|
|
900
|
+
* Selection criteria:
|
|
901
|
+
* 1. Function words (the, a, is, and…) → always quiet
|
|
902
|
+
* 2. Content words are scored by length + deterministic hash
|
|
903
|
+
* 3. Only the top ~35% of content words animate
|
|
904
|
+
* (since function/short words are ~55% of text,
|
|
905
|
+
* 35% of remaining 45% ≈ 16-20% of all words)
|
|
906
|
+
*/
|
|
907
|
+
/**
|
|
908
|
+
* Compute a prosody importance weight for a single word.
|
|
909
|
+
*
|
|
910
|
+
* @returns 0 (no motion) or 1 (full motion).
|
|
911
|
+
*/
|
|
912
|
+
declare function computeProsodyWeight(word: any, _emotionIntensity: number, _gts: number): number;
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Joy Animation Effects
|
|
916
|
+
*
|
|
917
|
+
* Per-letter joy pressure effects for wave and bulge animations
|
|
918
|
+
* Ported from app/utils/animation/joy.js
|
|
919
|
+
*/
|
|
920
|
+
/**
|
|
921
|
+
* Determine which joy animation mode to use based on word count
|
|
922
|
+
*/
|
|
923
|
+
declare function getJoyAnimationMode(wordCount: number): 'letter' | 'letter-bulge' | 'word';
|
|
924
|
+
interface JoyEffect {
|
|
925
|
+
pressure: number;
|
|
926
|
+
scale: number;
|
|
927
|
+
rotation: number;
|
|
928
|
+
translateX: number;
|
|
929
|
+
translateY: number;
|
|
930
|
+
skewX: number;
|
|
931
|
+
stretchY: number;
|
|
932
|
+
stretchX?: number;
|
|
933
|
+
brightness: number;
|
|
934
|
+
contrast: number;
|
|
935
|
+
shadowBlur: number;
|
|
936
|
+
shadowOpacity: number;
|
|
937
|
+
zIndex: number;
|
|
938
|
+
shadowOffsetX?: number;
|
|
939
|
+
shadowOffsetY?: number;
|
|
940
|
+
bulgeIntensity?: number;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Calculate per-letter joy pressure based on word progress and letter position
|
|
944
|
+
*/
|
|
945
|
+
declare function calculateJoy(progress: number, letterIndex: number, totalLetters: number, gtsMultiplier?: number): JoyEffect | null;
|
|
946
|
+
/**
|
|
947
|
+
* Calculate joy bulge effect (for multi-word subtitles)
|
|
948
|
+
*/
|
|
949
|
+
declare function calculateJoyBulge(progress: number, letterIndex: number, totalLetters: number, gtsMultiplier?: number): JoyEffect | null;
|
|
950
|
+
/**
|
|
951
|
+
* Generate CSS transform string from joy effect
|
|
952
|
+
*/
|
|
953
|
+
declare function getJoyTransform(joyEffect: JoyEffect | null): string;
|
|
954
|
+
/**
|
|
955
|
+
* Generate CSS filter string from joy effect
|
|
956
|
+
*/
|
|
957
|
+
declare function getJoyFilter(joyEffect: JoyEffect | null): string;
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Word Emphasis Effects
|
|
961
|
+
*
|
|
962
|
+
* Calculates emphasis effects (standard and strong) for words based on confidence levels.
|
|
963
|
+
*
|
|
964
|
+
* USED BY:
|
|
965
|
+
* - calculateWordAnimation: Main word animation orchestrator
|
|
966
|
+
*
|
|
967
|
+
* HOW IT WORKS:
|
|
968
|
+
* 1. Standard Emphasis (50-70%): Quick "pop" effect at word start
|
|
969
|
+
* 2. Strong Emphasis (70%+): Per-letter dramatic effects throughout word
|
|
970
|
+
* 3. Both scale with GTS (expressiveness level) and word confidence
|
|
971
|
+
*
|
|
972
|
+
* KEY FEATURES:
|
|
973
|
+
* - Confidence-based thresholds (50-70% vs 70%+)
|
|
974
|
+
* - GTS-scaled intensity
|
|
975
|
+
* - Emotion slant inheritance
|
|
976
|
+
* - Smooth transitions
|
|
977
|
+
*/
|
|
978
|
+
interface Word {
|
|
979
|
+
emphasis?: number;
|
|
980
|
+
start: number;
|
|
981
|
+
end: number;
|
|
982
|
+
animationEnd?: number;
|
|
983
|
+
}
|
|
984
|
+
interface EmphasisEffect {
|
|
985
|
+
scaleMultiplier: number;
|
|
986
|
+
slantAdditive: number;
|
|
987
|
+
weightBoost: number;
|
|
988
|
+
scaleProgress: number;
|
|
989
|
+
gtsMultiplier: number;
|
|
990
|
+
emphasis: number;
|
|
991
|
+
emphasisMultiplier: number;
|
|
992
|
+
elapsed: number;
|
|
993
|
+
scaleActive: boolean;
|
|
994
|
+
}
|
|
995
|
+
interface StrongEmphasisData {
|
|
996
|
+
emphasis: number;
|
|
997
|
+
rawEmphasis: number;
|
|
998
|
+
scale: number;
|
|
999
|
+
rotation: number;
|
|
1000
|
+
translateX: number;
|
|
1001
|
+
translateY: number;
|
|
1002
|
+
skewX: number;
|
|
1003
|
+
stretchY: number;
|
|
1004
|
+
brightness: number;
|
|
1005
|
+
contrast: number;
|
|
1006
|
+
shadowBlur: number;
|
|
1007
|
+
shadowOpacity: number;
|
|
1008
|
+
zIndex: number;
|
|
1009
|
+
weightBoost: number;
|
|
1010
|
+
shadowOffsetX: number;
|
|
1011
|
+
shadowOffsetY: number;
|
|
1012
|
+
gtsMultiplier: number;
|
|
1013
|
+
emphasisMultiplier: number;
|
|
1014
|
+
fadeFactor: number;
|
|
1015
|
+
isInTail: boolean;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Calculate emphasis effects for a word
|
|
1019
|
+
*
|
|
1020
|
+
* @param params - Emphasis calculation parameters
|
|
1021
|
+
* @returns Emphasis effects { emphasisEffect, strongEmphasisData }
|
|
1022
|
+
*/
|
|
1023
|
+
declare function calculateWordEmphasisEffects({ word, currentTime, maxExpression, emotionAnimationDuration, wordBaseSlant, isActive, wordEmotion, }: {
|
|
1024
|
+
word: Word;
|
|
1025
|
+
currentTime: number;
|
|
1026
|
+
maxExpression: number;
|
|
1027
|
+
emotionAnimationDuration: number;
|
|
1028
|
+
wordBaseSlant: number;
|
|
1029
|
+
isActive: boolean;
|
|
1030
|
+
wordEmotion?: string;
|
|
1031
|
+
}): {
|
|
1032
|
+
emphasisEffect: EmphasisEffect | null;
|
|
1033
|
+
strongEmphasisData: StrongEmphasisData | null;
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* ============================================
|
|
1038
|
+
* WORD LETTER SPACING
|
|
1039
|
+
* ============================================
|
|
1040
|
+
*
|
|
1041
|
+
* PURPOSE:
|
|
1042
|
+
* Calculates per-letter translateX offsets for GPU-accelerated spacing (no layout reflow).
|
|
1043
|
+
* Extracted from animationEngine for better maintainability.
|
|
1044
|
+
*
|
|
1045
|
+
* USED BY:
|
|
1046
|
+
* - animationEngine: Main word animation orchestrator
|
|
1047
|
+
*
|
|
1048
|
+
* HOW IT WORKS:
|
|
1049
|
+
* 1. Detects active effects (fear shake only - emphasis spacing is disabled)
|
|
1050
|
+
* 2. Calculates per-letter translateX offsets to create spacing between letters
|
|
1051
|
+
* 3. Returns array of translateX values in pixels (one per letter)
|
|
1052
|
+
*
|
|
1053
|
+
* KEY FEATURES:
|
|
1054
|
+
* - Transform-based spacing (GPU-accelerated, no layout reflow)
|
|
1055
|
+
* - Letter spacing disabled for emphasis effects (standard and strong)
|
|
1056
|
+
* - Only fear shake effects get letter spacing (if needed)
|
|
1057
|
+
* - Zero baseline for clean transitions
|
|
1058
|
+
*
|
|
1059
|
+
* PERFORMANCE BENEFITS:
|
|
1060
|
+
* - Uses translateX (compositor property) instead of letter-spacing (layout property)
|
|
1061
|
+
* - Smooth CSS transitions without triggering layout reflow
|
|
1062
|
+
* - All spacing animations run on GPU compositor layer
|
|
1063
|
+
*/
|
|
1064
|
+
interface WordLetterSpacingParams {
|
|
1065
|
+
strongEmphasisData?: any;
|
|
1066
|
+
emphasisEffect?: any;
|
|
1067
|
+
wordLetterShakeIntensity: number | null;
|
|
1068
|
+
wordFontFamily: string;
|
|
1069
|
+
currentTime: number;
|
|
1070
|
+
word: any;
|
|
1071
|
+
emotionAnimationDuration: number;
|
|
1072
|
+
wordEmotion?: string;
|
|
1073
|
+
totalLetters: number;
|
|
1074
|
+
fontSize: number;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Calculate per-letter translateX offsets for GPU-accelerated spacing
|
|
1078
|
+
*
|
|
1079
|
+
* @param params - Spacing calculation parameters
|
|
1080
|
+
* @returns Array of translateX offsets in pixels (one per letter)
|
|
1081
|
+
*/
|
|
1082
|
+
declare function calculateWordLetterSpacing({ strongEmphasisData, emphasisEffect, wordLetterShakeIntensity, wordFontFamily, currentTime, word, emotionAnimationDuration, wordEmotion, totalLetters, fontSize, }: WordLetterSpacingParams): number[];
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* GTS (Global Text Style) Calculations
|
|
1086
|
+
*
|
|
1087
|
+
* Centralized logic for GTS-derived values and emotion threshold calculations
|
|
1088
|
+
*/
|
|
1089
|
+
/**
|
|
1090
|
+
* Combine global GTS slider with optional per-segment / per-word authored gts.
|
|
1091
|
+
*/
|
|
1092
|
+
declare function resolveEffectiveGts(sliderGts: number, segmentGts?: number, wordGts?: number): number;
|
|
1093
|
+
/**
|
|
1094
|
+
* Calculate all GTS-derived values from slider position
|
|
1095
|
+
*
|
|
1096
|
+
* GTS slider range: 0 to 1
|
|
1097
|
+
* - GTS = 0 → maxExpression = 0.2, threshold = 0.7 (conservative)
|
|
1098
|
+
* - GTS = 1 → maxExpression = 1.0, threshold = 0.2 (expressive)
|
|
1099
|
+
*
|
|
1100
|
+
* @param gts - GTS slider value (0-1)
|
|
1101
|
+
* @returns All derived GTS values
|
|
1102
|
+
*/
|
|
1103
|
+
declare function calculateGTSValues(gts: number): {
|
|
1104
|
+
gts: number;
|
|
1105
|
+
maxExpression: number;
|
|
1106
|
+
threshold: number;
|
|
1107
|
+
shouldEnableAngryFont: boolean;
|
|
1108
|
+
};
|
|
1109
|
+
/**
|
|
1110
|
+
* Check if emotion intensity meets the threshold
|
|
1111
|
+
*
|
|
1112
|
+
* @param intensity - Raw emotion intensity from backend (0-1)
|
|
1113
|
+
* @param threshold - GTS-calculated threshold (0-1)
|
|
1114
|
+
* @returns True if intensity is above threshold
|
|
1115
|
+
*/
|
|
1116
|
+
declare function meetsEmotionThreshold(intensity: number, threshold: number): boolean;
|
|
1117
|
+
/**
|
|
1118
|
+
* Calculate scaled emotion intensity based on GTS maxExpression
|
|
1119
|
+
*
|
|
1120
|
+
* GTS acts as a scaling multiplier (final layer) to preserve variance in emotion values
|
|
1121
|
+
* while controlling overall expressiveness.
|
|
1122
|
+
*
|
|
1123
|
+
* When GTS=0.0 (maxExpression=0.2), animations are completely disabled.
|
|
1124
|
+
*
|
|
1125
|
+
* @param rawIntensity - Raw emotion intensity from backend (0-1)
|
|
1126
|
+
* @param maxExpression - GTS-derived max expression multiplier (0.2-1.0)
|
|
1127
|
+
* @returns Scaled emotion intensity (0-1), or 0 if GTS=0.0
|
|
1128
|
+
*/
|
|
1129
|
+
declare function capEmotionIntensity(rawIntensity: number, maxExpression: number): number;
|
|
1130
|
+
/**
|
|
1131
|
+
* Calculate emphasis multiplier within valid emphasis range
|
|
1132
|
+
*
|
|
1133
|
+
* Maps emphasis value within [minThreshold, maxThreshold] to [0, 1]
|
|
1134
|
+
* Used for scaling emphasis effects based on position in range
|
|
1135
|
+
*
|
|
1136
|
+
* @param emphasis - Word emphasis value (0-1)
|
|
1137
|
+
* @returns Emphasis multiplier (0-1), or 0 if outside range
|
|
1138
|
+
*/
|
|
1139
|
+
declare function calculateEmphasisMultiplier(emphasis: number): number;
|
|
1140
|
+
/**
|
|
1141
|
+
* Check if emphasis value is in valid range for emphasis animation
|
|
1142
|
+
*
|
|
1143
|
+
* @param emphasis - Word emphasis value (0-1)
|
|
1144
|
+
* @returns True if in emphasis range [0.5, 0.7)
|
|
1145
|
+
*/
|
|
1146
|
+
declare function isInEmphasisRange(emphasis: number): boolean;
|
|
1147
|
+
/**
|
|
1148
|
+
* Check if emphasis value is in valid range for strong emphasis animation
|
|
1149
|
+
*
|
|
1150
|
+
* @param emphasis - Word emphasis value (0-1)
|
|
1151
|
+
* @returns True if in strong emphasis range [0.7, 1.0]
|
|
1152
|
+
*/
|
|
1153
|
+
declare function isInStrongEmphasisRange(emphasis: number): boolean;
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Font Selection Utilities
|
|
1157
|
+
*
|
|
1158
|
+
* Centralized logic for selecting fonts based on emotion and intensity
|
|
1159
|
+
* Eliminates duplicate font selection code across controllers
|
|
1160
|
+
*/
|
|
1161
|
+
interface Stylesheet {
|
|
1162
|
+
name?: string;
|
|
1163
|
+
fonts?: Record<string, string>;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Select font family based on emotion and GTS intensity
|
|
1167
|
+
*
|
|
1168
|
+
* @param emotion - Emotion name (lowercase, e.g., 'anger', 'joy', 'sadness')
|
|
1169
|
+
* @param maxExpression - GTS-derived max expression (0-1)
|
|
1170
|
+
* @param emotionCategory - Emotion category for children stylesheet (shout, whisper, anger, fear, neutral)
|
|
1171
|
+
* @param stylesheet - Active stylesheet config (optional)
|
|
1172
|
+
* @returns Font family name
|
|
1173
|
+
*/
|
|
1174
|
+
declare function selectFontFamily(emotion: string, maxExpression: number, emotionCategory?: string | null, stylesheet?: Stylesheet | null): string;
|
|
1175
|
+
/**
|
|
1176
|
+
* Get CSS-ready font-family value
|
|
1177
|
+
*
|
|
1178
|
+
* @param emotion - Emotion name (lowercase)
|
|
1179
|
+
* @param maxExpression - GTS-derived max expression (0-1)
|
|
1180
|
+
* @param emotionCategory - Emotion category for children stylesheet
|
|
1181
|
+
* @param stylesheet - Active stylesheet config (optional)
|
|
1182
|
+
* @returns CSS font-family value
|
|
1183
|
+
*/
|
|
1184
|
+
declare function getFontFamilyCSS(emotion: string, maxExpression: number, emotionCategory?: string | null, stylesheet?: Stylesheet | null): string;
|
|
1185
|
+
/**
|
|
1186
|
+
* Check if a given emotion and intensity should use Rakkas (angry) font
|
|
1187
|
+
*
|
|
1188
|
+
* @param emotion - Emotion name (lowercase)
|
|
1189
|
+
* @param maxExpression - GTS-derived max expression (0-1)
|
|
1190
|
+
* @returns True if Rakkas font should be used
|
|
1191
|
+
*/
|
|
1192
|
+
declare function shouldUseAngryFont(emotion: string, maxExpression: number): boolean;
|
|
1193
|
+
/**
|
|
1194
|
+
* Check if a given emotion and intensity should use Parkinsans (joy) font
|
|
1195
|
+
*
|
|
1196
|
+
* @param emotion - Emotion name (lowercase)
|
|
1197
|
+
* @param maxExpression - GTS-derived max expression (0-1)
|
|
1198
|
+
* @returns True if Parkinsans font should be used
|
|
1199
|
+
*/
|
|
1200
|
+
declare function shouldUseJoyFont(emotion: string, maxExpression: number): boolean;
|
|
1201
|
+
/**
|
|
1202
|
+
* Check if a given emotion and intensity should use Cormorant (sadness) font
|
|
1203
|
+
*
|
|
1204
|
+
* @param emotion - Emotion name (lowercase)
|
|
1205
|
+
* @param maxExpression - GTS-derived max expression (0-1)
|
|
1206
|
+
* @returns True if Cormorant font should be used
|
|
1207
|
+
*/
|
|
1208
|
+
declare function shouldUseSadnessFont(emotion: string, maxExpression: number): boolean;
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* Letter Splitting Utilities
|
|
1212
|
+
*
|
|
1213
|
+
* Reusable utilities for splitting text into individual letters for per-letter animations
|
|
1214
|
+
*/
|
|
1215
|
+
interface Letter {
|
|
1216
|
+
char: string;
|
|
1217
|
+
isSpace: boolean;
|
|
1218
|
+
index: number;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Split text into individual letters for per-letter animation
|
|
1222
|
+
*
|
|
1223
|
+
* Preserves spaces as non-breaking spaces to maintain layout.
|
|
1224
|
+
* Each letter gets metadata for animation calculations.
|
|
1225
|
+
*/
|
|
1226
|
+
declare function splitTextIntoLetters(text: string): Letter[];
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Motion blur from scale deviation — matches main app WordRenderer during movement.
|
|
1230
|
+
* Authored `blur` from animation curves is separate.
|
|
1231
|
+
*/
|
|
1232
|
+
declare function getMotionBlurAmount(scale: number, { minDeviation, maxBlur, multiplier }?: {
|
|
1233
|
+
minDeviation?: number | undefined;
|
|
1234
|
+
maxBlur?: number | undefined;
|
|
1235
|
+
multiplier?: number | undefined;
|
|
1236
|
+
}): number;
|
|
1237
|
+
declare function getCombinedBlurAmount(authoredBlur?: number, scale?: number, gts?: number): number;
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Emotion Importance Processing
|
|
1241
|
+
*
|
|
1242
|
+
* Core emotions (anger, fear, sadness, joy) are prioritized over secondary emotions
|
|
1243
|
+
* by multiplying their intensity by 1.5 before selection.
|
|
1244
|
+
* Identical to monorepo app/utils/data/emotionImportance.js
|
|
1245
|
+
*/
|
|
1246
|
+
/**
|
|
1247
|
+
* Process emotions with importance factors
|
|
1248
|
+
*
|
|
1249
|
+
* Core emotions (anger, fear, sadness, joy) get ×1.5 multiplier
|
|
1250
|
+
* Secondary emotions get ×1.0 multiplier
|
|
1251
|
+
*
|
|
1252
|
+
* @param emotions - Array of [emotion, intensity] tuples
|
|
1253
|
+
* @returns Array of [emotion, originalIntensity, adjustedIntensity, importanceFactor, isCore] tuples
|
|
1254
|
+
*/
|
|
1255
|
+
declare function processEmotionsWithImportance(emotions: Array<[string, number]>): Array<[string, number, number, number, boolean]>;
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Backend Data Accessors
|
|
1259
|
+
*
|
|
1260
|
+
* Type-safe helpers for accessing backend subtitle data
|
|
1261
|
+
* Provides consistent interface and handles edge cases
|
|
1262
|
+
* Identical to monorepo app/utils/data/backendDataAccessors.js
|
|
1263
|
+
*/
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* Get word emphasis data
|
|
1267
|
+
*/
|
|
1268
|
+
declare function getWordEmphasis(word: any): {
|
|
1269
|
+
confidence: any;
|
|
1270
|
+
emphasis: any;
|
|
1271
|
+
hasEmphasis: boolean;
|
|
1272
|
+
hasStrongEmphasis: boolean;
|
|
1273
|
+
};
|
|
1274
|
+
/**
|
|
1275
|
+
* Get subtitle text content
|
|
1276
|
+
*
|
|
1277
|
+
* Handles both 'subtitle' and 'text' properties
|
|
1278
|
+
*/
|
|
1279
|
+
declare function getSubtitleText(subtitle: SubtitleSegment | null): string;
|
|
1280
|
+
/**
|
|
1281
|
+
* Get subtitle words array
|
|
1282
|
+
*/
|
|
1283
|
+
declare function getSubtitleWords(subtitle: SubtitleSegment | null): any[];
|
|
1284
|
+
/**
|
|
1285
|
+
* Get subtitle animations/emotions
|
|
1286
|
+
*/
|
|
1287
|
+
declare function getSubtitleAnimations(subtitle: SubtitleSegment | null): Record<string, number>;
|
|
1288
|
+
/**
|
|
1289
|
+
* Check if subtitle has merged subtitles
|
|
1290
|
+
*/
|
|
1291
|
+
declare function hasMergedSubtitles(subtitle: SubtitleSegment | null): boolean;
|
|
1292
|
+
/**
|
|
1293
|
+
* Get merged subtitles array
|
|
1294
|
+
*/
|
|
1295
|
+
declare function getMergedSubtitles(subtitle: SubtitleSegment | null): SubtitleSegment[];
|
|
1296
|
+
/**
|
|
1297
|
+
* Find subtitle containing a specific word based on timing
|
|
1298
|
+
*
|
|
1299
|
+
* Used for merged_subtitles to find which subtitle a word belongs to
|
|
1300
|
+
*/
|
|
1301
|
+
declare function findSubtitleForWord(subtitles: SubtitleSegment[], word: any): SubtitleSegment | null;
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Variable Font Calculations
|
|
1305
|
+
*
|
|
1306
|
+
* Pure functions for calculating font weight/width from volume/speechRate
|
|
1307
|
+
* Extracted from app/utils/font/variableFontCalculations.js
|
|
1308
|
+
*/
|
|
1309
|
+
interface FontAxisConfig {
|
|
1310
|
+
min: number;
|
|
1311
|
+
max: number;
|
|
1312
|
+
default: number;
|
|
1313
|
+
}
|
|
1314
|
+
interface FontConfig {
|
|
1315
|
+
axes?: {
|
|
1316
|
+
weight?: FontAxisConfig;
|
|
1317
|
+
width?: FontAxisConfig;
|
|
1318
|
+
};
|
|
1319
|
+
mapping?: {
|
|
1320
|
+
volume?: {
|
|
1321
|
+
min: number;
|
|
1322
|
+
max: number;
|
|
1323
|
+
};
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Calculate font weight from volume
|
|
1328
|
+
* Maps volume (0-1) to weight range
|
|
1329
|
+
*
|
|
1330
|
+
* @param volume - Volume value (0-1)
|
|
1331
|
+
* @param weightAxis - Weight axis config from font
|
|
1332
|
+
* @param fontConfig - Font configuration object (optional)
|
|
1333
|
+
* @param baseWeight - Optional base weight from URL (if provided, volume variation is applied on top)
|
|
1334
|
+
* @returns Calculated font weight
|
|
1335
|
+
*/
|
|
1336
|
+
declare function calculateWeightFromVolume(volume: number | undefined, weightAxis: FontAxisConfig | undefined, fontConfig?: FontConfig | null, baseWeight?: number): number;
|
|
1337
|
+
/**
|
|
1338
|
+
* Calculate font width from speech rate
|
|
1339
|
+
* Uses inverse relationship: slower speech = wider font
|
|
1340
|
+
* Maps speech rate (0-1) to width range
|
|
1341
|
+
*
|
|
1342
|
+
* @param speechRate - Speech rate value (0-1)
|
|
1343
|
+
* @param widthAxis - Width axis config from font
|
|
1344
|
+
* @param baseWidth - Optional base width from URL (if provided, speechRate variation is applied on top)
|
|
1345
|
+
* @returns Calculated font width
|
|
1346
|
+
*/
|
|
1347
|
+
declare function calculateWidthFromSpeechRate(speechRate: number | undefined, widthAxis: FontAxisConfig | undefined, baseWidth?: number): number;
|
|
1348
|
+
|
|
1349
|
+
declare function ensureFont(fontFamily: string | undefined | null): void;
|
|
1350
|
+
declare function preloadAnimationFonts(): void;
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
* PHONT API Client
|
|
1354
|
+
*
|
|
1355
|
+
* Supports both authentication methods:
|
|
1356
|
+
* - API Keys: For server-side/programmatic access (POST/DELETE /vods, GET /vods/status)
|
|
1357
|
+
* - Session Tokens: For frontend/user access (GET /vods, GET /vods/{id}, GET /vod-subtitles/{id})
|
|
3
1358
|
*
|
|
4
|
-
*
|
|
5
|
-
* Add new exports here as you create new modules.
|
|
1359
|
+
* Tenant isolation is handled automatically by the backend based on credentials.
|
|
6
1360
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1361
|
+
|
|
1362
|
+
interface LoginResponse {
|
|
1363
|
+
tenant_id: string;
|
|
1364
|
+
username: string;
|
|
1365
|
+
email: string;
|
|
1366
|
+
session_id: string;
|
|
1367
|
+
auth_token: string;
|
|
1368
|
+
}
|
|
1369
|
+
interface RegisterResponse {
|
|
1370
|
+
tenant_id: string;
|
|
1371
|
+
username: string;
|
|
1372
|
+
email: string;
|
|
1373
|
+
}
|
|
1374
|
+
interface VodMetadata {
|
|
1375
|
+
_id: string;
|
|
1376
|
+
tenant_id: string;
|
|
1377
|
+
manifest: string;
|
|
1378
|
+
thumbnail?: string;
|
|
1379
|
+
audio_url?: string;
|
|
1380
|
+
name: string;
|
|
1381
|
+
subtitle_available: boolean;
|
|
1382
|
+
created_at: string;
|
|
1383
|
+
}
|
|
1384
|
+
declare class PhontClient {
|
|
1385
|
+
private apiKey?;
|
|
1386
|
+
private authToken?;
|
|
1387
|
+
private sessionId?;
|
|
1388
|
+
readonly apiBaseUrl: string;
|
|
1389
|
+
constructor(options: PhontClientOptions);
|
|
1390
|
+
/**
|
|
1391
|
+
* Get authentication headers based on configured auth method
|
|
1392
|
+
*/
|
|
1393
|
+
getAuthHeaders(): Record<string, string>;
|
|
1394
|
+
/**
|
|
1395
|
+
* Make an authenticated request
|
|
1396
|
+
*/
|
|
1397
|
+
private request;
|
|
1398
|
+
/**
|
|
1399
|
+
* Login with credentials (for frontend/user access)
|
|
1400
|
+
* Returns session_id and auth_token for subsequent requests
|
|
1401
|
+
*/
|
|
1402
|
+
login(identifier: string, password: string): Promise<LoginResponse>;
|
|
1403
|
+
/**
|
|
1404
|
+
* Register a new user
|
|
1405
|
+
*/
|
|
1406
|
+
register(username: string, email: string, password: string, profile: {
|
|
1407
|
+
company: string;
|
|
1408
|
+
first_name: string;
|
|
1409
|
+
last_name: string;
|
|
1410
|
+
}): Promise<RegisterResponse>;
|
|
1411
|
+
/**
|
|
1412
|
+
* Get subtitle track for a VOD
|
|
1413
|
+
* Requires: Session token authentication
|
|
1414
|
+
* @param vodId - VOD ID
|
|
1415
|
+
* @param lang - Language code (optional). If not provided, returns original language subtitles.
|
|
1416
|
+
* Use null/undefined for original, or a valid language code for translations.
|
|
1417
|
+
* @param model - Model name for LLM translation (optional)
|
|
1418
|
+
*/
|
|
1419
|
+
getSubtitleTrack(vodId: string, lang?: string | null, model?: string | null): Promise<any>;
|
|
1420
|
+
/**
|
|
1421
|
+
* Get VOD metadata
|
|
1422
|
+
* Requires: Session token authentication
|
|
1423
|
+
*/
|
|
1424
|
+
getVod(vodId: string): Promise<VodMetadata>;
|
|
1425
|
+
/**
|
|
1426
|
+
* List user's VODs (tenant-isolated)
|
|
1427
|
+
* Requires: Session token authentication
|
|
1428
|
+
*/
|
|
1429
|
+
listVods(skip?: number, limit?: number): Promise<VodMetadata[]>;
|
|
1430
|
+
/**
|
|
1431
|
+
* Create a new VOD
|
|
1432
|
+
* Requires: API key authentication
|
|
1433
|
+
*/
|
|
1434
|
+
createVod(data: {
|
|
1435
|
+
manifest: string;
|
|
1436
|
+
thumbnail?: string;
|
|
1437
|
+
name: string;
|
|
1438
|
+
}): Promise<VodMetadata>;
|
|
1439
|
+
/**
|
|
1440
|
+
* Delete a VOD
|
|
1441
|
+
* Requires: API key authentication
|
|
1442
|
+
*/
|
|
1443
|
+
deleteVod(vodId: string): Promise<void>;
|
|
1444
|
+
/**
|
|
1445
|
+
* Get VOD processing status
|
|
1446
|
+
* Requires: API key authentication
|
|
1447
|
+
*/
|
|
1448
|
+
getVodStatus(vodId: string): Promise<{
|
|
1449
|
+
vod_id: string;
|
|
1450
|
+
status: string;
|
|
1451
|
+
}>;
|
|
1452
|
+
/**
|
|
1453
|
+
* Update credentials (useful after login)
|
|
1454
|
+
*/
|
|
1455
|
+
setCredentials(options: {
|
|
1456
|
+
apiKey?: string;
|
|
1457
|
+
authToken?: string;
|
|
1458
|
+
sessionId?: string;
|
|
1459
|
+
}): void;
|
|
1460
|
+
/**
|
|
1461
|
+
* Clear credentials (for logout)
|
|
1462
|
+
*/
|
|
1463
|
+
clearCredentials(): void;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
export { ANIMATION_STYLING, ANIMATION_URLS, AUTHORED_PLAYBACK_OPTIONS, type AnimationFetcher, type AnimationOptions, type AnimationParameterState, type AnimationParameterStates, type AnimationRecord, type AnimationState, type CurvePoint, EMOTION_ANIMATION_URLS, type Emotion, type EmotionSelectionResult, type EmotionStyle, type EmphasisEffect, type FontAxisConfig, type FontConfig, type JoyEffect, type Letter, type LoginResponse, type ParameterState, type ParameterStates, PhontClient, type PhontClientOptions, type PublishedAnimationResolver, REFERENCE_GTS, REFERENCE_INTENSITY, RENDERING_CONTROL, type RegisterResponse, type SentenceAnimationState, type StateCallback, type StrongEmphasisData, type Stylesheet, type SubtitleEngineState, type SubtitleLine, SubtitleManager, type SubtitleSegment, type SubtitleState$1 as SubtitleState, SubtitleStateManager, type SubtitleTrack, type SubtitleWord, type VodMetadata, type VodState$1 as VodState, type Word, type WordAnimationState, type WordLetterSpacingParams, calculateAnimationParameters, calculateAnimationState, calculateDynamicWordSpacing, calculateEmphasisMultiplier, calculateGTSValues, calculateJoy, calculateJoyBulge, calculateNormalizedTime, calculateWeightFromVolume, calculateWidthFromSpeechRate, calculateWordEmphasisEffects, calculateWordLetterSpacing, capEmotionIntensity, computeProsodyWeight, createPublishedAnimationResolver, decodeAnimationUrl, ensureFont, findSubtitleForWord, generateTextShadow, getAnimationUrlForEmotion, getCombinedBlurAmount, getFontFamilyCSS, getJoyAnimationMode, getJoyFilter, getJoyTransform, getMergedSubtitles, getMotionBlurAmount, getSubtitleAnimations, getSubtitleText, getSubtitleTiming, getSubtitleWords, getWordEmphasis, hasMergedSubtitles, hockeyStickIntensity, isInEmphasisRange, isInStrongEmphasisRange, meetsEmotionThreshold, preloadAnimationFonts, processEmotionsWithImportance, resolveEffectiveGts, sampleParameterCurve, selectEmotion, selectFontFamily, shouldUseAngryFont, shouldUseJoyFont, shouldUseSadnessFont, splitTextIntoLetters, subtitleManager };
|