ogplayer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,182 @@
1
+ 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 { AdListener } from "../ads/types.js";
3
+ import type { AdsProvider } from "../ads/provider.js";
4
+ export interface OGPlayerOptions {
5
+ /** Offline license key (`OGP1.…`); without one the watermark shows. */
6
+ licenseKey?: string;
7
+ /** onProgress cadence, default 250ms (Android DEFAULT_PROGRESS_INTERVAL_MS). */
8
+ progressUpdateIntervalMs?: number;
9
+ /** Seek button increments, default 10s each way. */
10
+ seekForwardIncrementMs?: number;
11
+ seekBackwardIncrementMs?: number;
12
+ }
13
+ export interface LoadOptions {
14
+ startPositionMs?: number;
15
+ autoplay?: boolean;
16
+ }
17
+ /**
18
+ * The OGPlayer playback engine — the web counterpart of the Android
19
+ * `OGPlayer` (the reference platform), wrapping an HTML `<video>` element:
20
+ * HLS via hls.js (MSE) everywhere, natively on Safari, progressive files
21
+ * directly. UI is `<og-player>` from the `ui` module; the engine also works
22
+ * fully headless.
23
+ */
24
+ export declare class OGPlayer {
25
+ readonly videoElement: HTMLVideoElement;
26
+ private hls;
27
+ private item;
28
+ private stateValue;
29
+ private listeners;
30
+ private analyticsListeners;
31
+ private adListeners;
32
+ private progressTimer;
33
+ private snapshotTimer;
34
+ private opts;
35
+ private startedOnce;
36
+ private didDispatchPause;
37
+ private seekTargetMs;
38
+ private atLiveEdgeValue;
39
+ private wasEnded;
40
+ private licensedValue;
41
+ private licenseListeners;
42
+ private loadGeneration;
43
+ private lastBitrate;
44
+ private sideloadedDefaultId;
45
+ private nativeLoadListeners;
46
+ private detachDrm;
47
+ private sawEncrypted;
48
+ private drmKeysAnnounced;
49
+ private drmLicenseRequests;
50
+ private thumbnailTrack;
51
+ private lastDroppedTotal;
52
+ /** Saved content position while a content-element ads provider holds the
53
+ * media element (FreeWheel handoff). */
54
+ private breakResume;
55
+ /** Opt-in client-side ads integration (e.g. `ImaAdsProvider`). Set before
56
+ * `load()`ing an item with `adBreaks`. */
57
+ adsProvider: AdsProvider | null;
58
+ private adContainer;
59
+ private attachedAdsProvider;
60
+ private pendingAdConfig;
61
+ /** Video+tag sessions whose whole ad schedule finished — watched ads are
62
+ * not re-requested when the same video+tag reloads in this instance. */
63
+ private completedAdSessions;
64
+ private currentAdSessionKey;
65
+ /** Last item passed to load() — what retry() reloads. */
66
+ private lastLoadedItem;
67
+ /** Position from the last healthy progress tick — where retry() resumes. */
68
+ private lastStablePositionMs;
69
+ private pendingContentAutoplay;
70
+ private isPlayingAdFlag;
71
+ private adsPendingFlag;
72
+ private adPausedFlag;
73
+ private adProgressValue;
74
+ private adCuePointsValue;
75
+ private lastAdInfo;
76
+ /** Break transitions on ACTIVE content are silent (mobile parity). */
77
+ private suppressAdBreakTransition;
78
+ /** While true, nothing may start playback — play() and every internal
79
+ * resume path no-op. Used by the hard ad-block policy; equally useful for
80
+ * host-side gates (paywalls, age gates). */
81
+ holdPlayback: boolean;
82
+ constructor(options?: OGPlayerOptions);
83
+ get state(): PlaybackState;
84
+ get isPlaying(): boolean;
85
+ get currentItem(): OGMediaItem | null;
86
+ get currentPositionMs(): number;
87
+ get durationMs(): number;
88
+ get bufferedPositionMs(): number;
89
+ get playbackSpeed(): number;
90
+ set playbackSpeed(v: number);
91
+ get volume(): number;
92
+ set volume(v: number);
93
+ get isMuted(): boolean;
94
+ set isMuted(v: boolean);
95
+ /** Web routes all volume to the element; kept for API parity. */
96
+ volumeControlMode: VolumeControlMode;
97
+ get isLicensed(): boolean;
98
+ get seekForwardIncrementMs(): number;
99
+ get seekBackwardIncrementMs(): number;
100
+ get isPlayingAd(): boolean;
101
+ /** True between an autoplay load-with-ads and the first provider verdict
102
+ * (content pause OR resume) — the UI keeps controls hidden meanwhile. */
103
+ get adsPending(): boolean;
104
+ get adPaused(): boolean;
105
+ /** Progress of the current ad (drives the yellow ad bar), or null. */
106
+ get adProgress(): {
107
+ positionMs: number;
108
+ durationMs: number;
109
+ adIndex: number;
110
+ adCount: number;
111
+ } | null;
112
+ /** VMAP cue-point times in seconds (negative = postroll). */
113
+ get adCuePoints(): number[];
114
+ get currentLoadGeneration(): number;
115
+ subtitleStyle: SubtitleStyle;
116
+ subtitleTextScale: number;
117
+ onLicenseChanged(cb: (licensed: boolean) => void): void;
118
+ addListener(l: PlaybackListener): void;
119
+ removeListener(l: PlaybackListener): void;
120
+ addAnalyticsListener(l: OGAnalyticsListener): void;
121
+ removeAnalyticsListener(l: OGAnalyticsListener): void;
122
+ addAdListener(l: AdListener): void;
123
+ removeAdListener(l: AdListener): void;
124
+ private dispatch;
125
+ private emit;
126
+ load(item: OGMediaItem, options?: LoadOptions): void;
127
+ /** Wire the media pipeline for `item` — everything about the SOURCE, none
128
+ * of the session state. Reused to restore content after a provider that
129
+ * plays ads in the content element (FreeWheel) hands it back. */
130
+ /**
131
+ * Reloads the last loaded item after a fatal error — VOD resumes at the
132
+ * last healthy playback position, live streams rejoin at the edge. The
133
+ * built-in error overlay's Retry button calls this; hosts building their
134
+ * own UI can too. No-op before the first load().
135
+ */
136
+ retry(): void;
137
+ private attachSource;
138
+ private adSessionKey;
139
+ private beginAdsIfNeeded;
140
+ private startAdsSession;
141
+ private resetAdSession;
142
+ private dispatchAd;
143
+ /** Provider → player bridge (the web `AdsProviderCallbacks`). */
144
+ private readonly adCallbacks;
145
+ play(): void;
146
+ pause(): void;
147
+ skipAd(): void;
148
+ clickAd(): void;
149
+ /** Ad-break cue positions in ms, Android encoding (postroll = duration). */
150
+ get adCuePositionsMs(): number[];
151
+ /** True when the current item carries a storyboard (trick-play) track. */
152
+ get hasThumbnails(): boolean;
153
+ /** The storyboard frame covering `positionMs`, or null (mobile parity:
154
+ * `getThumbnail`). The frame is a crop within a sprite image. */
155
+ getThumbnail(positionMs: number): Promise<ThumbnailFrame | null>;
156
+ /** Supplied by `<og-player>`: the element IMA renders its ad UI into. */
157
+ attachAdContainer(el: HTMLElement): void;
158
+ /** The player surface was resized — keep the ad UI matched. */
159
+ notifyAdViewResize(width: number, height: number, fullscreen: boolean): void;
160
+ seekTo(positionMs: number): void;
161
+ seekForward(): void;
162
+ seekBackward(): void;
163
+ seekToLiveEdge(): void;
164
+ release(): void;
165
+ private teardownSource;
166
+ get audioTracks(): AudioTrack[];
167
+ selectAudioTrack(id: string): void;
168
+ /** Embedded + sideloaded subtitle tracks, unified (sideloaded ids `ext-N`). */
169
+ get textTracks(): OGTextTrack[];
170
+ selectTextTrack(id: string | null): void;
171
+ get videoQualities(): VideoQuality[];
172
+ selectVideoQuality(id: string): void;
173
+ get liveInfo(): LiveInfo | null;
174
+ private setState;
175
+ private bindVideoEvents;
176
+ private bindHlsEvents;
177
+ private attachSideloadedSubtitles;
178
+ private startProgressTimer;
179
+ private stopProgressTimer;
180
+ private startSnapshotTimer;
181
+ private stopSnapshotTimer;
182
+ }
@@ -0,0 +1,26 @@
1
+ import type { ThumbnailFrame } from "./types.js";
2
+ /**
3
+ * Storyboard (trick-play) track — the web counterpart of the mobile SDKs'
4
+ * `hasThumbnails` / `getThumbnail`. Parses a WebVTT storyboard where each cue
5
+ * names a sprite image, optionally with a `#xywh=` media fragment (the format
6
+ * Mux, JW and Bitmovin tooling emit):
7
+ *
8
+ * 00:00:00.000 --> 00:00:05.000
9
+ * storyboard.jpg#xywh=0,0,284,160
10
+ */
11
+ interface Cue {
12
+ startMs: number;
13
+ endMs: number;
14
+ frame: ThumbnailFrame;
15
+ }
16
+ export declare function parseStoryboardVtt(vtt: string, baseUrl: string): Cue[];
17
+ /** Lazily-fetched storyboard for one media item. */
18
+ export declare class ThumbnailTrack {
19
+ private readonly vttUrl;
20
+ private cues;
21
+ private loading;
22
+ constructor(vttUrl: string);
23
+ private ensureLoaded;
24
+ frameAt(positionMs: number): Promise<ThumbnailFrame | null>;
25
+ }
26
+ export {};
@@ -0,0 +1,204 @@
1
+ /**
2
+ * OGPlayer web SDK — public model types. The web counterpart of Android's
3
+ * `com.ogplayer.api` (the reference platform): same names, same semantics,
4
+ * so code and docs translate 1:1 across Android / iOS / web.
5
+ */
6
+ export type StreamType = "VOD" | "LIVE" | "LIVE_DVR";
7
+ export type PlaybackState = "IDLE" | "BUFFERING" | "READY" | "ENDED";
8
+ /** Web has no per-app device-volume routing; DEVICE behaves like PLAYER. */
9
+ export type VolumeControlMode = "DEVICE" | "PLAYER";
10
+ export interface SubtitleSource {
11
+ url: string;
12
+ language: string;
13
+ label: string;
14
+ isDefault?: boolean;
15
+ }
16
+ export type SubtitleEdgeType = "NONE" | "OUTLINE" | "DROP_SHADOW" | "RAISED" | "DEPRESSED";
17
+ /** Caption styling — colors are CSS color strings on the web. */
18
+ export interface SubtitleStyle {
19
+ textSizeFraction: number;
20
+ foregroundColor: string;
21
+ backgroundColor: string;
22
+ windowColor: string;
23
+ edgeType: SubtitleEdgeType;
24
+ edgeColor: string;
25
+ applyEmbeddedStyles: boolean;
26
+ }
27
+ export declare const DEFAULT_TEXT_SIZE_FRACTION = 0.045;
28
+ export declare const defaultSubtitleStyle: SubtitleStyle;
29
+ export type ContentRatingAge = "ALL" | "SIX" | "NINE" | "TWELVE" | "FOURTEEN" | "SIXTEEN" | "EIGHTEEN";
30
+ export type ContentRatingDescriptor = "VIOLENCE" | "FEAR" | "SEX" | "DISCRIMINATION" | "DRUGS_ALCOHOL" | "COARSE_LANGUAGE";
31
+ /** Kijkwijzer-style presets + custom icon URL (the web `Custom`). */
32
+ export type ContentRating = {
33
+ age: ContentRatingAge;
34
+ } | {
35
+ descriptor: ContentRatingDescriptor;
36
+ } | {
37
+ customIconUrl: string;
38
+ };
39
+ export interface DrmSchemeConfig {
40
+ licenseUrl: string;
41
+ headers?: Record<string, string>;
42
+ }
43
+ /** Why a DRM session was renewed (mobile parity). */
44
+ export type DrmRenewalReason = "PROACTIVE" | "REACTIVE";
45
+ /** One license request about to go out — passed to the token provider. */
46
+ export interface DrmTokenRequest {
47
+ licenseUrl: string;
48
+ /** True when this is a renewal on an existing session, not the first key. */
49
+ renewal: boolean;
50
+ }
51
+ /**
52
+ * Fresh headers for every license request — the web counterpart of the
53
+ * Android/iOS token providers. Called per request (renewals included) so
54
+ * expiring tokens can rotate; the result is merged OVER the scheme's static
55
+ * `headers`. Failures surface as DRM_TOKEN_FETCH_FAILED (4002).
56
+ */
57
+ export type DrmTokenProvider = (request: DrmTokenRequest) => Promise<Record<string, string>>;
58
+ /**
59
+ * Multi-DRM configuration — the web counterpart of Android's `DrmConfig` /
60
+ * iOS's `FairPlayConfig`, unified: configure every scheme you have and the
61
+ * SDK picks whichever the visitor's browser supports (Chrome/Firefox/Android
62
+ * → Widevine, Edge → PlayReady or Widevine, Safari → FairPlay).
63
+ */
64
+ export interface DrmConfig {
65
+ widevine?: DrmSchemeConfig & {
66
+ serverCertificateUrl?: string;
67
+ };
68
+ playready?: DrmSchemeConfig;
69
+ fairplay?: DrmSchemeConfig & {
70
+ certificateUrl: string;
71
+ };
72
+ /** Rotating-token hook, applied to whichever scheme engages. */
73
+ tokenProvider?: DrmTokenProvider;
74
+ }
75
+ export interface OGMediaItem {
76
+ url: string;
77
+ streamType?: StreamType;
78
+ title?: string;
79
+ drm?: DrmConfig;
80
+ sideloadedSubtitles?: SubtitleSource[];
81
+ contentRatings?: ContentRating[];
82
+ thumbnailTrackUrl?: string;
83
+ /** Interpreted by the active ads provider (web providers ship later). */
84
+ adBreaks?: import("../ads/types.js").AdBreakConfig;
85
+ }
86
+ export interface AudioTrack {
87
+ id: string;
88
+ label: string;
89
+ language: string;
90
+ channelCount: number;
91
+ isSelected: boolean;
92
+ }
93
+ export interface TextTrack {
94
+ id: string;
95
+ label: string;
96
+ language: string;
97
+ isSelected: boolean;
98
+ }
99
+ export interface VideoQuality {
100
+ id: string;
101
+ width: number;
102
+ height: number;
103
+ bitrate: number;
104
+ isSelected: boolean;
105
+ }
106
+ export interface LiveInfo {
107
+ streamType: StreamType;
108
+ dvrWindowMs: number;
109
+ atLiveEdge: boolean;
110
+ latencyMs: number;
111
+ playheadWallClockMs: number | null;
112
+ }
113
+ /** Android's PlaybackListener, method-for-method. All members optional. */
114
+ export interface PlaybackListener {
115
+ onStateChanged?(state: PlaybackState): void;
116
+ onPlay?(): void;
117
+ onPause?(): void;
118
+ onResume?(): void;
119
+ onIsPlayingChanged?(isPlaying: boolean): void;
120
+ onProgress?(positionMs: number, bufferedMs: number, durationMs: number): void;
121
+ onSeekStarted?(fromMs: number, toMs: number): void;
122
+ onSeekCompleted?(positionMs: number): void;
123
+ onPlaybackCompleted?(): void;
124
+ onLiveEdgeChanged?(atLiveEdge: boolean): void;
125
+ onDrmSessionRenewed?(reason: DrmRenewalReason): void;
126
+ onError?(error: OGPlayerError): void;
127
+ }
128
+ /** One frame of a storyboard/trick-play track: a crop within a sprite. */
129
+ export interface ThumbnailFrame {
130
+ /** Sprite image URL (resolved against the storyboard VTT). */
131
+ url: string;
132
+ x: number;
133
+ y: number;
134
+ width: number;
135
+ height: number;
136
+ }
137
+ export type AnalyticsEvent = {
138
+ type: "Play";
139
+ } | {
140
+ type: "Pause";
141
+ } | {
142
+ type: "Seek";
143
+ fromMs: number;
144
+ toMs: number;
145
+ } | {
146
+ type: "BufferStart";
147
+ } | {
148
+ type: "BufferEnd";
149
+ } | {
150
+ type: "BitrateChanged";
151
+ bitrate: number;
152
+ width: number;
153
+ height: number;
154
+ frameRate: number;
155
+ } | {
156
+ type: "QualitySnapshot";
157
+ positionMs: number;
158
+ bufferedMs: number;
159
+ bandwidthEstimateBps: number;
160
+ droppedFramesTotal: number;
161
+ } | {
162
+ type: "DroppedFrames";
163
+ count: number;
164
+ elapsedMs: number;
165
+ } | {
166
+ type: "DrmKeysLoaded";
167
+ } | {
168
+ type: "Complete";
169
+ } | {
170
+ type: "Error";
171
+ error: OGPlayerError;
172
+ };
173
+ export type OGAnalyticsListener = (event: AnalyticsEvent) => void;
174
+ /** Error taxonomy — same numeric codes as Android/iOS. */
175
+ export declare const ErrorCodes: {
176
+ readonly NETWORK_CONNECTION_FAILED: 2000;
177
+ readonly NETWORK_TIMEOUT: 2001;
178
+ readonly NETWORK_HTTP_STATUS: 2002;
179
+ readonly SOURCE_MALFORMED: 3000;
180
+ readonly SOURCE_UNSUPPORTED: 3001;
181
+ readonly SOURCE_NOT_FOUND: 3002;
182
+ readonly DRM_LICENSE_FAILED: 4000;
183
+ readonly DRM_LICENSE_HTTP_STATUS: 4001;
184
+ readonly DRM_TOKEN_FETCH_FAILED: 4002;
185
+ readonly DRM_PROVISIONING_FAILED: 4003;
186
+ readonly DRM_SESSION_EXPIRED: 4004;
187
+ readonly DRM_UNSUPPORTED: 4005;
188
+ readonly DECODER_INIT_FAILED: 5000;
189
+ readonly DECODING_FAILED: 5001;
190
+ readonly AUDIO_OUTPUT_FAILED: 5002;
191
+ readonly BEHIND_LIVE_WINDOW: 6000;
192
+ readonly UNKNOWN: 9000;
193
+ };
194
+ export type ErrorCategory = "Network" | "Source" | "Drm" | "Renderer" | "Live" | "Unknown";
195
+ export interface OGPlayerError {
196
+ code: number;
197
+ codeName: string;
198
+ category: ErrorCategory;
199
+ message: string;
200
+ retryable: boolean;
201
+ httpStatusCode?: number;
202
+ cause?: unknown;
203
+ }
204
+ export declare const OGPLAYER_VERSION = "0.1.0";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * OGPlayer web SDK — one video player API across Android, iOS and the web.
3
+ * A product of Inverse DOO.
4
+ */
5
+ export { OGPlayer, type OGPlayerOptions, type LoadOptions } from "./core/player.js";
6
+ export * from "./core/types.js";
7
+ export { verifyLicense, type LicenseResult } from "./core/license.js";
8
+ export * from "./ads/types.js";
9
+ export type { AdsProvider, AdsProviderCallbacks } from "./ads/provider.js";
10
+ export { ImaAdsProvider, type ImaAdsProviderOptions } from "./ads/ima.js";
11
+ export { freewheelVmapTagUrl, isFreewheelConfig, type FreewheelConfig } from "./ads/freewheel.js";
12
+ export { FreewheelAdsProvider, type FreewheelAdsProviderOptions } from "./ads/freewheel-provider.js";
13
+ export { OGPlayerElement, defineOGPlayerElement, hidingAllControls, type OGUIConfig, type CustomAction, defaultUIConfig, } from "./ui/og-player.js";
14
+ export type { OGControlColors, OGControlDimens } from "./ui/tokens.js";
15
+ export { defaultColors, embeddedDimens, fullscreenDimens } from "./ui/tokens.js";
16
+ /** Build timestamp of this bundle (diagnosing stale caches). */
17
+ export declare const OGPLAYER_BUILD: string;