ogplayer 0.1.1 → 0.2.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.
@@ -1,3 +1,4 @@
1
+ import { type HlsConfig } from "hls.js";
1
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
3
  import type { AdListener } from "../ads/types.js";
3
4
  import type { AdsProvider } from "../ads/provider.js";
@@ -9,6 +10,22 @@ export interface OGPlayerOptions {
9
10
  /** Seek button increments, default 10s each way. */
10
11
  seekForwardIncrementMs?: number;
11
12
  seekBackwardIncrementMs?: number;
13
+ /**
14
+ * Extra hls.js configuration merged over the SDK's defaults — buffer
15
+ * targets, ABR knobs, anything hls.js accepts. SDK-managed fields
16
+ * (EME/DRM wiring, text-track rendering) win over conflicting keys.
17
+ */
18
+ hlsConfig?: Partial<HlsConfig>;
19
+ /** `<video preload>` hint, default "auto". "metadata" defers segment
20
+ * loading until play — the standby setting for pooled players. */
21
+ preload?: "" | "none" | "metadata" | "auto";
22
+ /**
23
+ * When the browser blocks autoplay-with-audio (NotAllowedError), retry
24
+ * muted instead of staying paused. Fires `onMutedAutoplayFallback` and a
25
+ * `MutedAutoplayFallback` analytics event so the UI can offer unmute.
26
+ * Default false — the standard player shows the big play button instead.
27
+ */
28
+ fallbackToMutedAutoplay?: boolean;
12
29
  }
13
30
  export interface LoadOptions {
14
31
  startPositionMs?: number;
@@ -75,6 +92,10 @@ export declare class OGPlayer {
75
92
  private lastAdInfo;
76
93
  /** Break transitions on ACTIVE content are silent (mobile parity). */
77
94
  private suppressAdBreakTransition;
95
+ private hlsConfig;
96
+ /** See `OGPlayerOptions.fallbackToMutedAutoplay`; mutable so a host (or
97
+ * the vertical feed pool) can flip it per lifecycle phase. */
98
+ fallbackToMutedAutoplay: boolean;
78
99
  /** While true, nothing may start playback — play() and every internal
79
100
  * resume path no-op. Used by the hard ad-block policy; equally useful for
80
101
  * host-side gates (paywalls, age gates). */
@@ -92,6 +113,17 @@ export declare class OGPlayer {
92
113
  set volume(v: number);
93
114
  get isMuted(): boolean;
94
115
  set isMuted(v: boolean);
116
+ /** Loop the current item seamlessly (native `<video loop>`); survives
117
+ * across load() calls, mirroring Android/iOS `setLooping`. */
118
+ get isLooping(): boolean;
119
+ setLooping(v: boolean): void;
120
+ /**
121
+ * Buffer discipline for pooled players (the vertical feed): STANDBY caps
122
+ * appetite (10s forward / 8MB, no back-buffer, metadata-only preload for
123
+ * progressive sources); ACTIVE restores full defaults. Mirrors Android's
124
+ * `setBufferRole` / iOS `setPreferredForwardBufferDuration`.
125
+ */
126
+ setBufferRole(role: "STANDBY" | "ACTIVE"): void;
95
127
  /** Web routes all volume to the element; kept for API parity. */
96
128
  volumeControlMode: VolumeControlMode;
97
129
  get isLicensed(): boolean;
@@ -23,6 +23,12 @@ export interface SubtitleStyle {
23
23
  edgeType: SubtitleEdgeType;
24
24
  edgeColor: string;
25
25
  applyEmbeddedStyles: boolean;
26
+ /**
27
+ * Caption font-family (any CSS font-family value, e.g. "Georgia, serif");
28
+ * null = platform default. Accessibility note: captions are a reading
29
+ * surface — prefer a highly legible face and test at small sizes.
30
+ */
31
+ fontFamily: string | null;
26
32
  }
27
33
  export declare const DEFAULT_TEXT_SIZE_FRACTION = 0.045;
28
34
  export declare const defaultSubtitleStyle: SubtitleStyle;
@@ -76,6 +82,9 @@ export interface OGMediaItem {
76
82
  url: string;
77
83
  streamType?: StreamType;
78
84
  title?: string;
85
+ /** Poster image shown by the UI until playback first starts (native
86
+ * <video poster>). */
87
+ posterUrl?: string;
79
88
  drm?: DrmConfig;
80
89
  sideloadedSubtitles?: SubtitleSource[];
81
90
  contentRatings?: ContentRating[];
@@ -123,6 +132,9 @@ export interface PlaybackListener {
123
132
  onPlaybackCompleted?(): void;
124
133
  onLiveEdgeChanged?(atLiveEdge: boolean): void;
125
134
  onDrmSessionRenewed?(reason: DrmRenewalReason): void;
135
+ /** The browser blocked autoplay-with-audio and `fallbackToMutedAutoplay`
136
+ * restarted playback muted — show your own unmute affordance. */
137
+ onMutedAutoplayFallback?(): void;
126
138
  onError?(error: OGPlayerError): void;
127
139
  }
128
140
  /** One frame of a storyboard/trick-play track: a crop within a sprite. */
@@ -166,6 +178,22 @@ export type AnalyticsEvent = {
166
178
  type: "DrmKeysLoaded";
167
179
  } | {
168
180
  type: "Complete";
181
+ } | {
182
+ type: "MutedAutoplayFallback";
183
+ } | {
184
+ type: "VerticalFeedImpression";
185
+ index: number;
186
+ url: string;
187
+ } | {
188
+ type: "VerticalFeedItemLooped";
189
+ index: number;
190
+ loopCount: number;
191
+ } | {
192
+ type: "VerticalFeedItemWatched";
193
+ index: number;
194
+ url: string;
195
+ watchTimeMs: number;
196
+ loopCount: number;
169
197
  } | {
170
198
  type: "Error";
171
199
  error: OGPlayerError;
@@ -201,4 +229,4 @@ export interface OGPlayerError {
201
229
  httpStatusCode?: number;
202
230
  cause?: unknown;
203
231
  }
204
- export declare const OGPLAYER_VERSION = "0.1.0";
232
+ export declare const OGPLAYER_VERSION: string;
@@ -0,0 +1,126 @@
1
+ import { OGPlayer } from "./player.js";
2
+ import type { AnalyticsEvent, OGMediaItem, OGPlayerError } from "./types.js";
3
+ /**
4
+ * Vertical feed data model + player pool — the web counterpart of the
5
+ * Android `VerticalFeedPlayerPool` (the reference implementation) and its
6
+ * iOS mirror. The pool owns at most `preloadAhead + keepBehind + 1` (≤3)
7
+ * engine instances: neighbours of the visible page sit PREPARED — muted,
8
+ * paused at 0, on capped standby buffers — so swipes start instantly;
9
+ * pages outside the window are recycled; everything is released on dispose.
10
+ *
11
+ * The UI element `<og-vertical-feed>` drives this; the pool is UI-free and
12
+ * unit-testable.
13
+ */
14
+ export type SplitAudioSource = "PRIMARY" | "SECONDARY";
15
+ export type TextPlacement = "OVERLAY" | "ABOVE_VIDEO" | "BELOW_VIDEO";
16
+ export type VerticalFeedContentFit = "FILL" | "FIT";
17
+ /** One action on the feed's right rail — the host's buttons; the SDK ships
18
+ * none. State changes flow by re-supplying the items array. */
19
+ export interface RailAction {
20
+ /** Inline SVG markup for the icon (24×24 viewBox recommended). */
21
+ iconSvg: string;
22
+ /** Optional label under the icon (usually a count or a state word). */
23
+ label?: string;
24
+ isActive?: boolean;
25
+ accessibilityLabel?: string;
26
+ onClick: () => void;
27
+ }
28
+ /** One entry of a vertical feed: media plus feed-specific presentation. */
29
+ export interface OGVerticalFeedItem {
30
+ media: OGMediaItem;
31
+ /** Second source for the split-video layout: plays in the bottom half,
32
+ * starts in the same frame as the primary, pauses/resumes with it. */
33
+ secondaryMedia?: OGMediaItem;
34
+ /** Which half of a split-video page is audible (default PRIMARY). */
35
+ splitAudioSource?: SplitAudioSource;
36
+ /** Per-item override of the feed-level fit. */
37
+ contentFit?: VerticalFeedContentFit;
38
+ /** Poster shown for pages outside the player window. Falls back to
39
+ * `media.posterUrl`. */
40
+ posterUrl?: string;
41
+ /** Up to six host buttons for the right rail. */
42
+ railActions?: RailAction[];
43
+ title?: string;
44
+ subtitle?: string;
45
+ /** OVERLAY (default) or one of the split text layouts. */
46
+ textPlacement?: TextPlacement;
47
+ /** Marks an ad item: "Sponsored" chip, never loops, auto-advances. */
48
+ isSponsored?: boolean;
49
+ }
50
+ /** All knobs, mirroring the Android `OGVerticalFeedConfig` names. */
51
+ export interface OGVerticalFeedConfig {
52
+ preloadAhead: number;
53
+ keepBehind: number;
54
+ loopItems: boolean;
55
+ contentFit: VerticalFeedContentFit;
56
+ showTitle: boolean;
57
+ showSubtitle: boolean;
58
+ showProgressBar: boolean;
59
+ showSponsoredBadge: boolean;
60
+ /** Hairline thickness in px. */
61
+ progressBarThickness: number;
62
+ progressBarColor: string;
63
+ progressBarTrackColor: string;
64
+ /** Inline SVG replacing the center play glyph. */
65
+ playIconSvg: string | null;
66
+ /** CSS font-family for title/subtitle/band text; null = system. */
67
+ textFontFamily: string | null;
68
+ /** px */
69
+ titleTextSize: number;
70
+ /** px */
71
+ subtitleTextSize: number;
72
+ /** CSS color of the split-text band background. */
73
+ textBandColor: string;
74
+ /** Fraction of the page height the text band owns (0.15–0.5). */
75
+ textBandFraction: number;
76
+ /** px between the band's top edge and its text. */
77
+ textBandTopMargin: number;
78
+ /** `onNearEnd` fires when this many items remain below the active one. */
79
+ paginationThreshold: number;
80
+ /** Items longer than this are skipped at prepare time; 0 = no limit. */
81
+ maxItemDurationSeconds: number;
82
+ licenseKey: string | null;
83
+ /** Replace the SDK's default error copy (level 2 of error handling). */
84
+ errorMessageProvider: ((error: OGPlayerError) => string | null) | null;
85
+ }
86
+ export declare const defaultVerticalFeedConfig: OGVerticalFeedConfig;
87
+ export interface VerticalFeedPoolCallbacks {
88
+ onActiveItemChanged?(index: number): void;
89
+ onNearEnd?(remaining: number): void;
90
+ onItemSkipped?(index: number, reason: string): void;
91
+ onItemError?(index: number, error: OGPlayerError): void;
92
+ onAnalyticsEvent?(event: AnalyticsEvent): void;
93
+ /** Pool assignments changed — the UI re-parents `<video>` elements. */
94
+ onAssignmentsChanged?(): void;
95
+ /** A sponsored item ended — the UI advances to the next page. */
96
+ onAdvanceRequested?(fromIndex: number): void;
97
+ }
98
+ export declare class VerticalFeedPlayerPool {
99
+ private config;
100
+ private callbacks;
101
+ private items;
102
+ private entries;
103
+ private readonly capacity;
104
+ private activeIndexValue;
105
+ private released;
106
+ private feedMuted;
107
+ private skipped;
108
+ private licensedValue;
109
+ constructor(config: OGVerticalFeedConfig, callbacks: VerticalFeedPoolCallbacks);
110
+ get activeIndex(): number;
111
+ get isLicensed(): boolean | null;
112
+ get skippedIndices(): ReadonlySet<number>;
113
+ updateItems(items: OGVerticalFeedItem[]): void;
114
+ playerAt(index: number): OGPlayer | null;
115
+ setFeedMuted(muted: boolean): void;
116
+ /** The pager settled on `index`: promote, demote, recycle, prefill. */
117
+ settle(index: number): void;
118
+ releaseAll(): void;
119
+ private primaryMuted;
120
+ private windowOrder;
121
+ private entryFor;
122
+ private createEntry;
123
+ private demote;
124
+ private recycle;
125
+ private emitWatched;
126
+ }
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ export { ImaAdsProvider, type ImaAdsProviderOptions } from "./ads/ima.js";
11
11
  export { freewheelVmapTagUrl, isFreewheelConfig, type FreewheelConfig } from "./ads/freewheel.js";
12
12
  export { FreewheelAdsProvider, type FreewheelAdsProviderOptions } from "./ads/freewheel-provider.js";
13
13
  export { OGPlayerElement, defineOGPlayerElement, hidingAllControls, type OGUIConfig, type CustomAction, defaultUIConfig, } from "./ui/og-player.js";
14
+ export { VerticalFeedPlayerPool, defaultVerticalFeedConfig, type OGVerticalFeedItem, type OGVerticalFeedConfig, type RailAction, type SplitAudioSource, type TextPlacement, type VerticalFeedContentFit, } from "./core/vertical-feed.js";
15
+ export { OGVerticalFeedElement, defineOGVerticalFeedElement } from "./ui/og-vertical-feed.js";
14
16
  export type { OGControlColors, OGControlDimens } from "./ui/tokens.js";
15
17
  export { defaultColors, embeddedDimens, fullscreenDimens } from "./ui/tokens.js";
16
18
  /** Build timestamp of this bundle (diagnosing stale caches). */