@oddin-gg/havik-player 2.8.0 → 2.9.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/docs/API.md CHANGED
@@ -249,6 +249,7 @@ player.on('error', (e) => console.error(e.code, e.httpStatus, e.message));
249
249
  | `snapToLiveOnRefocus` | `boolean` | `true` | Snap to the live edge when a backgrounded tab is refocused while far behind. Live/LL only. |
250
250
  | `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. Set `false` to disable. |
251
251
  | `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`. |
252
+ | `analytics` | `boolean \| { endpoint?: string }` | `true` | QoE beacons. Active only when the playback response carries a server-minted `sid`. The beacons host is derived from `baseUrl` by hostname convention (`feed[-dev].<domain>` → `beacons[-dev].<domain>`). **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). |
252
253
  | `eventsBaseUrl` | `string` | derived | Override the SSE endpoint base. Default: `baseUrl` with the host prefixed by `events.` (e.g. `feed.<d>` → `events.<d>`). |
253
254
 
254
255
  ### Low-latency & adaptive bitrate
@@ -376,7 +377,7 @@ Notes:
376
377
  | `paused` | `void` | Playback is paused. |
377
378
  | `ended` | `EndedReason \| undefined` | The live stream ended; playback stops and the last frame is kept. The payload says **why**: `'interrupted'` (the match is still running, and its stream dropped, so show "reconnecting", **never** "ended"; the player rejoins by itself) or `'match_ended'` (a real end). `undefined` = the end came from the HLS staleness watchdog, or a server predating the field; treat it as a real end. Fires **again** with `'match_ended'` if an interruption later turns out to be the end, so don't latch on the first one. Also readable any time as `player.endedReason`. |
378
379
  | `error` | [`PlaybackError`](#playbackerror) | A fatal error occurred (playback stopped). |
379
- | `warning` | [`PlaybackError`](#playbackerror) | A non-fatal condition (e.g. FairPlay passthrough, a failed license refresh). Playback continues. |
380
+ | `warning` | [`PlaybackError`](#playbackerror) | A non-fatal condition (e.g. FairPlay passthrough, a failed license refresh, or **QoE beacons disabled because the endpoint could not be derived** — see [`analytics`](#createplayeroptions)). Playback continues. |
380
381
  | `autoplayblocked` | `void` | The browser blocked autoplay (`NotAllowedError`). Show a tap-to-play / tap-to-unmute affordance, then call `player.play()` from the gesture. |
381
382
  | `qualitychange` | `number` | Active rendition changed (level index, `-1` = auto). |
382
383
  | `audiotrackchange` | `number` | Active audio track changed (track id). |
@@ -948,15 +949,16 @@ api-key. Otherwise (dev/demo) config comes from the query string. The postMessag
948
949
  **fails closed**: with no `parentOrigin`, the embed neither accepts commands nor posts
949
950
  events.
950
951
 
951
- | Field | Type | Default | Description |
952
- | -------------- | ----------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
953
- | `baseUrl` | `string` | — | **Required.** API base URL. |
954
- | `matchUrn` | `string` | — | **Required.** Match to play. |
955
- | `apiKey` | `string` | — | **Required.** Inject server‑side in production; query param is dev‑only. |
956
- | `parentOrigin` | `string` | — | Origin to post events to and accept commands from. **Set this in production** (`mountEmbed` sets it automatically). When absent, the iframe posts to `*` and skips the inbound origin check. |
957
- | `autoplay` | `boolean` | `true` | Start playback once ready. |
958
- | `muted` | `boolean` | `true` | Start muted (recommended for autoplay). |
959
- | `waitForLive` | [`WaitForLive`](#waitforlive) | `true` | Arm and auto‑play at go‑live. |
952
+ | Field | Type | Default | Description |
953
+ | -------------- | ---------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
954
+ | `baseUrl` | `string` | — | **Required.** API base URL. |
955
+ | `matchUrn` | `string` | — | **Required.** Match to play. |
956
+ | `apiKey` | `string` | — | **Required.** Inject server‑side in production; query param is dev‑only. |
957
+ | `parentOrigin` | `string` | — | Origin to post events to and accept commands from. **Set this in production** (`mountEmbed` sets it automatically). When absent, the iframe posts to `*` and skips the inbound origin check. |
958
+ | `autoplay` | `boolean` | `true` | Start playback once ready. |
959
+ | `muted` | `boolean` | `true` | Start muted (recommended for autoplay). |
960
+ | `waitForLive` | [`WaitForLive`](#waitforlive) | `true` | Arm and auto‑play at go‑live. |
961
+ | `analytics` | `boolean \| { endpoint?: string }` | `true` | QoE beacons, forwarded verbatim to [`createPlayer`](#createplayer). **Injected config only** — never read from the query string, so a link cannot redirect a session's beacons. Set `{ endpoint }` when `baseUrl` is not a `feed[-dev].<domain>` host, since nothing can be derived and analytics is otherwise off. ⚠️ [`mountEmbed`](#mountembed) configures the page via query string, so it cannot carry this: a `mountEmbed` integration on a non-feed `baseUrl` has no way to enable beacons and must self-host the embed page instead. |
960
962
 
961
963
  <a id="frametohost"></a>
962
964
  <a id="hosttoframe"></a>
package/embed/index.html CHANGED
@@ -22,8 +22,13 @@
22
22
  2) INJECT config server-side as window.HAVIK_EMBED_CONFIG (baseUrl, matchUrn,
23
23
  apiKey, parentOrigin). When injected, the URL query string is IGNORED for
24
24
  security; the api-key must NOT travel in the query in production.
25
+ 3) If baseUrl is NOT a feed[-dev].<domain> host (your own CDN or gateway
26
+ fronts the streams API), also inject
27
+ analytics: { endpoint: 'https://beacons[-dev].<domain>' } — otherwise no
28
+ beacons endpoint can be derived and QoE analytics is silently off.
29
+ `analytics` is honoured from the injected config only, never the query.
25
30
  -->
26
- <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.8.0/dist/havik-player.global.js" integrity="sha384-3wwFxlZvuLLFtcjEtRI9dqqHXLOgurt0RsZ6ifTHhyiC0LMJnbijjgvp9dJTbDiW" crossorigin="anonymous"></script>
31
+ <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.9.1/dist/havik-player.global.js" integrity="sha384-KT7nnDHWKZkCl+wBu1Bxa4pPyxzwIOriF75qrCyKk6iaBgzB6xDFhVIAdDZsXkqt" crossorigin="anonymous"></script>
27
32
  <script>
28
33
  HavikPlayer.bootEmbed();
29
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.8.0/dist/havik-player.global.js" integrity="sha384-3wwFxlZvuLLFtcjEtRI9dqqHXLOgurt0RsZ6ifTHhyiC0LMJnbijjgvp9dJTbDiW" crossorigin="anonymous"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.9.1/dist/havik-player.global.js" integrity="sha384-KT7nnDHWKZkCl+wBu1Bxa4pPyxzwIOriF75qrCyKk6iaBgzB6xDFhVIAdDZsXkqt" 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.8.0",
3
+ "version": "2.9.1",
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,