ogplayer 1.0.4 → 1.1.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/dist/core/license.d.ts +4 -4
- package/dist/core/player.d.ts +29 -1
- package/dist/core/session-tracker.d.ts +28 -0
- package/dist/core/token-fetch.d.ts +15 -0
- package/dist/core/types.d.ts +62 -3
- package/dist/core/vertical-feed.d.ts +12 -1
- package/dist/ogplayer.global.js +25 -26
- package/dist/ogplayer.js +25 -26
- package/dist/ui/og-player.d.ts +5 -1
- package/package.json +1 -2
package/dist/core/license.d.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* by the shared private key unlocks all three platforms. No network, ever.
|
|
5
5
|
*
|
|
6
6
|
* License format: `OGP1.<base64url payload JSON>.<base64url ECDSA sig>`.
|
|
7
|
-
* On the web the `apps` patterns bind to the page HOSTNAME
|
|
8
|
-
*
|
|
9
|
-
* shows the watermark, playback is unaffected.
|
|
7
|
+
* On the web the `apps` patterns bind to the page HOSTNAME: exact match, or
|
|
8
|
+
* `*.example.com` for the domain and all its subdomains. Failure is soft —
|
|
9
|
+
* the caller shows the watermark, playback is unaffected.
|
|
10
10
|
*/
|
|
11
11
|
export type LicenseResult = {
|
|
12
12
|
licensed: true;
|
|
@@ -20,7 +20,7 @@ export type LicenseResult = {
|
|
|
20
20
|
export declare function verifyLicense(licenseKey: string | undefined, hostname: string, publicKeyB64?: string): Promise<LicenseResult>;
|
|
21
21
|
/** DER SEQUENCE { INTEGER r, INTEGER s } → 64-byte raw r||s. */
|
|
22
22
|
export declare function derSignatureToRaw(der: Uint8Array): Uint8Array | null;
|
|
23
|
-
/** Exact match, or
|
|
23
|
+
/** Exact match, or `*.example.com` for the domain and all its subdomains. */
|
|
24
24
|
export declare function licensePatternMatches(host: string, pattern: string): boolean;
|
|
25
25
|
/** Valid through the end of `exp` (YYYY-MM-DD), device local date. */
|
|
26
26
|
export declare function isExpired(exp: string): boolean;
|
package/dist/core/player.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HlsConfig } from "hls.js";
|
|
2
|
-
import { type AudioTrack, type LiveInfo, type OGAnalyticsListener, type OGMediaItem, type PlaybackListener, type PlaybackState, type SubtitleStyle, type TextTrack as OGTextTrack, type ThumbnailFrame, type VideoQuality, type VolumeControlMode } from "./types.js";
|
|
2
|
+
import { type AnalyticsEvent, type AudioTrack, type LiveInfo, type OGAnalyticsListener, type OGMediaItem, type PlaybackListener, type PlaybackState, type SubtitleStyle, type TextTrack as OGTextTrack, type ThumbnailFrame, type VideoQuality, type VolumeControlMode } from "./types.js";
|
|
3
3
|
import type { AdListener } from "../ads/types.js";
|
|
4
4
|
import type { AdsProvider } from "../ads/provider.js";
|
|
5
5
|
export interface OGPlayerOptions {
|
|
@@ -66,6 +66,9 @@ export declare class OGPlayer {
|
|
|
66
66
|
private drmLicenseRequests;
|
|
67
67
|
private thumbnailTrack;
|
|
68
68
|
private lastDroppedTotal;
|
|
69
|
+
private sessionIdValue;
|
|
70
|
+
/** BufferStart reasons + time-to-first-frame (per-load state machine). */
|
|
71
|
+
private sessionTracker;
|
|
69
72
|
/** Saved content position while a content-element ads provider holds the
|
|
70
73
|
* media element (FreeWheel handoff). */
|
|
71
74
|
private breakResume;
|
|
@@ -144,6 +147,11 @@ export declare class OGPlayer {
|
|
|
144
147
|
/** VMAP cue-point times in seconds (negative = postroll). */
|
|
145
148
|
get adCuePoints(): number[];
|
|
146
149
|
get currentLoadGeneration(): number;
|
|
150
|
+
/** Id of the current analytics session — a fresh UUID minted by every
|
|
151
|
+
* `load()`, stamped on each AnalyticsEvent of that load so consumers can
|
|
152
|
+
* key their pipelines without inventing their own ids. Null before the
|
|
153
|
+
* first load. */
|
|
154
|
+
get sessionId(): string | null;
|
|
147
155
|
subtitleStyle: SubtitleStyle;
|
|
148
156
|
subtitleTextScale: number;
|
|
149
157
|
onLicenseChanged(cb: (licensed: boolean) => void): void;
|
|
@@ -154,7 +162,16 @@ export declare class OGPlayer {
|
|
|
154
162
|
addAdListener(l: AdListener): void;
|
|
155
163
|
removeAdListener(l: AdListener): void;
|
|
156
164
|
private dispatch;
|
|
165
|
+
/** Single exit for analytics: every event leaves stamped with the session
|
|
166
|
+
* id and asset URL of the load it belongs to (Android parity). */
|
|
157
167
|
private emit;
|
|
168
|
+
/** @internal Feeds a view-layer analytics event into this player's stream,
|
|
169
|
+
* session-stamped like every other event. Called by the SDK's own elements
|
|
170
|
+
* (`<og-player>`) — not host-app API (the web `emitViewAnalytics` of
|
|
171
|
+
* Android's internal hook; kept off the docs surface via `@internal`). */
|
|
172
|
+
emitViewAnalytics(e: Extract<AnalyticsEvent, {
|
|
173
|
+
type: "FullscreenChanged" | "OrientationChanged";
|
|
174
|
+
}>): void;
|
|
158
175
|
/** The queue set by loadPlaylist() — empty when a single item was loaded. */
|
|
159
176
|
private playlistItems;
|
|
160
177
|
private playlistPosition;
|
|
@@ -212,6 +229,17 @@ export declare class OGPlayer {
|
|
|
212
229
|
*/
|
|
213
230
|
retry(): void;
|
|
214
231
|
private attachSource;
|
|
232
|
+
/**
|
|
233
|
+
* Emits PlaybackStarted { startupMs } — load() to the load's first
|
|
234
|
+
* rendered content frame (time-to-first-frame). requestVideoFrameCallback
|
|
235
|
+
* (Chrome/Safari) reports the actual composited frame; browsers without it
|
|
236
|
+
* (Firefox) fall back to 'loadeddata' — there the first frame is decoded
|
|
237
|
+
* and paintable, which lands a paint or two before true presentation but
|
|
238
|
+
* is the closest cross-browser signal. Armed from attachSource so a
|
|
239
|
+
* content restore after a content-element ad break (FreeWheel handoff)
|
|
240
|
+
* re-arms it; the tracker's once-per-load latch dedupes.
|
|
241
|
+
*/
|
|
242
|
+
private armFirstFrame;
|
|
215
243
|
private adSessionKey;
|
|
216
244
|
private beginAdsIfNeeded;
|
|
217
245
|
private startAdsSession;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BufferReason } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pure per-load state behind two analytics facts: why a buffer started
|
|
4
|
+
* (`BufferReason`) and time-to-first-frame. Reset by every load; driven by
|
|
5
|
+
* the media-element events the player receives ('seeking', 'seeked',
|
|
6
|
+
* 'canplay'/'playing'). Pure logic — unit-tested in node without a DOM.
|
|
7
|
+
* The web port of Android's `PlaybackSessionTracker` (the reference).
|
|
8
|
+
*/
|
|
9
|
+
export declare class PlaybackSessionTracker {
|
|
10
|
+
private readySeen;
|
|
11
|
+
private seekInFlight;
|
|
12
|
+
private loadStartMs;
|
|
13
|
+
private firstFrameEmitted;
|
|
14
|
+
onNewLoad(nowMs: number): void;
|
|
15
|
+
/** 'seeking' fired — buffering that follows is seek-caused. */
|
|
16
|
+
onSeek(): void;
|
|
17
|
+
/** 'seeked' fired — the seek is no longer in flight. */
|
|
18
|
+
onSeekCompleted(): void;
|
|
19
|
+
/** 'canplay'/'playing' — the load has been ready at least once. */
|
|
20
|
+
onReady(): void;
|
|
21
|
+
/** Reason for a buffering transition happening right now. */
|
|
22
|
+
bufferReason(): BufferReason;
|
|
23
|
+
/**
|
|
24
|
+
* Startup time when this is the load's first rendered frame, null on
|
|
25
|
+
* every later frame (and before any load).
|
|
26
|
+
*/
|
|
27
|
+
takeFirstFrameStartupMs(nowMs: number): number | null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DrmTokenProvider, DrmTokenRequest } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fenced DRM token fetch — the web counterpart of the Android SDK's
|
|
4
|
+
* `OGMediaDrmCallback.fetchToken()`: a hung token service must never hold
|
|
5
|
+
* playback forever. Bounded attempts with a per-attempt timeout and a
|
|
6
|
+
* progressive backoff; the last failure propagates so callers map it to
|
|
7
|
+
* DRM_TOKEN_FETCH_FAILED (4002). Same 3 × 8 s fence on all platforms.
|
|
8
|
+
*/
|
|
9
|
+
export declare const TOKEN_ATTEMPTS = 3;
|
|
10
|
+
export declare const TOKEN_TIMEOUT_MS = 8000;
|
|
11
|
+
/** Whole-call deadline for license/certificate HTTP requests — the web
|
|
12
|
+
* mirror of Android's `boundedHttpClient` callTimeout and iOS's
|
|
13
|
+
* `timeoutIntervalForResource` (15 s on every platform). */
|
|
14
|
+
export declare const LICENSE_CALL_TIMEOUT_MS = 15000;
|
|
15
|
+
export declare function fetchTokenHeaders(provider: DrmTokenProvider, request: DrmTokenRequest, timeoutMs?: number): Promise<Record<string, string>>;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -154,7 +154,21 @@ export interface ThumbnailFrame {
|
|
|
154
154
|
width: number;
|
|
155
155
|
height: number;
|
|
156
156
|
}
|
|
157
|
-
|
|
157
|
+
/** Why a BufferStart happened — rebuffer-ratio contracts need initial and
|
|
158
|
+
* seek buffering excluded, so the reason is explicit instead of inferred
|
|
159
|
+
* from event order. */
|
|
160
|
+
export type BufferReason = "INITIAL" | "SEEK" | "REBUFFER";
|
|
161
|
+
/** Physical device orientation, as reported by the view layer. */
|
|
162
|
+
export type DeviceOrientation = "PORTRAIT" | "LANDSCAPE";
|
|
163
|
+
/** Fields stamped on every analytics event: the per-load session id (a
|
|
164
|
+
* fresh UUID minted by every `load()`) and the item URL, so analytics
|
|
165
|
+
* pipelines can key events without inventing their own ids. Absent only
|
|
166
|
+
* on events emitted before the first load. */
|
|
167
|
+
export interface AnalyticsEventBase {
|
|
168
|
+
sessionId?: string;
|
|
169
|
+
assetUrl?: string;
|
|
170
|
+
}
|
|
171
|
+
export type AnalyticsEvent = AnalyticsEventBase & ({
|
|
158
172
|
type: "Play";
|
|
159
173
|
} | {
|
|
160
174
|
type: "Pause";
|
|
@@ -162,8 +176,16 @@ export type AnalyticsEvent = {
|
|
|
162
176
|
type: "Seek";
|
|
163
177
|
fromMs: number;
|
|
164
178
|
toMs: number;
|
|
179
|
+
}
|
|
180
|
+
/** First video frame of a load rendered; `startupMs` is wall time from
|
|
181
|
+
* `load()` to that frame — time-to-first-frame, the industry's primary
|
|
182
|
+
* startup metric. Emitted once per load. */
|
|
183
|
+
| {
|
|
184
|
+
type: "PlaybackStarted";
|
|
185
|
+
startupMs: number;
|
|
165
186
|
} | {
|
|
166
187
|
type: "BufferStart";
|
|
188
|
+
reason: BufferReason;
|
|
167
189
|
} | {
|
|
168
190
|
type: "BufferEnd";
|
|
169
191
|
} | {
|
|
@@ -172,16 +194,53 @@ export type AnalyticsEvent = {
|
|
|
172
194
|
width: number;
|
|
173
195
|
height: number;
|
|
174
196
|
frameRate: number;
|
|
175
|
-
}
|
|
197
|
+
}
|
|
198
|
+
/** Periodic playback-quality sample, every 10s while playing.
|
|
199
|
+
* `liveLatencyMs`/`atLiveEdge` are populated for live streams only. */
|
|
200
|
+
| {
|
|
176
201
|
type: "QualitySnapshot";
|
|
177
202
|
positionMs: number;
|
|
178
203
|
bufferedMs: number;
|
|
179
204
|
bandwidthEstimateBps: number;
|
|
180
205
|
droppedFramesTotal: number;
|
|
206
|
+
liveLatencyMs?: number;
|
|
207
|
+
atLiveEdge?: boolean;
|
|
181
208
|
} | {
|
|
182
209
|
type: "DroppedFrames";
|
|
183
210
|
count: number;
|
|
184
211
|
elapsedMs: number;
|
|
212
|
+
}
|
|
213
|
+
/** The viewer changed volume or mute — the muted-to-sound conversion
|
|
214
|
+
* metric for autoplay/ad measurement. */
|
|
215
|
+
| {
|
|
216
|
+
type: "VolumeChanged";
|
|
217
|
+
volume: number;
|
|
218
|
+
muted: boolean;
|
|
219
|
+
}
|
|
220
|
+
/** A text track was selected (`id` null = subtitles off). */
|
|
221
|
+
| {
|
|
222
|
+
type: "TextTrackChanged";
|
|
223
|
+
id: string | null;
|
|
224
|
+
language: string | null;
|
|
225
|
+
}
|
|
226
|
+
/** An audio track was selected. */
|
|
227
|
+
| {
|
|
228
|
+
type: "AudioTrackChanged";
|
|
229
|
+
id: string;
|
|
230
|
+
language: string | null;
|
|
231
|
+
}
|
|
232
|
+
/** The player entered or left fullscreen (reported by the view layer;
|
|
233
|
+
* headless integrations do not emit it). */
|
|
234
|
+
| {
|
|
235
|
+
type: "FullscreenChanged";
|
|
236
|
+
isFullscreen: boolean;
|
|
237
|
+
}
|
|
238
|
+
/** The device rotated (reported by the view layer once on attach with the
|
|
239
|
+
* initial value, then on change). The vertical feed does not emit it —
|
|
240
|
+
* the feed is portrait by design. */
|
|
241
|
+
| {
|
|
242
|
+
type: "OrientationChanged";
|
|
243
|
+
orientation: DeviceOrientation;
|
|
185
244
|
} | {
|
|
186
245
|
type: "DrmKeysLoaded";
|
|
187
246
|
} | {
|
|
@@ -205,7 +264,7 @@ export type AnalyticsEvent = {
|
|
|
205
264
|
} | {
|
|
206
265
|
type: "Error";
|
|
207
266
|
error: OGPlayerError;
|
|
208
|
-
};
|
|
267
|
+
});
|
|
209
268
|
export type OGAnalyticsListener = (event: AnalyticsEvent) => void;
|
|
210
269
|
/** Error taxonomy — same numeric codes as Android/iOS. */
|
|
211
270
|
export declare const ErrorCodes: {
|
|
@@ -104,12 +104,23 @@ export declare class VerticalFeedPlayerPool {
|
|
|
104
104
|
private activeIndexValue;
|
|
105
105
|
private released;
|
|
106
106
|
private feedMuted;
|
|
107
|
-
|
|
107
|
+
/**
|
|
108
|
+
* ITEMS rejected by the max-duration guard; the feed auto-skips them.
|
|
109
|
+
* Keyed by the item object (not its index): positions shift on every
|
|
110
|
+
* `updateItems` refresh, and index-keyed positional skips would
|
|
111
|
+
* permanently poison whatever lands on those positions in refreshed
|
|
112
|
+
* lists. A refresh that rebuilds the item objects naturally clears
|
|
113
|
+
* their skips (the too-long stream is then re-discovered and re-skipped
|
|
114
|
+
* on first ready); reordering a stable array keeps them.
|
|
115
|
+
*/
|
|
116
|
+
private skippedItems;
|
|
108
117
|
private licensedValue;
|
|
109
118
|
constructor(config: OGVerticalFeedConfig, callbacks: VerticalFeedPoolCallbacks);
|
|
110
119
|
get activeIndex(): number;
|
|
111
120
|
get isLicensed(): boolean | null;
|
|
121
|
+
/** Indices of the CURRENT items array whose item is skip-listed. */
|
|
112
122
|
get skippedIndices(): ReadonlySet<number>;
|
|
123
|
+
private isSkipped;
|
|
113
124
|
updateItems(items: OGVerticalFeedItem[]): void;
|
|
114
125
|
playerAt(index: number): OGPlayer | null;
|
|
115
126
|
setFeedMuted(muted: boolean): void;
|