@oddin-gg/havik-player 2.17.0 → 2.19.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.
- package/dist/havik-player.global.js +18 -18
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +402 -326
- package/dist/index.js.map +1 -1
- package/docs/API.md +41 -2
- package/docs/STABILITY.md +1 -1
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/docs/API.md
CHANGED
|
@@ -249,6 +249,7 @@ player.on('error', (e) => console.error(e.code, e.httpStatus, e.message));
|
|
|
249
249
|
| `maxBitrate` | `number` | — | Cap ABR to renditions at/below this bitrate (bps). |
|
|
250
250
|
| `liveLatencyTarget` | `number` | `2` | Target live‑edge latency in seconds (hls.js `liveSyncDuration`). The player derives a matching latency **ceiling** internally and catches up at 1.1×, so this is the only live‑latency knob you need. Lower = closer to live but more rebuffer‑prone. Live/LL only. |
|
|
251
251
|
| `snapToLiveOnRefocus` | `boolean` | `true` | Snap to the live edge when a backgrounded tab is refocused: on hls.js while far behind; on the native player (iPhone, iPad, FairPlay) on every return after 2 s or more away and on every back/forward‑cache restore, 3 s behind the edge once the first playlist refresh after the return moves it (up to 6 s wait; the edge on return is still the one from before leaving), which also redraws a picture iOS left black. Live/LL only. |
|
|
252
|
+
| `rememberBandwidth` | `boolean` | `true` | hls.js engine: start from the bandwidth this device measured in a recent session (`localStorage` key `havik.player.bandwidth`, per connection type, up to 3 days old, reused at 80%) instead of the conservative ~1 Mbps start; a slow record starts from that lower figure (0.3 Mbps floor). A first visit, a stale record or blocked storage keeps the conservative start; an explicit `hlsConfig.abrEwmaDefaultEstimate` or `startLevel` wins. Reset with [`clearRememberedBandwidth()`](#clearrememberedbandwidth). |
|
|
252
253
|
| `mediaDuringKey` | `boolean` | `true` | DRM cold start: download the first init segment and parts while the first key is still being acquired, instead of after it. Bytes reach hls.js only when it asks for them; only the network time moves off the critical path (~0.5 s on Widevine LL-HLS). Ignored on clear streams and when `hlsConfig.fLoader` is set. Pass `false` to opt out. |
|
|
253
254
|
| `liveStateEvents` | `boolean` | `true` | Use the push-based **live-state SSE** channel for go-live and end-of-stream (see [Live state](#live-state-sse)). Graceful: falls back to polling if the endpoint is unreachable. On a return from the background (5 s or more away, or a back/forward-cache restore) the subscription is reopened so the server replays the current state: a page suspended through the end of a match does not sit buffering on a socket that will never deliver it. Set `false` to disable. |
|
|
254
255
|
| `rejoinOnLive` | `boolean` | `true` | Rejoin automatically when a stream that ended comes back live. An ingest outage longer than the bridge's reconnect grace recreates the session under a **new manifest URL** (the old playlist freezes at HTTP 200), so the player keeps the live-state subscription open after `ended` (until `gone`) and re-arms on a pushed `live`. Set `false` to keep `ended` terminal. Needs `liveStateEvents`. |
|
|
@@ -305,7 +306,14 @@ don't need to set any of the live or ABR knobs:
|
|
|
305
306
|
rendition is one a modest link can sustain (**~360p**) and then **ramps up**
|
|
306
307
|
within a few seconds as measured bandwidth allows, rather than grabbing a high
|
|
307
308
|
rung and rebuffering on a constrained link. This trades a slightly softer first
|
|
308
|
-
~2 s for a **stall‑free start** (the first‑seconds buffering fix).
|
|
309
|
+
~2 s for a **stall‑free start** (the first‑seconds buffering fix). A device
|
|
310
|
+
that has **measured a fast line in the last 3 days** starts from that
|
|
311
|
+
measurement instead (`rememberBandwidth`, on by default: the estimate after
|
|
312
|
+
~10 s of playback, stored per connection type and reused at 80%), so a
|
|
313
|
+
returning viewer on a fast line starts near full quality without a 360p jump,
|
|
314
|
+
and a device that measured a slow line starts from that lower figure (also at
|
|
315
|
+
80%, never below 0.3 Mbps). A first visit or a device with no usable record
|
|
316
|
+
keeps the ~1 Mbps start. In addition,
|
|
309
317
|
the session is bounded to the player's pixel box, so a small/embedded player
|
|
310
318
|
won't fetch 1080p, but by an SDK‑managed, **debounced** size cap rather than
|
|
311
319
|
hls.js's own `capLevelToPlayerSize` poller: a size change only reaches ABR
|
|
@@ -386,6 +394,7 @@ interface Player {
|
|
|
386
394
|
exitPictureInPicture(): Promise<void>;
|
|
387
395
|
enterFullscreen(): Promise<boolean>; // element FS on the player container; iPhone → video-native. false = no mechanism
|
|
388
396
|
exitFullscreen(): Promise<void>; // element or video-native; safe when not fullscreen
|
|
397
|
+
fullscreenSupported(): boolean; // false = permissions policy blocks it (iframe without allow=fullscreen) or no mechanism; optimistic before media loads on iPhone
|
|
389
398
|
retry(): Promise<void>; // recover from an error (re-resolve + re-attach)
|
|
390
399
|
reload(): Promise<void>; // re‑resolve playback (refreshes the license URL) + re‑attach
|
|
391
400
|
on<E extends PlayerEvent>(event: E, cb: (payload: PlayerEventMap[E]) => void): () => void;
|
|
@@ -460,6 +469,17 @@ stream.
|
|
|
460
469
|
> effect **after playback has started** (a tap on the pre-play poster is a
|
|
461
470
|
> no-op there). Everywhere else (desktop, Android, iPad) the button uses
|
|
462
471
|
> element fullscreen and keeps the branded controls.
|
|
472
|
+
>
|
|
473
|
+
> **Where fullscreen cannot work, the button is not shown** and the `f`
|
|
474
|
+
> shortcut is inert, the same gate the Picture-in-Picture button has: a
|
|
475
|
+
> cross-origin iframe whose `allow` list omits `fullscreen` (the browser
|
|
476
|
+
> reports `document.fullscreenEnabled === false`), or a webview with no
|
|
477
|
+
> fullscreen mechanism at all. So an operator who must block fullscreen on an
|
|
478
|
+
> embed drops it from the iframe's `allow` attribute and needs no custom
|
|
479
|
+
> control bar. The decision is re-made on the video's `loadedmetadata`, which
|
|
480
|
+
> is when iPhone first reports whether its video-native presentation exists,
|
|
481
|
+
> so on iPhone the button is still shown (and inert, as above) until playback
|
|
482
|
+
> has started.
|
|
463
483
|
|
|
464
484
|
```ts
|
|
465
485
|
type Controls = 'custom' | 'native' | 'none';
|
|
@@ -689,6 +709,7 @@ hls.attachMedia(videoElement);
|
|
|
689
709
|
function enterVideoFullscreen(video: HTMLVideoElement, container?: HTMLElement): Promise<boolean>;
|
|
690
710
|
function exitVideoFullscreen(video: HTMLVideoElement, container?: HTMLElement): Promise<void>;
|
|
691
711
|
function videoFullscreenActive(video: HTMLVideoElement, container?: HTMLElement): boolean;
|
|
712
|
+
function fullscreenSupported(video: HTMLVideoElement, container?: HTMLElement): boolean;
|
|
692
713
|
```
|
|
693
714
|
|
|
694
715
|
Fullscreen with the platform quirks handled, so your own fullscreen button
|
|
@@ -708,7 +729,13 @@ first; it reports only through the video, never the document), is safe to
|
|
|
708
729
|
call when not fullscreen, and is **scoped to your video's presentation**: an
|
|
709
730
|
unrelated element's fullscreen elsewhere on the page is left alone.
|
|
710
731
|
`videoFullscreenActive` tells you whether your video's presentation is
|
|
711
|
-
currently fullscreen.
|
|
732
|
+
currently fullscreen. `fullscreenSupported` is the pre-flight for your button:
|
|
733
|
+
`false` when a request is known to fail: the element API is there but the
|
|
734
|
+
document reports `fullscreenEnabled === false` (permissions policy; an iframe
|
|
735
|
+
without `fullscreen` in `allow`), or media is loaded and the platform has no
|
|
736
|
+
mechanism at all. It stays `true` before media loads where no element API
|
|
737
|
+
exists (iPhone cannot say yet), so re-check it on `loadedmetadata`. Pass the
|
|
738
|
+
same `container` to all four. For fullscreen **state changes**, listen to `fullscreenchange` /
|
|
712
739
|
`webkitfullscreenchange` on the document and `webkitbeginfullscreen` /
|
|
713
740
|
`webkitendfullscreen` on the video; the last two are the only signal for
|
|
714
741
|
iPhone's video-native fullscreen.
|
|
@@ -769,6 +796,18 @@ function clearDeviceId(): void;
|
|
|
769
796
|
> under GDPR/ePrivacy). Disclose it in your privacy policy and offer a reset:
|
|
770
797
|
> `clearDeviceId()` removes the stored id so the next call generates a fresh one.
|
|
771
798
|
|
|
799
|
+
### `clearRememberedBandwidth`
|
|
800
|
+
|
|
801
|
+
```ts
|
|
802
|
+
function clearRememberedBandwidth(): void;
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
Forgets the bandwidth the player remembered for this device (the
|
|
806
|
+
`rememberBandwidth` option, `localStorage` key `havik.player.bandwidth`). The next
|
|
807
|
+
session starts from the conservative ~1 Mbps estimate again. The record holds
|
|
808
|
+
only an estimated speed and a timestamp per connection type; offer it beside
|
|
809
|
+
`clearDeviceId()` in a "reset player data" control.
|
|
810
|
+
|
|
772
811
|
### `fetchCatalog`
|
|
773
812
|
|
|
774
813
|
```ts
|
package/docs/STABILITY.md
CHANGED
|
@@ -13,7 +13,7 @@ breaking change → major. **Pin a version in production** (npm and the CDN URL)
|
|
|
13
13
|
|
|
14
14
|
- `createPlayer` + the `Player` interface (Mode A)
|
|
15
15
|
- `resolveStream`, `fetchCatalog`, `flattenMatches`, `watchStatus`,
|
|
16
|
-
`licenseRequestHeaders`, `getDeviceId`, `clearDeviceId`, `isLowLatencyManifest` (Mode B)
|
|
16
|
+
`licenseRequestHeaders`, `getDeviceId`, `clearDeviceId`, `clearRememberedBandwidth`, `isLowLatencyManifest` (Mode B)
|
|
17
17
|
- `mountEmbed` / `bootEmbed` + `EmbedHandle` / the `oddin:*` postMessage protocol (Mode C)
|
|
18
18
|
- `PlaybackError` and all exported `type` definitions
|
|
19
19
|
|
package/embed/index.html
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
beacons endpoint can be derived and QoE analytics is silently off.
|
|
29
29
|
`analytics` is honoured from the injected config only, never the query.
|
|
30
30
|
-->
|
|
31
|
-
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.
|
|
31
|
+
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.19.0/dist/havik-player.global.js" integrity="sha384-RWHZWgsgSqUcFMISDll7/ApdE2YOBYQ9C0xvHZo3OtY8AuU9OoSL5kOA3G29w/bg" crossorigin="anonymous"></script>
|
|
32
32
|
<script>
|
|
33
33
|
HavikPlayer.bootEmbed();
|
|
34
34
|
</script>
|
package/examples/vanilla.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
shipped in each npm release is pinned to that release with Subresource
|
|
13
13
|
Integrity, injected at publish time (scripts/inject-sri.mjs). In
|
|
14
14
|
production, always use a pinned + SRI'd tag like the released copy. -->
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.19.0/dist/havik-player.global.js" integrity="sha384-RWHZWgsgSqUcFMISDll7/ApdE2YOBYQ9C0xvHZo3OtY8AuU9OoSL5kOA3G29w/bg" crossorigin="anonymous"></script>
|
|
16
16
|
<script>
|
|
17
17
|
(async () => {
|
|
18
18
|
const player = await HavikPlayer.createPlayer({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oddin-gg/havik-player",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "Havik HTML5 player SDK — LL-HLS + DRM playback for Oddin live streams (managed, bring-your-own-player, and iframe-embed modes).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|