@mebius-io/web 0.6.0 → 0.6.2

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/index.js CHANGED
@@ -810,6 +810,7 @@ function normalize(c, fallback) {
810
810
  var TICK_MS = 100;
811
811
  var STALE_MS = 5e3;
812
812
  var MAX_QUEUE_MS = 1500;
813
+ var HIDDEN_GRACE_MS = 6e4;
813
814
  var MebiusCaptions = class extends TypedEmitter {
814
815
  /** @internal */
815
816
  constructor(signaling, player, opts) {
@@ -827,25 +828,74 @@ var MebiusCaptions = class extends TypedEmitter {
827
828
  * every 100ms tick. Comparing revs emits exactly once per actual revision.
828
829
  */
829
830
  this.shown = /* @__PURE__ */ new Map();
831
+ /** Kept so the feed can be reopened after a hidden-tab suspend. */
832
+ this.streamId = null;
833
+ this.hiddenTimer = null;
834
+ this.onVisibility = null;
830
835
  }
831
836
  /** Open the SSE connection and begin emitting segments for `streamId`. */
832
837
  start(streamId) {
833
838
  if (this.es) return;
834
- const url = this.signaling.captionsUrl(streamId, this.opts.lang);
835
- const es = new EventSource(url);
839
+ this.streamId = streamId;
840
+ this.openFeed();
841
+ this.watchVisibility();
842
+ }
843
+ /** Close the connection and drop all buffered segments. */
844
+ stop() {
845
+ this.streamId = null;
846
+ if (this.onVisibility && typeof document !== "undefined") {
847
+ document.removeEventListener("visibilitychange", this.onVisibility);
848
+ }
849
+ this.onVisibility = null;
850
+ if (this.hiddenTimer) clearTimeout(this.hiddenTimer);
851
+ this.hiddenTimer = null;
852
+ this.closeFeed();
853
+ this.pending.clear();
854
+ this.shown.clear();
855
+ }
856
+ openFeed() {
857
+ if (this.es || !this.streamId) return;
858
+ const es = new EventSource(this.signaling.captionsUrl(this.streamId, this.opts.lang));
836
859
  es.onmessage = (ev) => this.onFrame(ev);
837
860
  es.onerror = () => this.emit("error", void 0);
838
861
  this.es = es;
839
862
  this.timer = setInterval(() => this.tick(), TICK_MS);
840
863
  }
841
- /** Close the connection and drop all buffered segments. */
842
- stop() {
864
+ closeFeed() {
843
865
  this.es?.close();
844
866
  this.es = null;
845
867
  if (this.timer) clearInterval(this.timer);
846
868
  this.timer = null;
847
- this.pending.clear();
848
- this.shown.clear();
869
+ }
870
+ /**
871
+ * Drop the feed while the page is hidden, restore it when it comes back.
872
+ *
873
+ * Nothing here is about rendering — a hidden tab shows no captions either
874
+ * way. It is about not holding a subscriber open, since that subscriber is
875
+ * what keeps a billed session alive on the engine.
876
+ *
877
+ * Buffered segments are deliberately kept across a suspend: they age out on
878
+ * their own (STALE_MS) and clearing them would blank the screen on return
879
+ * for no benefit.
880
+ */
881
+ watchVisibility() {
882
+ if (typeof document === "undefined") return;
883
+ this.onVisibility = () => {
884
+ if (document.visibilityState === "hidden") {
885
+ if (this.hiddenTimer) return;
886
+ this.hiddenTimer = setTimeout(() => {
887
+ this.hiddenTimer = null;
888
+ this.closeFeed();
889
+ }, HIDDEN_GRACE_MS);
890
+ return;
891
+ }
892
+ if (this.hiddenTimer) {
893
+ clearTimeout(this.hiddenTimer);
894
+ this.hiddenTimer = null;
895
+ }
896
+ this.openFeed();
897
+ };
898
+ document.addEventListener("visibilitychange", this.onVisibility);
849
899
  }
850
900
  onFrame(ev) {
851
901
  let frame;