@oddin-gg/havik-player 2.18.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/docs/API.md CHANGED
@@ -394,6 +394,7 @@ interface Player {
394
394
  exitPictureInPicture(): Promise<void>;
395
395
  enterFullscreen(): Promise<boolean>; // element FS on the player container; iPhone → video-native. false = no mechanism
396
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
397
398
  retry(): Promise<void>; // recover from an error (re-resolve + re-attach)
398
399
  reload(): Promise<void>; // re‑resolve playback (refreshes the license URL) + re‑attach
399
400
  on<E extends PlayerEvent>(event: E, cb: (payload: PlayerEventMap[E]) => void): () => void;
@@ -468,6 +469,17 @@ stream.
468
469
  > effect **after playback has started** (a tap on the pre-play poster is a
469
470
  > no-op there). Everywhere else (desktop, Android, iPad) the button uses
470
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.
471
483
 
472
484
  ```ts
473
485
  type Controls = 'custom' | 'native' | 'none';
@@ -697,6 +709,7 @@ hls.attachMedia(videoElement);
697
709
  function enterVideoFullscreen(video: HTMLVideoElement, container?: HTMLElement): Promise<boolean>;
698
710
  function exitVideoFullscreen(video: HTMLVideoElement, container?: HTMLElement): Promise<void>;
699
711
  function videoFullscreenActive(video: HTMLVideoElement, container?: HTMLElement): boolean;
712
+ function fullscreenSupported(video: HTMLVideoElement, container?: HTMLElement): boolean;
700
713
  ```
701
714
 
702
715
  Fullscreen with the platform quirks handled, so your own fullscreen button
@@ -716,7 +729,13 @@ first; it reports only through the video, never the document), is safe to
716
729
  call when not fullscreen, and is **scoped to your video's presentation**: an
717
730
  unrelated element's fullscreen elsewhere on the page is left alone.
718
731
  `videoFullscreenActive` tells you whether your video's presentation is
719
- currently fullscreen. Pass the same `container` to all three. For fullscreen **state changes**, listen to `fullscreenchange` /
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` /
720
739
  `webkitfullscreenchange` on the document and `webkitbeginfullscreen` /
721
740
  `webkitendfullscreen` on the video; the last two are the only signal for
722
741
  iPhone's video-native fullscreen.
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.18.0/dist/havik-player.global.js" integrity="sha384-bna/v7G8EjUwpvfY3vAwrzvlkh2RO3sMM8ccsC/A0LO1gYZyIluCR/6t5Mj8Qmmj" crossorigin="anonymous"></script>
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>
@@ -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.18.0/dist/havik-player.global.js" integrity="sha384-bna/v7G8EjUwpvfY3vAwrzvlkh2RO3sMM8ccsC/A0LO1gYZyIluCR/6t5Mj8Qmmj" crossorigin="anonymous"></script>
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.18.0",
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,