@phont-ai/subtitles-core 0.1.10 → 0.2.1
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 +501 -407
- package/dist/index.d.ts +1437 -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/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @phont/subtitles-core
|
|
2
|
+
|
|
3
|
+
Core subtitle engine for PHONT expressive subtitles. Pure TypeScript, framework-agnostic.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package contains the core logic for:
|
|
8
|
+
- Subtitle timing and synchronization
|
|
9
|
+
- Emotion detection and style mapping
|
|
10
|
+
- Word-by-word karaoke mode
|
|
11
|
+
- API client for fetching subtitle data
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createSubtitleEngine, SubtitleTrack } from '@phont/subtitles-core';
|
|
17
|
+
|
|
18
|
+
const track: SubtitleTrack = {
|
|
19
|
+
vodId: 'video-123',
|
|
20
|
+
language: 'en',
|
|
21
|
+
lines: [/* ... */]
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const engine = createSubtitleEngine(track);
|
|
25
|
+
const state = engine.at(42.5); // Get subtitle state at 42.5 seconds
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Development
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Build
|
|
32
|
+
pnpm build
|
|
33
|
+
|
|
34
|
+
# Watch mode
|
|
35
|
+
pnpm dev
|
|
36
|
+
|
|
37
|
+
# Test
|
|
38
|
+
pnpm test
|
|
39
|
+
|
|
40
|
+
# Type check
|
|
41
|
+
pnpm typecheck
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Extraction Plan
|
|
45
|
+
|
|
46
|
+
This package will be created by extracting logic from:
|
|
47
|
+
- `app/hooks/subtitle/useSentenceAnimation.js` → `src/engine.ts`
|
|
48
|
+
- `app/hooks/subtitle/useWordAnimation.js` → `src/engine.ts`
|
|
49
|
+
- `app/config/animation/` → `src/emotionMapper.ts`
|
|
50
|
+
- `app/components/subtitle/data/SubtitleFetcher.js` → `src/client.ts`
|
|
51
|
+
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
interface AttachOptions {
|
|
2
|
+
fontSize?: string;
|
|
3
|
+
}
|
|
4
|
+
interface PhontInstance {
|
|
5
|
+
overlay: HTMLDivElement;
|
|
6
|
+
wordsContainer: HTMLSpanElement;
|
|
7
|
+
destroy: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare function attach(videoSelector: string | HTMLVideoElement, options?: AttachOptions): PhontInstance | null;
|
|
10
|
+
declare function loadMetadataTrack(video: HTMLVideoElement, vttUrl: string): HTMLTrackElement;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Animation Types
|
|
14
|
+
*
|
|
15
|
+
* Type definitions for animation parameter states, curves, and animation state
|
|
16
|
+
*/
|
|
17
|
+
interface CurvePoint {
|
|
18
|
+
time: number;
|
|
19
|
+
value: number | string;
|
|
20
|
+
}
|
|
21
|
+
interface ParameterState {
|
|
22
|
+
points: CurvePoint[];
|
|
23
|
+
currentValue?: number | string;
|
|
24
|
+
}
|
|
25
|
+
interface ParameterStates {
|
|
26
|
+
[key: string]: ParameterState;
|
|
27
|
+
}
|
|
28
|
+
interface AnimationParameterStates {
|
|
29
|
+
parameterStates: ParameterStates;
|
|
30
|
+
duration?: number;
|
|
31
|
+
format?: 'normalized' | 'absolute';
|
|
32
|
+
fontData?: {
|
|
33
|
+
style?: {
|
|
34
|
+
fontFamily?: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
config?: unknown;
|
|
38
|
+
fontId?: string | null;
|
|
39
|
+
fontName?: string | null;
|
|
40
|
+
};
|
|
41
|
+
text?: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* URL Decoder for Animation Parameters
|
|
47
|
+
*
|
|
48
|
+
* Extracted from app/playground/utils/urlEncoding.js
|
|
49
|
+
* Decodes animation URLs into parameter states
|
|
50
|
+
*
|
|
51
|
+
* Supports both full URLs and query strings:
|
|
52
|
+
* - Full URL: https://stage-dev.phont.ai/playground/shared?name=...&scale=0,1|1,1.2
|
|
53
|
+
* - Query string: ?name=...&scale=0,1|1,1.2
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Decode animation data from URL or query string
|
|
58
|
+
* @param urlOrQuery - Full URL (https://...) or query string (?name=... or name=...)
|
|
59
|
+
* @returns Decoded animation data object with parameterStates
|
|
60
|
+
*/
|
|
61
|
+
declare function decodeAnimationUrl(urlOrQuery: string): AnimationParameterStates;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Animation parameter calculation -- the unified rendering pipeline.
|
|
65
|
+
* TypeScript port of app/utils/animation/animationParameters.js.
|
|
66
|
+
*
|
|
67
|
+
* Samples authored URL curves with linear interpolation, then scales deviations
|
|
68
|
+
* proportionally from a reference point (REFERENCE_INTENSITY × REFERENCE_GTS).
|
|
69
|
+
* At the reference point the authored curve is rendered 1:1.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
interface AnimationParameterState {
|
|
73
|
+
rawIntensity: number;
|
|
74
|
+
scaleFactor: number;
|
|
75
|
+
scale: number;
|
|
76
|
+
slant: number;
|
|
77
|
+
stretchY: number;
|
|
78
|
+
translateX: number;
|
|
79
|
+
translateY: number;
|
|
80
|
+
weight: number;
|
|
81
|
+
width: number;
|
|
82
|
+
fontFamily: string;
|
|
83
|
+
textColor: string;
|
|
84
|
+
opacity: number;
|
|
85
|
+
textShadow: string;
|
|
86
|
+
blur: number;
|
|
87
|
+
outlineWidth: number;
|
|
88
|
+
outlineColor: string;
|
|
89
|
+
outline2Width?: number;
|
|
90
|
+
outline2Color?: string;
|
|
91
|
+
outline3Width?: number;
|
|
92
|
+
outline3Color?: string;
|
|
93
|
+
outlineShadowWidth?: number;
|
|
94
|
+
outlineShadowColor?: string;
|
|
95
|
+
outlineShadowBlur?: number;
|
|
96
|
+
glowIntensity?: number;
|
|
97
|
+
glowColor?: string;
|
|
98
|
+
snakeWave?: number;
|
|
99
|
+
karaoke?: number;
|
|
100
|
+
stretchX?: number;
|
|
101
|
+
letterSpacing: string;
|
|
102
|
+
fontVariationAxes: Record<string, number>;
|
|
103
|
+
text: string;
|
|
104
|
+
joy: number;
|
|
105
|
+
fear: number;
|
|
106
|
+
standardEmphasis: number;
|
|
107
|
+
strongEmphasis: number;
|
|
108
|
+
joyWaveProgress: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sample all animation parameters at time `t` with proportional intensity scaling.
|
|
112
|
+
*
|
|
113
|
+
* The authored curve represents the animation at REFERENCE_INTENSITY × REFERENCE_GTS.
|
|
114
|
+
* At those values you see exactly what was authored. Other intensity × GTS values
|
|
115
|
+
* scale linearly from there.
|
|
116
|
+
*/
|
|
117
|
+
declare function calculateAnimationParameters(animationData: AnimationParameterStates | null, rawIntensity: number, t: number, gts: number, subtitleDuration?: number | null, { rawMode }?: {
|
|
118
|
+
rawMode?: boolean;
|
|
119
|
+
}): AnimationParameterState | null;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Phont Animated Subtitles — Browser SDK
|
|
123
|
+
*
|
|
124
|
+
* Built from @phont-ai/subtitles-core. A lightweight script that renders
|
|
125
|
+
* Phont animated subtitles on top of any HTML5 <video> element.
|
|
126
|
+
*
|
|
127
|
+
* Data source: a WebVTT metadata track where each cue payload is JSON:
|
|
128
|
+
* { "text": "...", "animation_url": "https://...shared?...", "words": [...] }
|
|
129
|
+
*
|
|
130
|
+
* Usage (script tag):
|
|
131
|
+
* <video id="v" src="video.mp4">
|
|
132
|
+
* <track kind="metadata" src="phont-animations.vtt" default>
|
|
133
|
+
* </video>
|
|
134
|
+
* <script src="phont-subtitles.js"></script>
|
|
135
|
+
* <script>PhontSubtitles.attach('#v');</script>
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
declare const version = "0.3.0";
|
|
139
|
+
|
|
140
|
+
export { type AttachOptions, type PhontInstance, attach, decodeAnimationUrl, loadMetadataTrack, calculateAnimationParameters as sampleAt, version };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
interface AttachOptions {
|
|
2
|
+
fontSize?: string;
|
|
3
|
+
}
|
|
4
|
+
interface PhontInstance {
|
|
5
|
+
overlay: HTMLDivElement;
|
|
6
|
+
wordsContainer: HTMLSpanElement;
|
|
7
|
+
destroy: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare function attach(videoSelector: string | HTMLVideoElement, options?: AttachOptions): PhontInstance | null;
|
|
10
|
+
declare function loadMetadataTrack(video: HTMLVideoElement, vttUrl: string): HTMLTrackElement;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Animation Types
|
|
14
|
+
*
|
|
15
|
+
* Type definitions for animation parameter states, curves, and animation state
|
|
16
|
+
*/
|
|
17
|
+
interface CurvePoint {
|
|
18
|
+
time: number;
|
|
19
|
+
value: number | string;
|
|
20
|
+
}
|
|
21
|
+
interface ParameterState {
|
|
22
|
+
points: CurvePoint[];
|
|
23
|
+
currentValue?: number | string;
|
|
24
|
+
}
|
|
25
|
+
interface ParameterStates {
|
|
26
|
+
[key: string]: ParameterState;
|
|
27
|
+
}
|
|
28
|
+
interface AnimationParameterStates {
|
|
29
|
+
parameterStates: ParameterStates;
|
|
30
|
+
duration?: number;
|
|
31
|
+
format?: 'normalized' | 'absolute';
|
|
32
|
+
fontData?: {
|
|
33
|
+
style?: {
|
|
34
|
+
fontFamily?: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
config?: unknown;
|
|
38
|
+
fontId?: string | null;
|
|
39
|
+
fontName?: string | null;
|
|
40
|
+
};
|
|
41
|
+
text?: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* URL Decoder for Animation Parameters
|
|
47
|
+
*
|
|
48
|
+
* Extracted from app/playground/utils/urlEncoding.js
|
|
49
|
+
* Decodes animation URLs into parameter states
|
|
50
|
+
*
|
|
51
|
+
* Supports both full URLs and query strings:
|
|
52
|
+
* - Full URL: https://stage-dev.phont.ai/playground/shared?name=...&scale=0,1|1,1.2
|
|
53
|
+
* - Query string: ?name=...&scale=0,1|1,1.2
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Decode animation data from URL or query string
|
|
58
|
+
* @param urlOrQuery - Full URL (https://...) or query string (?name=... or name=...)
|
|
59
|
+
* @returns Decoded animation data object with parameterStates
|
|
60
|
+
*/
|
|
61
|
+
declare function decodeAnimationUrl(urlOrQuery: string): AnimationParameterStates;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Animation parameter calculation -- the unified rendering pipeline.
|
|
65
|
+
* TypeScript port of app/utils/animation/animationParameters.js.
|
|
66
|
+
*
|
|
67
|
+
* Samples authored URL curves with linear interpolation, then scales deviations
|
|
68
|
+
* proportionally from a reference point (REFERENCE_INTENSITY × REFERENCE_GTS).
|
|
69
|
+
* At the reference point the authored curve is rendered 1:1.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
interface AnimationParameterState {
|
|
73
|
+
rawIntensity: number;
|
|
74
|
+
scaleFactor: number;
|
|
75
|
+
scale: number;
|
|
76
|
+
slant: number;
|
|
77
|
+
stretchY: number;
|
|
78
|
+
translateX: number;
|
|
79
|
+
translateY: number;
|
|
80
|
+
weight: number;
|
|
81
|
+
width: number;
|
|
82
|
+
fontFamily: string;
|
|
83
|
+
textColor: string;
|
|
84
|
+
opacity: number;
|
|
85
|
+
textShadow: string;
|
|
86
|
+
blur: number;
|
|
87
|
+
outlineWidth: number;
|
|
88
|
+
outlineColor: string;
|
|
89
|
+
outline2Width?: number;
|
|
90
|
+
outline2Color?: string;
|
|
91
|
+
outline3Width?: number;
|
|
92
|
+
outline3Color?: string;
|
|
93
|
+
outlineShadowWidth?: number;
|
|
94
|
+
outlineShadowColor?: string;
|
|
95
|
+
outlineShadowBlur?: number;
|
|
96
|
+
glowIntensity?: number;
|
|
97
|
+
glowColor?: string;
|
|
98
|
+
snakeWave?: number;
|
|
99
|
+
karaoke?: number;
|
|
100
|
+
stretchX?: number;
|
|
101
|
+
letterSpacing: string;
|
|
102
|
+
fontVariationAxes: Record<string, number>;
|
|
103
|
+
text: string;
|
|
104
|
+
joy: number;
|
|
105
|
+
fear: number;
|
|
106
|
+
standardEmphasis: number;
|
|
107
|
+
strongEmphasis: number;
|
|
108
|
+
joyWaveProgress: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sample all animation parameters at time `t` with proportional intensity scaling.
|
|
112
|
+
*
|
|
113
|
+
* The authored curve represents the animation at REFERENCE_INTENSITY × REFERENCE_GTS.
|
|
114
|
+
* At those values you see exactly what was authored. Other intensity × GTS values
|
|
115
|
+
* scale linearly from there.
|
|
116
|
+
*/
|
|
117
|
+
declare function calculateAnimationParameters(animationData: AnimationParameterStates | null, rawIntensity: number, t: number, gts: number, subtitleDuration?: number | null, { rawMode }?: {
|
|
118
|
+
rawMode?: boolean;
|
|
119
|
+
}): AnimationParameterState | null;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Phont Animated Subtitles — Browser SDK
|
|
123
|
+
*
|
|
124
|
+
* Built from @phont-ai/subtitles-core. A lightweight script that renders
|
|
125
|
+
* Phont animated subtitles on top of any HTML5 <video> element.
|
|
126
|
+
*
|
|
127
|
+
* Data source: a WebVTT metadata track where each cue payload is JSON:
|
|
128
|
+
* { "text": "...", "animation_url": "https://...shared?...", "words": [...] }
|
|
129
|
+
*
|
|
130
|
+
* Usage (script tag):
|
|
131
|
+
* <video id="v" src="video.mp4">
|
|
132
|
+
* <track kind="metadata" src="phont-animations.vtt" default>
|
|
133
|
+
* </video>
|
|
134
|
+
* <script src="phont-subtitles.js"></script>
|
|
135
|
+
* <script>PhontSubtitles.attach('#v');</script>
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
declare const version = "0.3.0";
|
|
139
|
+
|
|
140
|
+
export { type AttachOptions, type PhontInstance, attach, decodeAnimationUrl, loadMetadataTrack, calculateAnimationParameters as sampleAt, version };
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var it=Object.defineProperty;var $t=Object.getOwnPropertyDescriptor;var Ft=Object.getOwnPropertyNames;var Pt=Object.prototype.hasOwnProperty;var Ht=(t,e)=>{for(var n in e)it(t,n,{get:e[n],enumerable:!0})},It=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ft(e))!Pt.call(t,i)&&i!==n&&it(t,i,{get:()=>e[i],enumerable:!(r=$t(e,i))||r.enumerable});return t};var Rt=t=>It(it({},"__esModule",{value:!0}),t);var Dt={};Ht(Dt,{attach:()=>vt,decodeAnimationUrl:()=>Z,loadMetadataTrack:()=>Tt,sampleAt:()=>tt,version:()=>Xt});module.exports=Rt(Dt);function Nt(t,e=!1){if(!t)return[];try{return t.split("|").map(n=>{let[r,i]=n.split(","),o=e||typeof i=="string"&&(i.startsWith("#")||i.startsWith("%23")),s;return o?s=i.replace(/%23/g,"#"):s=parseFloat(i),{time:parseFloat(r),value:s}})}catch(n){return console.error("Error decoding points:",n),[]}}function zt(t){if(!t)return"";if(t.includes("://"))try{let e=new URL(t);return e.search.startsWith("?")?e.search.slice(1):e.search}catch{let e=t.indexOf("?");return e!==-1?t.substring(e+1):t}return t.startsWith("?")?t.slice(1):t}function Z(t){try{let e=zt(t),n=new URLSearchParams(e),r={parameterStates:{}};if(n.has("name")&&(r.name=n.get("name")||void 0),n.has("user")&&(r.userName=n.get("user")||void 0),n.has("duration")&&(r.duration=parseFloat(n.get("duration")||"0")),n.has("mode")&&(r.animationMode=n.get("mode")||void 0),n.has("text")&&(r.text=n.get("text")||void 0),n.get("format")==="normalized"&&(r.format="normalized"),n.has("font"))try{let o=JSON.parse(n.get("font")||"{}");o&&typeof o=="object"&&(o.style||o.config||o.fontId?r.fontData={style:o.style,config:o.config||null,fontId:o.fontId||null,fontName:o.fontName||null}:r.fontData={style:o,config:null})}catch(o){console.warn("Failed to parse font data:",o)}if(n.has("bg"))try{r.backgroundStyle=JSON.parse(n.get("bg")||"{}")}catch(o){console.warn("Failed to parse background style:",o)}for(let[o,s]of n.entries())if(!["name","user","duration","mode","text","font","bg","v","c","m","p","format"].includes(o)&&(s.includes("|")||s.includes(","))){let l=o==="color"||o==="shadowColor"||o==="outlineColor"||s.includes("%23"),f=Nt(s,l);if(f.length>0){let m=l?"#000000":0;r.parameterStates[o]={points:f,currentValue:f[0]?.value||m}}}return r}catch(e){throw console.error("Error decoding animation URL:",e),new Error("Failed to decode animation URL. The link may be invalid or corrupted.")}}function Q(t){if(typeof t!="string")return{r:0,g:0,b:0};let e=t.replace("#","").padStart(6,"0"),n=/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:{r:0,g:0,b:0}}function ft(t,e,n){let r=i=>{let o=Math.round(Math.max(0,Math.min(255,i))).toString(16);return o.length===1?"0"+o:o};return"#"+r(t)+r(e)+r(n)}function mt(t,e,n){let r=Q(t),i=Q(e),o=Math.max(0,Math.min(1,n)),s=r.r+(i.r-r.r)*o,l=r.g+(i.g-r.g)*o,f=r.b+(i.b-r.b)*o;return ft(s,l,f)}function jt(t,e,n){if(n===0)return t;let r=Q(t),i=Q(e),o=r.r+(i.r-r.r)*n,s=r.g+(i.g-r.g)*n,l=r.b+(i.b-r.b)*n;return ft(o,s,l)}function K(t,e,n,r=!1,i=null){if(!t||t.length===0)return null;let o=r?e:e*n,s=null,l=null;for(let f=0;f<t.length;f++){let m=t[f],c=m.time!==void 0?m.time:Array.isArray(m)?m[0]:0;if(c!==void 0&&c<=o&&(s=m),c!==void 0&&c>=o&&!l){l=m;break}}if(!s&&t[0])return(t[0].value!==void 0?t[0].value:Array.isArray(t[0])?t[0][1]:null)??null;if(!l&&s)return(s.value!==void 0?s.value:Array.isArray(s)?s[1]:null)??null;if(s&&l){let f=s.time!==void 0?s.time??0:Array.isArray(s)?s[0]:0,m=l.time!==void 0?l.time??0:Array.isArray(l)?l[0]:0,c=s.value!==void 0?s.value:Array.isArray(s)?s[1]:null,d=l.value!==void 0?l.value:Array.isArray(l)?l[1]:null;if(f===m||c===null||d===null)return c??null;let a=(o-f)/(m-f),b=i==="color"||i==="shadowColor"||i==="outlineColor"||i==="outline2Color"||i==="outline3Color"||i==="outlineShadowColor"||i==="glowColor";if(b&&(typeof c=="string"||typeof d=="string"))return mt(typeof c=="string"?c:"#000000",typeof d=="string"?d:"#000000",a);if(!b&&(typeof c=="string"||typeof d=="string")){let W=typeof c=="string"&&(c.startsWith("#")||/^[0-9a-f]{6}$/i.test(c)),C=typeof d=="string"&&(d.startsWith("#")||/^[0-9a-f]{6}$/i.test(d));if(W&&C){let E=c.startsWith("#")?c:`#${c}`,T=d.startsWith("#")?d:`#${d}`;return mt(E,T,a)}return d??null}return c+(d-c)*a}return null}var st=.7,Yt=.7;function tt(t,e,n,r,i=null,{rawMode:o=!1}={}){if(!t||!t.parameterStates)return null;let s=o?1:e*r/(st*Yt),l=t.duration||2,m=t.format==="normalized"?n:n*l,c=!0,d=t.parameterStates||{},a=(p,w,v=!0,M=null)=>{let dt=M??s,J=K(d[p]?.points,m,l,c);return J==null?w:v?dt===0?w:w+(J-w)*dt:J},b=a("scale",1),W=a("skew",0),C=a("stretch",1),E=a("translateX",0),T=a("translateY",0),L=a("weight",500),$=a("width",100),I=(t.fontData||{}).style?.fontFamily||"Roboto Flex",V=t.text||"Hello",k=(p,w)=>{let v=K(d[p]?.points,m,l,c,p);if(v==null)return w;let M=typeof v=="string"?v.startsWith("#")?v:`#${v}`:`#${Math.round(v).toString(16).padStart(6,"0")}`;return s===0?w:jt(w,M,s)},X=k("color","#ffffff"),A=a("opacity",1),j=a("shadowX",0),u=a("shadowY",0),h=a("shadowBlur",4),g=a("shadowOpacity",.5),y=k("shadowColor","#000000"),R=a("outlineWidth",0),N=k("outlineColor","#000000"),x=p=>d[p]?.points?.length>0,D=x("stretchX")?a("stretchX",1):void 0,O=x("outline2Width")?a("outline2Width",0):void 0,z=x("outline2Color")?k("outline2Color","#000000"):void 0,_=x("outline3Width")?a("outline3Width",0):void 0,q=x("outline3Color")?k("outline3Color","#000000"):void 0,G=x("outlineShadowWidth")?a("outlineShadowWidth",0):void 0,F=x("outlineShadowColor")?k("outlineShadowColor","#000000"):void 0,Y=x("outlineShadowBlur")?a("outlineShadowBlur",0):void 0,P=x("glowIntensity")?a("glowIntensity",0):void 0,U=x("glowColor")?k("glowColor","#ffffff"):void 0,ut=x("snakeWave")?a("snakeWave",0):void 0,ct=x("karaoke")?a("karaoke",0):void 0,Ct=1-.65*Math.min(1,Math.max(0,r)),kt=a("blur",0,!0,s*Ct),nt=K(d.thinStroke?.points,m,l,c),rt=K(d.ascenderHeight?.points,m,l,c),B={};nt!=null&&typeof nt=="number"&&(B.YOPQ=nt),rt!=null&&typeof rt=="number"&&(B.YTAS=rt),L!=null&&(B.wght=L),$!=null&&(B.wdth=$);let H=[];if(j!==0||u!==0||h!==4||g!==.5){let p=typeof y=="string"?y:"#000000",w=parseInt(p.slice(1,3),16),v=parseInt(p.slice(3,5),16),M=parseInt(p.slice(5,7),16);H.push(`${j}px ${u}px ${h}px rgba(${w}, ${v}, ${M}, ${g})`)}else if(y&&typeof y=="string"&&y!=="#000000"){let p=y.startsWith("#")?y:`#${y}`,w=parseInt(p.slice(1,3),16)||0,v=parseInt(p.slice(3,5),16)||0,M=parseInt(p.slice(5,7),16)||0;H.push(`3px 2px 4px rgba(${w}, ${v}, ${M}, 0.5)`)}if(P!=null&&P>0){let p=typeof U=="string"?U:"#ffffff";H.push(`0 0 ${P}px ${p}`),H.push(`0 0 ${P*2}px ${p}`),H.push(`0 0 ${P*4}px ${p}`)}let Et=H.length>0?H.join(", "):"",ot=0;if(d.joy?.points&&d.joy.points.length>0)for(let p of d.joy.points){let w=Array.isArray(p)?p[1]:p.y??p.value??0;typeof w=="number"&&w>ot&&(ot=w)}let At=Math.min(1,ot*s),Mt=a("fear",0),Wt=a("standardEmphasis",0,!1),Lt=a("strongEmphasis",0,!1),S={rawIntensity:e,scaleFactor:s,scale:b,slant:W,stretchY:C,translateX:E,translateY:T,weight:L,width:$,fontFamily:I,textColor:X,opacity:A,textShadow:Et,blur:kt,outlineWidth:R,outlineColor:N,letterSpacing:`${Math.max(0,(b-1)*.02)}em`,fontVariationAxes:B,text:V,joy:At,fear:Mt,standardEmphasis:Wt,strongEmphasis:Lt,joyWaveProgress:m/l*1.5};return D!==void 0&&(S.stretchX=D),O!==void 0&&(S.outline2Width=O),z!==void 0&&(S.outline2Color=z),_!==void 0&&(S.outline3Width=_),q!==void 0&&(S.outline3Color=q),G!==void 0&&(S.outlineShadowWidth=G),F!==void 0&&(S.outlineShadowColor=F),Y!==void 0&&(S.outlineShadowBlur=Y),P!==void 0&&(S.glowIntensity=P),U!==void 0&&(S.glowColor=U),ut!==void 0&&(S.snakeWave=ut),ct!==void 0&&(S.karaoke=ct),S}var pt={},Bt={"Roboto Flex":"family=Roboto+Flex:opsz,wdth,wght,YOPQ,YTAS@8..144,25..151,100..1000,25..125,649..854&display=swap",Rakkas:"family=Rakkas&display=swap",Parkinsans:"family=Parkinsans:wght@300..800&display=swap","Bricolage Grotesque":"family=Bricolage+Grotesque:opsz,wdth,wght@12..96,75..100,200..800&display=swap",Cormorant:"family=Cormorant:wght@300..700&display=block"};function ht(t){if(!t)return;let e=t.replace(/["']/g,"").split(",")[0].trim();if(!e||e==="sans-serif"||e==="serif"||pt[e]||(pt[e]=!0,typeof document>"u"))return;let n=Bt[e],r=document.createElement("link");r.rel="stylesheet",r.href=n?`https://fonts.googleapis.com/css2?${n}`:`https://fonts.googleapis.com/css2?family=${encodeURIComponent(e)}:wght@100..900&display=swap`,document.head.appendChild(r)}function at(t,e,n){let r=t.split(/\s+/).filter(Boolean);if(r.length===0)return[];let i=n-e,o=r.map(f=>f.length+1),s=o.reduce((f,m)=>f+m,0),l=e;return r.map((f,m)=>{let c=o[m]/s*i,d=Math.round(l*1e3)/1e3;return l+=c,{word:f,start:d,end:Math.round(l*1e3)/1e3}})}function gt(t){let e=t.parentElement;if(e&&e.classList.contains("phont-container"))return e;let n=document.createElement("div");return n.className="phont-container",n.style.cssText="position:relative;display:inline-block;width:100%;line-height:0;background:#000;",t.parentNode.insertBefore(n,t),n.appendChild(t),n}function bt(t){let e=document.createElement("div");return e.className="phont-subtitle-overlay",e.style.cssText="position:absolute;left:0;right:0;bottom:8%;display:flex;justify-content:center;align-items:center;pointer-events:none;z-index:10;padding:0 5%;box-sizing:border-box;",t.appendChild(e),e}function yt(t){let e=document.createElement("span");return e.className="phont-subtitle-text",e.style.cssText="display:inline-flex;flex-wrap:wrap;justify-content:center;align-items:baseline;gap:0;text-align:center;word-spacing:normal;",t.appendChild(e),e}function xt(){let t=document.createElement("style");return t.textContent=".phont-container video::-webkit-media-controls-fullscreen-button{display:none!important}.phont-container:fullscreen{background:#000}.phont-container:fullscreen video{width:100%;height:100%;object-fit:contain}",document.head.appendChild(t),t}function wt(t){let e=document.createElement("button");return e.className="phont-fs-btn",e.setAttribute("aria-label","Fullscreen"),e.innerHTML="⛶",e.style.cssText="position:absolute;top:12px;right:12px;z-index:20;background:rgba(0,0,0,0.55);border:none;color:#fff;font-size:18px;width:34px;height:34px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s;pointer-events:auto;",t.appendChild(e),e}function lt(t,e){t.innerHTML="";let n=[];for(let r=0;r<e.length;r++){let i=document.createElement("span");i.className="phont-word",i.textContent=e[r].word,i.style.cssText="display:inline-block;white-space:pre;will-change:transform,opacity,margin;transform-origin:center bottom;transition:transform 60ms ease-out,opacity 60ms ease-out,filter 60ms ease-out,color 60ms ease-out,margin 60ms ease-out;",t.appendChild(i),n.push(i),r<e.length-1&&t.appendChild(document.createTextNode(" "))}return n}function Vt(t,e,n,r){t.style.fontSize=r,t.style.fontFamily=e.fontFamily,t.style.fontWeight=String(e.weight),t.style.fontStretch=`${e.width}%`,t.style.fontVariationSettings=`"wght" ${e.weight}, "wdth" ${e.width}`,t.style.color=e.textColor,t.style.transform=`skewX(${e.slant}deg) scaleX(${e.stretchX??1}) scaleY(${e.stretchY}) scale(${e.scale}) translate(${e.translateX}px,${e.translateY}px) translateZ(0)`,t.style.textShadow=e.textShadow||"none",t.style.filter=e.blur>0?`blur(${e.blur}px)`:"none",t.style.letterSpacing=e.letterSpacing,e.outlineWidth>0?(t.style.webkitTextStroke=`${e.outlineWidth}px ${e.outlineColor}`,t.style.paintOrder="stroke fill"):(t.style.webkitTextStroke="",t.style.paintOrder=""),t.style.opacity=String(n?e.opacity:Math.min(.25,e.opacity));let i=e.scale>1.01?(e.scale-1)*(t.textContent?.length||1)*.25:0,o=e.outlineWidth>0?e.outlineWidth*.02:0;t.style.marginLeft=i>0?`${i}em`:"",t.style.marginRight=`${.3+i+o}em`}function St(t,e,n){t.style.fontSize=n,t.style.opacity=e?"1":"0.25",t.style.color="#ffffff",t.style.transform="none",t.style.textShadow="1px 1px 3px rgba(0,0,0,0.7)",t.style.fontWeight="500",t.style.fontStretch="100%",t.style.fontVariationSettings='"wght" 500, "wdth" 100',t.style.filter="none",t.style.webkitTextStroke="",t.style.paintOrder="",t.style.letterSpacing="0em",t.style.marginLeft="",t.style.marginRight="0.3em"}function vt(t,e){let n=e||{},r=typeof t=="string"?document.querySelector(t):t;if(!r)return console.error("[PhontSubtitles] Video element not found:",t),null;let i=r,o=gt(i),s=xt(),l=wt(o),f=bt(o),m=yt(f);o.addEventListener("mouseenter",()=>{l.style.opacity="1"}),o.addEventListener("mouseleave",()=>{l.style.opacity="0"}),l.addEventListener("click",()=>{document.fullscreenElement||document.webkitFullscreenElement?(document.exitFullscreen||document.webkitExitFullscreen).call(document):(o.requestFullscreen||o.webkitRequestFullscreen).call(o)});function c(){let u=document.fullscreenElement||document.webkitFullscreenElement;l.innerHTML=u===o?"✖":"⛶"}document.addEventListener("fullscreenchange",c),document.addEventListener("webkitfullscreenchange",c);let d=null,a=null,b=null,W=.7,C=[],E=null,T=null,L=null;function $(){d=null,a=null,b=null,m.innerHTML="",m.style.opacity="0",C=[]}function et(){if(!d||!b||b.length===0){$();return}m.style.opacity="1";let u=i.currentTime,h=n.fontSize||"clamp(1.2rem, 3vw, 2.4rem)";for(let g=0;g<b.length;g++){let y=b[g],R=C[g];if(!R)continue;let N=y.start,x=y.end,D=Math.max(.01,x-N),O=Math.max(0,Math.min(1,(u-N)/D)),z=u>=N,_=u>=N&&u<x,q=u>=x;if(!a){St(R,z,h);continue}let G=y.gts!==void 0?y.gts:W,F;_?F=O:q?F=1:F=0;let Y=tt(a,st,F,G,null);if(!Y){St(R,z,h);continue}Vt(R,Y,z,h)}}function I(){let u=this;if(!u.activeCues||u.activeCues.length===0){$();return}let h=u.activeCues[0];try{let g=JSON.parse(h.text);if(d=h,g.words&&g.words.length>0?b=g.words:b=at(g.text||"",h.startTime,h.endTime),W=typeof g.gts=="number"?g.gts:.7,g.animation_url){a=Z(g.animation_url);let y=a.fontData;ht(y?.style?.fontFamily??y?.fontName??null)}else a=null;C=lt(m,b)}catch{d=h,a=null,b=at(h.text||"",h.startTime,h.endTime),C=lt(m,b)}et()}function V(u){return u.kind!=="metadata"?!1:(u.mode="hidden",u.addEventListener("cuechange",I),T=u,!0)}function k(){if(!T||!T.activeCues)return;let u=T.activeCues;if(u.length===0){d&&$(),L=null;return}let h=u[0],g=`${h.startTime}_${h.endTime}`;g!==L&&(L=g,I.call(T))}function X(){k(),d&&et(),E=requestAnimationFrame(X)}let A=i.textTracks,j=!1;for(let u=0;u<A.length;u++)if(V(A[u])){j=!0;break}if(!j){let u=h=>{h.track&&h.track.kind==="metadata"&&(V(h.track),A.removeEventListener("addtrack",u))};A.addEventListener("addtrack",u)}return E=requestAnimationFrame(X),{overlay:f,wordsContainer:m,destroy(){E&&cancelAnimationFrame(E),T&&T.removeEventListener("cuechange",I);for(let u=0;u<A.length;u++)A[u].removeEventListener("cuechange",I);document.removeEventListener("fullscreenchange",c),document.removeEventListener("webkitfullscreenchange",c),s.parentNode&&s.remove(),l.remove(),f.remove()}}}function Tt(t,e){let n=document.createElement("track");return n.kind="metadata",n.src=e,n.default=!0,t.appendChild(n),n}var Xt="0.3.0";
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function Mt(t,e=!1){if(!t)return[];try{return t.split("|").map(n=>{let[r,i]=n.split(","),o=e||typeof i=="string"&&(i.startsWith("#")||i.startsWith("%23")),s;return o?s=i.replace(/%23/g,"#"):s=parseFloat(i),{time:parseFloat(r),value:s}})}catch(n){return console.error("Error decoding points:",n),[]}}function Wt(t){if(!t)return"";if(t.includes("://"))try{let e=new URL(t);return e.search.startsWith("?")?e.search.slice(1):e.search}catch{let e=t.indexOf("?");return e!==-1?t.substring(e+1):t}return t.startsWith("?")?t.slice(1):t}function rt(t){try{let e=Wt(t),n=new URLSearchParams(e),r={parameterStates:{}};if(n.has("name")&&(r.name=n.get("name")||void 0),n.has("user")&&(r.userName=n.get("user")||void 0),n.has("duration")&&(r.duration=parseFloat(n.get("duration")||"0")),n.has("mode")&&(r.animationMode=n.get("mode")||void 0),n.has("text")&&(r.text=n.get("text")||void 0),n.get("format")==="normalized"&&(r.format="normalized"),n.has("font"))try{let o=JSON.parse(n.get("font")||"{}");o&&typeof o=="object"&&(o.style||o.config||o.fontId?r.fontData={style:o.style,config:o.config||null,fontId:o.fontId||null,fontName:o.fontName||null}:r.fontData={style:o,config:null})}catch(o){console.warn("Failed to parse font data:",o)}if(n.has("bg"))try{r.backgroundStyle=JSON.parse(n.get("bg")||"{}")}catch(o){console.warn("Failed to parse background style:",o)}for(let[o,s]of n.entries())if(!["name","user","duration","mode","text","font","bg","v","c","m","p","format"].includes(o)&&(s.includes("|")||s.includes(","))){let l=o==="color"||o==="shadowColor"||o==="outlineColor"||s.includes("%23"),f=Mt(s,l);if(f.length>0){let m=l?"#000000":0;r.parameterStates[o]={points:f,currentValue:f[0]?.value||m}}}return r}catch(e){throw console.error("Error decoding animation URL:",e),new Error("Failed to decode animation URL. The link may be invalid or corrupted.")}}function K(t){if(typeof t!="string")return{r:0,g:0,b:0};let e=t.replace("#","").padStart(6,"0"),n=/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:{r:0,g:0,b:0}}function mt(t,e,n){let r=i=>{let o=Math.round(Math.max(0,Math.min(255,i))).toString(16);return o.length===1?"0"+o:o};return"#"+r(t)+r(e)+r(n)}function dt(t,e,n){let r=K(t),i=K(e),o=Math.max(0,Math.min(1,n)),s=r.r+(i.r-r.r)*o,l=r.g+(i.g-r.g)*o,f=r.b+(i.b-r.b)*o;return mt(s,l,f)}function Lt(t,e,n){if(n===0)return t;let r=K(t),i=K(e),o=r.r+(i.r-r.r)*n,s=r.g+(i.g-r.g)*n,l=r.b+(i.b-r.b)*n;return mt(o,s,l)}function Z(t,e,n,r=!1,i=null){if(!t||t.length===0)return null;let o=r?e:e*n,s=null,l=null;for(let f=0;f<t.length;f++){let m=t[f],c=m.time!==void 0?m.time:Array.isArray(m)?m[0]:0;if(c!==void 0&&c<=o&&(s=m),c!==void 0&&c>=o&&!l){l=m;break}}if(!s&&t[0])return(t[0].value!==void 0?t[0].value:Array.isArray(t[0])?t[0][1]:null)??null;if(!l&&s)return(s.value!==void 0?s.value:Array.isArray(s)?s[1]:null)??null;if(s&&l){let f=s.time!==void 0?s.time??0:Array.isArray(s)?s[0]:0,m=l.time!==void 0?l.time??0:Array.isArray(l)?l[0]:0,c=s.value!==void 0?s.value:Array.isArray(s)?s[1]:null,d=l.value!==void 0?l.value:Array.isArray(l)?l[1]:null;if(f===m||c===null||d===null)return c??null;let a=(o-f)/(m-f),b=i==="color"||i==="shadowColor"||i==="outlineColor"||i==="outline2Color"||i==="outline3Color"||i==="outlineShadowColor"||i==="glowColor";if(b&&(typeof c=="string"||typeof d=="string"))return dt(typeof c=="string"?c:"#000000",typeof d=="string"?d:"#000000",a);if(!b&&(typeof c=="string"||typeof d=="string")){let W=typeof c=="string"&&(c.startsWith("#")||/^[0-9a-f]{6}$/i.test(c)),C=typeof d=="string"&&(d.startsWith("#")||/^[0-9a-f]{6}$/i.test(d));if(W&&C){let E=c.startsWith("#")?c:`#${c}`,T=d.startsWith("#")?d:`#${d}`;return dt(E,T,a)}return d??null}return c+(d-c)*a}return null}var ot=.7,$t=.7;function it(t,e,n,r,i=null,{rawMode:o=!1}={}){if(!t||!t.parameterStates)return null;let s=o?1:e*r/(ot*$t),l=t.duration||2,m=t.format==="normalized"?n:n*l,c=!0,d=t.parameterStates||{},a=(p,w,v=!0,M=null)=>{let ct=M??s,J=Z(d[p]?.points,m,l,c);return J==null?w:v?ct===0?w:w+(J-w)*ct:J},b=a("scale",1),W=a("skew",0),C=a("stretch",1),E=a("translateX",0),T=a("translateY",0),L=a("weight",500),$=a("width",100),I=(t.fontData||{}).style?.fontFamily||"Roboto Flex",V=t.text||"Hello",k=(p,w)=>{let v=Z(d[p]?.points,m,l,c,p);if(v==null)return w;let M=typeof v=="string"?v.startsWith("#")?v:`#${v}`:`#${Math.round(v).toString(16).padStart(6,"0")}`;return s===0?w:Lt(w,M,s)},X=k("color","#ffffff"),A=a("opacity",1),j=a("shadowX",0),u=a("shadowY",0),h=a("shadowBlur",4),g=a("shadowOpacity",.5),y=k("shadowColor","#000000"),R=a("outlineWidth",0),N=k("outlineColor","#000000"),x=p=>d[p]?.points?.length>0,D=x("stretchX")?a("stretchX",1):void 0,O=x("outline2Width")?a("outline2Width",0):void 0,z=x("outline2Color")?k("outline2Color","#000000"):void 0,_=x("outline3Width")?a("outline3Width",0):void 0,q=x("outline3Color")?k("outline3Color","#000000"):void 0,G=x("outlineShadowWidth")?a("outlineShadowWidth",0):void 0,F=x("outlineShadowColor")?k("outlineShadowColor","#000000"):void 0,Y=x("outlineShadowBlur")?a("outlineShadowBlur",0):void 0,P=x("glowIntensity")?a("glowIntensity",0):void 0,U=x("glowColor")?k("glowColor","#ffffff"):void 0,lt=x("snakeWave")?a("snakeWave",0):void 0,ut=x("karaoke")?a("karaoke",0):void 0,St=1-.65*Math.min(1,Math.max(0,r)),vt=a("blur",0,!0,s*St),tt=Z(d.thinStroke?.points,m,l,c),et=Z(d.ascenderHeight?.points,m,l,c),B={};tt!=null&&typeof tt=="number"&&(B.YOPQ=tt),et!=null&&typeof et=="number"&&(B.YTAS=et),L!=null&&(B.wght=L),$!=null&&(B.wdth=$);let H=[];if(j!==0||u!==0||h!==4||g!==.5){let p=typeof y=="string"?y:"#000000",w=parseInt(p.slice(1,3),16),v=parseInt(p.slice(3,5),16),M=parseInt(p.slice(5,7),16);H.push(`${j}px ${u}px ${h}px rgba(${w}, ${v}, ${M}, ${g})`)}else if(y&&typeof y=="string"&&y!=="#000000"){let p=y.startsWith("#")?y:`#${y}`,w=parseInt(p.slice(1,3),16)||0,v=parseInt(p.slice(3,5),16)||0,M=parseInt(p.slice(5,7),16)||0;H.push(`3px 2px 4px rgba(${w}, ${v}, ${M}, 0.5)`)}if(P!=null&&P>0){let p=typeof U=="string"?U:"#ffffff";H.push(`0 0 ${P}px ${p}`),H.push(`0 0 ${P*2}px ${p}`),H.push(`0 0 ${P*4}px ${p}`)}let Tt=H.length>0?H.join(", "):"",nt=0;if(d.joy?.points&&d.joy.points.length>0)for(let p of d.joy.points){let w=Array.isArray(p)?p[1]:p.y??p.value??0;typeof w=="number"&&w>nt&&(nt=w)}let Ct=Math.min(1,nt*s),kt=a("fear",0),Et=a("standardEmphasis",0,!1),At=a("strongEmphasis",0,!1),S={rawIntensity:e,scaleFactor:s,scale:b,slant:W,stretchY:C,translateX:E,translateY:T,weight:L,width:$,fontFamily:I,textColor:X,opacity:A,textShadow:Tt,blur:vt,outlineWidth:R,outlineColor:N,letterSpacing:`${Math.max(0,(b-1)*.02)}em`,fontVariationAxes:B,text:V,joy:Ct,fear:kt,standardEmphasis:Et,strongEmphasis:At,joyWaveProgress:m/l*1.5};return D!==void 0&&(S.stretchX=D),O!==void 0&&(S.outline2Width=O),z!==void 0&&(S.outline2Color=z),_!==void 0&&(S.outline3Width=_),q!==void 0&&(S.outline3Color=q),G!==void 0&&(S.outlineShadowWidth=G),F!==void 0&&(S.outlineShadowColor=F),Y!==void 0&&(S.outlineShadowBlur=Y),P!==void 0&&(S.glowIntensity=P),U!==void 0&&(S.glowColor=U),lt!==void 0&&(S.snakeWave=lt),ut!==void 0&&(S.karaoke=ut),S}var ft={},Ft={"Roboto Flex":"family=Roboto+Flex:opsz,wdth,wght,YOPQ,YTAS@8..144,25..151,100..1000,25..125,649..854&display=swap",Rakkas:"family=Rakkas&display=swap",Parkinsans:"family=Parkinsans:wght@300..800&display=swap","Bricolage Grotesque":"family=Bricolage+Grotesque:opsz,wdth,wght@12..96,75..100,200..800&display=swap",Cormorant:"family=Cormorant:wght@300..700&display=block"};function pt(t){if(!t)return;let e=t.replace(/["']/g,"").split(",")[0].trim();if(!e||e==="sans-serif"||e==="serif"||ft[e]||(ft[e]=!0,typeof document>"u"))return;let n=Ft[e],r=document.createElement("link");r.rel="stylesheet",r.href=n?`https://fonts.googleapis.com/css2?${n}`:`https://fonts.googleapis.com/css2?family=${encodeURIComponent(e)}:wght@100..900&display=swap`,document.head.appendChild(r)}function st(t,e,n){let r=t.split(/\s+/).filter(Boolean);if(r.length===0)return[];let i=n-e,o=r.map(f=>f.length+1),s=o.reduce((f,m)=>f+m,0),l=e;return r.map((f,m)=>{let c=o[m]/s*i,d=Math.round(l*1e3)/1e3;return l+=c,{word:f,start:d,end:Math.round(l*1e3)/1e3}})}function ht(t){let e=t.parentElement;if(e&&e.classList.contains("phont-container"))return e;let n=document.createElement("div");return n.className="phont-container",n.style.cssText="position:relative;display:inline-block;width:100%;line-height:0;background:#000;",t.parentNode.insertBefore(n,t),n.appendChild(t),n}function gt(t){let e=document.createElement("div");return e.className="phont-subtitle-overlay",e.style.cssText="position:absolute;left:0;right:0;bottom:8%;display:flex;justify-content:center;align-items:center;pointer-events:none;z-index:10;padding:0 5%;box-sizing:border-box;",t.appendChild(e),e}function bt(t){let e=document.createElement("span");return e.className="phont-subtitle-text",e.style.cssText="display:inline-flex;flex-wrap:wrap;justify-content:center;align-items:baseline;gap:0;text-align:center;word-spacing:normal;",t.appendChild(e),e}function yt(){let t=document.createElement("style");return t.textContent=".phont-container video::-webkit-media-controls-fullscreen-button{display:none!important}.phont-container:fullscreen{background:#000}.phont-container:fullscreen video{width:100%;height:100%;object-fit:contain}",document.head.appendChild(t),t}function xt(t){let e=document.createElement("button");return e.className="phont-fs-btn",e.setAttribute("aria-label","Fullscreen"),e.innerHTML="⛶",e.style.cssText="position:absolute;top:12px;right:12px;z-index:20;background:rgba(0,0,0,0.55);border:none;color:#fff;font-size:18px;width:34px;height:34px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s;pointer-events:auto;",t.appendChild(e),e}function at(t,e){t.innerHTML="";let n=[];for(let r=0;r<e.length;r++){let i=document.createElement("span");i.className="phont-word",i.textContent=e[r].word,i.style.cssText="display:inline-block;white-space:pre;will-change:transform,opacity,margin;transform-origin:center bottom;transition:transform 60ms ease-out,opacity 60ms ease-out,filter 60ms ease-out,color 60ms ease-out,margin 60ms ease-out;",t.appendChild(i),n.push(i),r<e.length-1&&t.appendChild(document.createTextNode(" "))}return n}function Pt(t,e,n,r){t.style.fontSize=r,t.style.fontFamily=e.fontFamily,t.style.fontWeight=String(e.weight),t.style.fontStretch=`${e.width}%`,t.style.fontVariationSettings=`"wght" ${e.weight}, "wdth" ${e.width}`,t.style.color=e.textColor,t.style.transform=`skewX(${e.slant}deg) scaleX(${e.stretchX??1}) scaleY(${e.stretchY}) scale(${e.scale}) translate(${e.translateX}px,${e.translateY}px) translateZ(0)`,t.style.textShadow=e.textShadow||"none",t.style.filter=e.blur>0?`blur(${e.blur}px)`:"none",t.style.letterSpacing=e.letterSpacing,e.outlineWidth>0?(t.style.webkitTextStroke=`${e.outlineWidth}px ${e.outlineColor}`,t.style.paintOrder="stroke fill"):(t.style.webkitTextStroke="",t.style.paintOrder=""),t.style.opacity=String(n?e.opacity:Math.min(.25,e.opacity));let i=e.scale>1.01?(e.scale-1)*(t.textContent?.length||1)*.25:0,o=e.outlineWidth>0?e.outlineWidth*.02:0;t.style.marginLeft=i>0?`${i}em`:"",t.style.marginRight=`${.3+i+o}em`}function wt(t,e,n){t.style.fontSize=n,t.style.opacity=e?"1":"0.25",t.style.color="#ffffff",t.style.transform="none",t.style.textShadow="1px 1px 3px rgba(0,0,0,0.7)",t.style.fontWeight="500",t.style.fontStretch="100%",t.style.fontVariationSettings='"wght" 500, "wdth" 100',t.style.filter="none",t.style.webkitTextStroke="",t.style.paintOrder="",t.style.letterSpacing="0em",t.style.marginLeft="",t.style.marginRight="0.3em"}function Ht(t,e){let n=e||{},r=typeof t=="string"?document.querySelector(t):t;if(!r)return console.error("[PhontSubtitles] Video element not found:",t),null;let i=r,o=ht(i),s=yt(),l=xt(o),f=gt(o),m=bt(f);o.addEventListener("mouseenter",()=>{l.style.opacity="1"}),o.addEventListener("mouseleave",()=>{l.style.opacity="0"}),l.addEventListener("click",()=>{document.fullscreenElement||document.webkitFullscreenElement?(document.exitFullscreen||document.webkitExitFullscreen).call(document):(o.requestFullscreen||o.webkitRequestFullscreen).call(o)});function c(){let u=document.fullscreenElement||document.webkitFullscreenElement;l.innerHTML=u===o?"✖":"⛶"}document.addEventListener("fullscreenchange",c),document.addEventListener("webkitfullscreenchange",c);let d=null,a=null,b=null,W=.7,C=[],E=null,T=null,L=null;function $(){d=null,a=null,b=null,m.innerHTML="",m.style.opacity="0",C=[]}function Q(){if(!d||!b||b.length===0){$();return}m.style.opacity="1";let u=i.currentTime,h=n.fontSize||"clamp(1.2rem, 3vw, 2.4rem)";for(let g=0;g<b.length;g++){let y=b[g],R=C[g];if(!R)continue;let N=y.start,x=y.end,D=Math.max(.01,x-N),O=Math.max(0,Math.min(1,(u-N)/D)),z=u>=N,_=u>=N&&u<x,q=u>=x;if(!a){wt(R,z,h);continue}let G=y.gts!==void 0?y.gts:W,F;_?F=O:q?F=1:F=0;let Y=it(a,ot,F,G,null);if(!Y){wt(R,z,h);continue}Pt(R,Y,z,h)}}function I(){let u=this;if(!u.activeCues||u.activeCues.length===0){$();return}let h=u.activeCues[0];try{let g=JSON.parse(h.text);if(d=h,g.words&&g.words.length>0?b=g.words:b=st(g.text||"",h.startTime,h.endTime),W=typeof g.gts=="number"?g.gts:.7,g.animation_url){a=rt(g.animation_url);let y=a.fontData;pt(y?.style?.fontFamily??y?.fontName??null)}else a=null;C=at(m,b)}catch{d=h,a=null,b=st(h.text||"",h.startTime,h.endTime),C=at(m,b)}Q()}function V(u){return u.kind!=="metadata"?!1:(u.mode="hidden",u.addEventListener("cuechange",I),T=u,!0)}function k(){if(!T||!T.activeCues)return;let u=T.activeCues;if(u.length===0){d&&$(),L=null;return}let h=u[0],g=`${h.startTime}_${h.endTime}`;g!==L&&(L=g,I.call(T))}function X(){k(),d&&Q(),E=requestAnimationFrame(X)}let A=i.textTracks,j=!1;for(let u=0;u<A.length;u++)if(V(A[u])){j=!0;break}if(!j){let u=h=>{h.track&&h.track.kind==="metadata"&&(V(h.track),A.removeEventListener("addtrack",u))};A.addEventListener("addtrack",u)}return E=requestAnimationFrame(X),{overlay:f,wordsContainer:m,destroy(){E&&cancelAnimationFrame(E),T&&T.removeEventListener("cuechange",I);for(let u=0;u<A.length;u++)A[u].removeEventListener("cuechange",I);document.removeEventListener("fullscreenchange",c),document.removeEventListener("webkitfullscreenchange",c),s.parentNode&&s.remove(),l.remove(),f.remove()}}}function It(t,e){let n=document.createElement("track");return n.kind="metadata",n.src=e,n.default=!0,t.appendChild(n),n}var Gt="0.3.0";export{Ht as attach,rt as decodeAnimationUrl,It as loadMetadataTrack,it as sampleAt,Gt as version};
|