@oddin-gg/havik-player 2.23.2 → 2.23.4

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/docs/API.md CHANGED
@@ -254,7 +254,7 @@ player.on('error', (e) => console.error(e.code, e.httpStatus, e.message));
254
254
  | `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). |
255
255
  | `sizeCapHeadroom` | `number` | unset | hls.js engine: rendition rungs ABR may pick above the one that merely covers the player's pixel box. Unset: `1` on displays below 2× `devicePixelRatio`, `0` at 2× and above. A 960 px box at 1x covers with 960×540; one rung of headroom plays 720p downscaled instead, which looks sharper for the same box; phones and 2× screens keep the cover-only cap (their covering rung already matches their device pixels; a rung above it mostly costs data). An explicit value applies on every display: `0` = strict cover-only cap, `Infinity` = no size cap. Never past the top rung; `maxBitrate` and the frame-drop cap still apply. `hlsConfig.capLevelToPlayerSize: true` caps to the covering rung with no headroom. |
256
256
  | `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. |
257
- | `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. |
257
+ | `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; a 410 (match gone) stops the subscription and a 404 (not in the catalog) slows it to one retry every 5 min, each with one `warning`. 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. |
258
258
  | `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`. |
259
259
  | `analytics` | `boolean \| { endpoint?: string }` | `true` | QoE beacons. Active only when the playback response carries a server-minted `sid`. **havik-streams v0.10.0+ names the beacons host in that response, and it is used whatever `baseUrl` is**; it also wins over `{ endpoint }` (a differing value is ignored with one `console.warn`). Against an older server the host is `{ endpoint }` if given, else derived from `baseUrl` by hostname convention (`feed[-dev].<domain>` → `beacons[-dev].<domain>`); there, **if `baseUrl` is not a `feed` host nothing derives and beacons are disabled** — the SDK then logs one `console.warn` naming the host and emits a non-fatal [`warning`](#player-events) event carrying the same sentence. Recovery: pass `{ endpoint: 'https://beacons[-dev].<domain>' }`. Pass `false` to opt out deliberately (no warning). |
260
260
  | `eventsBaseUrl` | `string` | derived | Override the SSE endpoint base. Default: `baseUrl` with the host prefixed by `events.` (e.g. `feed.<d>` → `events.<d>`). |
@@ -957,6 +957,13 @@ Uses `fetch` (not native `EventSource`, which can't set the `x-api-key` header),
957
957
  reconnects with backoff, and replays the current state on connect. Every event is
958
958
  also reported via `onAlive` (including heartbeats) so callers can treat the stream as
959
959
  healthy. A subscription problem is **non-fatal**; it retries in the background.
960
+ Two answers are special, both reported to `onError` as a `LiveStateHttpError`
961
+ (`status`, `terminal`): an HTTP **410** means the match is gone for good and the
962
+ subscription stops (`terminal: true`); an HTTP **404** means the match is not in the
963
+ catalog _now_ (it ended and was dropped, or it is not listed yet) and the retry moves
964
+ to a slow lane, `notFoundRetryMs` (default 5 min), so a tab left open on a finished
965
+ match costs one request every five minutes instead of one every 15 s, while a match
966
+ listed late is still picked up without a reload.
960
967
 
961
968
  A connection can also go silent **without closing** (a black-holed socket after
962
969
  a laptop sleep, a NAT rebind, a half-closing proxy). Nothing in the reconnect
@@ -987,17 +994,18 @@ const sub = subscribeLiveState({
987
994
 
988
995
  #### `SubscribeLiveStateOptions`
989
996
 
990
- | Option | Type | Default | Description |
991
- | --------------- | --------------------------------------- | ------- | ------------------------------------------------------------------ |
992
- | `eventsBaseUrl` | `string` | — | **Required.** SSE base, e.g. from `deriveEventsBaseUrl(baseUrl)`. |
993
- | `matchUrn` | `string` | — | **Required.** |
994
- | `credential` | [`CredentialSource`](#credentialsource) | — | **Required.** |
995
- | `onState` | `(ev: LiveStateEvent) => void` | — | **Required.** Initial snapshot on connect + every transition. |
996
- | `onError` | `(err: unknown) => void` | — | Non-fatal subscription error; the client keeps retrying. |
997
- | `onAlive` | `() => void` | — | Liveness tick on every received frame (incl. heartbeats). |
998
- | `minBackoffMs` | `number` | `1000` | Reconnect backoff floor. |
999
- | `maxBackoffMs` | `number` | `15000` | Reconnect backoff ceiling. |
1000
- | `idleTimeoutMs` | `number` | `45000` | Silence (awaiting headers, or bytes) before reconnecting; `0` off. |
997
+ | Option | Type | Default | Description |
998
+ | ----------------- | --------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
999
+ | `eventsBaseUrl` | `string` | — | **Required.** SSE base, e.g. from `deriveEventsBaseUrl(baseUrl)`. |
1000
+ | `matchUrn` | `string` | — | **Required.** |
1001
+ | `credential` | [`CredentialSource`](#credentialsource) | — | **Required.** |
1002
+ | `onState` | `(ev: LiveStateEvent) => void` | — | **Required.** Initial snapshot on connect + every transition. |
1003
+ | `onError` | `(err: unknown) => void` | — | Subscription error; the client keeps retrying, except: an HTTP answer arrives as a `LiveStateHttpError` (`status`, `terminal`), a 410 is `terminal` and stops the subscription, a 404 moves it to the `notFoundRetryMs` lane. |
1004
+ | `onAlive` | `() => void` | — | Liveness tick on every received frame (incl. heartbeats). |
1005
+ | `minBackoffMs` | `number` | `1000` | Reconnect backoff floor. |
1006
+ | `maxBackoffMs` | `number` | `15000` | Reconnect backoff ceiling. |
1007
+ | `idleTimeoutMs` | `number` | `45000` | Silence (awaiting headers, or bytes) before reconnecting; `0` off. |
1008
+ | `notFoundRetryMs` | `number` | `300000` | Retry interval after an HTTP 404 (match not in the catalog now); not floored by `maxBackoffMs`. |
1001
1009
 
1002
1010
  ### `isLowLatencyManifest`
1003
1011
 
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.23.2/dist/havik-player.global.js" integrity="sha384-BWNGh69xTtsQNC/eG6h+v2R4LXOE8ez3vbqK9K7LU3uby3I5BJbJygF9MUH6QdAK" crossorigin="anonymous"></script>
31
+ <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.23.4/dist/havik-player.global.js" integrity="sha384-6/qyxMocRo8JFOEGUwPF1mWnThrfbhRFkzcsDUzypy9IvKw7Ki17nGFchYeIQyoA" crossorigin="anonymous"></script>
32
32
  <script>
33
33
  HavikPlayer.bootEmbed();
34
34
  </script>
@@ -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.23.2/dist/havik-player.global.js" integrity="sha384-BWNGh69xTtsQNC/eG6h+v2R4LXOE8ez3vbqK9K7LU3uby3I5BJbJygF9MUH6QdAK" crossorigin="anonymous"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.23.4/dist/havik-player.global.js" integrity="sha384-6/qyxMocRo8JFOEGUwPF1mWnThrfbhRFkzcsDUzypy9IvKw7Ki17nGFchYeIQyoA" 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.23.2",
3
+ "version": "2.23.4",
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,