@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.d.cts CHANGED
@@ -431,12 +431,30 @@ declare class MebiusCaptions extends TypedEmitter<CaptionsEventMap> {
431
431
  * every 100ms tick. Comparing revs emits exactly once per actual revision.
432
432
  */
433
433
  private readonly shown;
434
+ /** Kept so the feed can be reopened after a hidden-tab suspend. */
435
+ private streamId;
436
+ private hiddenTimer;
437
+ private onVisibility;
434
438
  /** @internal */
435
439
  constructor(signaling: SignalingClient, player: MebiusPlayer, opts: CaptionsOptions);
436
440
  /** Open the SSE connection and begin emitting segments for `streamId`. */
437
441
  start(streamId: string): void;
438
442
  /** Close the connection and drop all buffered segments. */
439
443
  stop(): void;
444
+ private openFeed;
445
+ private closeFeed;
446
+ /**
447
+ * Drop the feed while the page is hidden, restore it when it comes back.
448
+ *
449
+ * Nothing here is about rendering — a hidden tab shows no captions either
450
+ * way. It is about not holding a subscriber open, since that subscriber is
451
+ * what keeps a billed session alive on the engine.
452
+ *
453
+ * Buffered segments are deliberately kept across a suspend: they age out on
454
+ * their own (STALE_MS) and clearing them would blank the screen on return
455
+ * for no benefit.
456
+ */
457
+ private watchVisibility;
440
458
  private onFrame;
441
459
  private tick;
442
460
  }
package/dist/index.d.ts CHANGED
@@ -431,12 +431,30 @@ declare class MebiusCaptions extends TypedEmitter<CaptionsEventMap> {
431
431
  * every 100ms tick. Comparing revs emits exactly once per actual revision.
432
432
  */
433
433
  private readonly shown;
434
+ /** Kept so the feed can be reopened after a hidden-tab suspend. */
435
+ private streamId;
436
+ private hiddenTimer;
437
+ private onVisibility;
434
438
  /** @internal */
435
439
  constructor(signaling: SignalingClient, player: MebiusPlayer, opts: CaptionsOptions);
436
440
  /** Open the SSE connection and begin emitting segments for `streamId`. */
437
441
  start(streamId: string): void;
438
442
  /** Close the connection and drop all buffered segments. */
439
443
  stop(): void;
444
+ private openFeed;
445
+ private closeFeed;
446
+ /**
447
+ * Drop the feed while the page is hidden, restore it when it comes back.
448
+ *
449
+ * Nothing here is about rendering — a hidden tab shows no captions either
450
+ * way. It is about not holding a subscriber open, since that subscriber is
451
+ * what keeps a billed session alive on the engine.
452
+ *
453
+ * Buffered segments are deliberately kept across a suspend: they age out on
454
+ * their own (STALE_MS) and clearing them would blank the screen on return
455
+ * for no benefit.
456
+ */
457
+ private watchVisibility;
440
458
  private onFrame;
441
459
  private tick;
442
460
  }
@@ -43428,6 +43428,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43428
43428
  var TICK_MS = 100;
43429
43429
  var STALE_MS = 5e3;
43430
43430
  var MAX_QUEUE_MS = 1500;
43431
+ var HIDDEN_GRACE_MS = 6e4;
43431
43432
  var MebiusCaptions = class extends TypedEmitter {
43432
43433
  /** @internal */
43433
43434
  constructor(signaling, player, opts) {
@@ -43445,25 +43446,74 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43445
43446
  * every 100ms tick. Comparing revs emits exactly once per actual revision.
43446
43447
  */
43447
43448
  this.shown = /* @__PURE__ */ new Map();
43449
+ /** Kept so the feed can be reopened after a hidden-tab suspend. */
43450
+ this.streamId = null;
43451
+ this.hiddenTimer = null;
43452
+ this.onVisibility = null;
43448
43453
  }
43449
43454
  /** Open the SSE connection and begin emitting segments for `streamId`. */
43450
43455
  start(streamId) {
43451
43456
  if (this.es) return;
43452
- const url = this.signaling.captionsUrl(streamId, this.opts.lang);
43453
- const es = new EventSource(url);
43457
+ this.streamId = streamId;
43458
+ this.openFeed();
43459
+ this.watchVisibility();
43460
+ }
43461
+ /** Close the connection and drop all buffered segments. */
43462
+ stop() {
43463
+ this.streamId = null;
43464
+ if (this.onVisibility && typeof document !== "undefined") {
43465
+ document.removeEventListener("visibilitychange", this.onVisibility);
43466
+ }
43467
+ this.onVisibility = null;
43468
+ if (this.hiddenTimer) clearTimeout(this.hiddenTimer);
43469
+ this.hiddenTimer = null;
43470
+ this.closeFeed();
43471
+ this.pending.clear();
43472
+ this.shown.clear();
43473
+ }
43474
+ openFeed() {
43475
+ if (this.es || !this.streamId) return;
43476
+ const es = new EventSource(this.signaling.captionsUrl(this.streamId, this.opts.lang));
43454
43477
  es.onmessage = (ev) => this.onFrame(ev);
43455
43478
  es.onerror = () => this.emit("error", void 0);
43456
43479
  this.es = es;
43457
43480
  this.timer = setInterval(() => this.tick(), TICK_MS);
43458
43481
  }
43459
- /** Close the connection and drop all buffered segments. */
43460
- stop() {
43482
+ closeFeed() {
43461
43483
  this.es?.close();
43462
43484
  this.es = null;
43463
43485
  if (this.timer) clearInterval(this.timer);
43464
43486
  this.timer = null;
43465
- this.pending.clear();
43466
- this.shown.clear();
43487
+ }
43488
+ /**
43489
+ * Drop the feed while the page is hidden, restore it when it comes back.
43490
+ *
43491
+ * Nothing here is about rendering — a hidden tab shows no captions either
43492
+ * way. It is about not holding a subscriber open, since that subscriber is
43493
+ * what keeps a billed session alive on the engine.
43494
+ *
43495
+ * Buffered segments are deliberately kept across a suspend: they age out on
43496
+ * their own (STALE_MS) and clearing them would blank the screen on return
43497
+ * for no benefit.
43498
+ */
43499
+ watchVisibility() {
43500
+ if (typeof document === "undefined") return;
43501
+ this.onVisibility = () => {
43502
+ if (document.visibilityState === "hidden") {
43503
+ if (this.hiddenTimer) return;
43504
+ this.hiddenTimer = setTimeout(() => {
43505
+ this.hiddenTimer = null;
43506
+ this.closeFeed();
43507
+ }, HIDDEN_GRACE_MS);
43508
+ return;
43509
+ }
43510
+ if (this.hiddenTimer) {
43511
+ clearTimeout(this.hiddenTimer);
43512
+ this.hiddenTimer = null;
43513
+ }
43514
+ this.openFeed();
43515
+ };
43516
+ document.addEventListener("visibilitychange", this.onVisibility);
43467
43517
  }
43468
43518
  onFrame(ev) {
43469
43519
  let frame;