@oddin-gg/havik-player 1.17.0 → 1.18.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/dist/havik-player.global.js +2 -2
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +41 -18
- package/dist/index.js.map +1 -1
- package/docs/API.md +57 -47
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -125,6 +125,16 @@ export declare interface CreatePlayerOptions {
|
|
|
125
125
|
statusOverlays?: boolean;
|
|
126
126
|
/** Message on the end-of-stream card. Default: "This stream has ended". */
|
|
127
127
|
endedMessage?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Message shown instead of {@link endedMessage} when the stream stopped but
|
|
130
|
+
* the match is still running (the server's `interrupted` reason — an ingest
|
|
131
|
+
* outage that outlived the bridge's reconnect hold). Default:
|
|
132
|
+
* "Reconnecting — please stand by" — a promise the player keeps while
|
|
133
|
+
* {@link rejoinOnLive} is on (the default; it also needs
|
|
134
|
+
* {@link liveStateEvents}). Override it if you disable auto-rejoin, since
|
|
135
|
+
* nothing will then re-attach on its own.
|
|
136
|
+
*/
|
|
137
|
+
interruptedMessage?: string;
|
|
128
138
|
/** Message on the fatal-error card. Default: the error's own message. */
|
|
129
139
|
errorMessage?: string;
|
|
130
140
|
/**
|
|
@@ -304,6 +314,22 @@ export declare interface EmbedHandle {
|
|
|
304
314
|
destroy(): void;
|
|
305
315
|
}
|
|
306
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Why a stream ended (havik-streams #191). `ended` alone cannot tell a viewer
|
|
319
|
+
* whether the MATCH finished or only its stream dropped — two situations that
|
|
320
|
+
* deserve opposite messaging:
|
|
321
|
+
*
|
|
322
|
+
* - `interrupted` — the match is still running; no session is serving (an
|
|
323
|
+
* ingest outage that outlived the bridge's reconnect grace). Play is
|
|
324
|
+
* expected to resume, so surface "reconnecting", never "this has ended".
|
|
325
|
+
* - `match_ended` — the match itself finished. A real end.
|
|
326
|
+
*
|
|
327
|
+
* Absent on every other state, and absent entirely from servers predating the
|
|
328
|
+
* field — so treat `undefined` as "unknown, assume a real end" (what clients
|
|
329
|
+
* did before it existed).
|
|
330
|
+
*/
|
|
331
|
+
export declare type EndedReason = 'interrupted' | 'match_ended';
|
|
332
|
+
|
|
307
333
|
/**
|
|
308
334
|
* Enter fullscreen for `video`, walking the capability ladder above.
|
|
309
335
|
* `container` is the element to present in element fullscreen (defaults to
|
|
@@ -525,6 +551,8 @@ export declare type LiveState = 'live' | 'upcoming' | 'ended' | 'gone';
|
|
|
525
551
|
export declare interface LiveStateEvent {
|
|
526
552
|
matchUrn: string;
|
|
527
553
|
state: LiveState;
|
|
554
|
+
/** Set only on `ended`; see {@link EndedReason}. */
|
|
555
|
+
reason?: EndedReason;
|
|
528
556
|
/** RFC3339 server timestamp the transition was stamped at. */
|
|
529
557
|
serverTime: string;
|
|
530
558
|
}
|
|
@@ -647,6 +675,9 @@ export declare interface PlaybackStats {
|
|
|
647
675
|
export declare interface Player {
|
|
648
676
|
readonly state: PlayerState;
|
|
649
677
|
readonly descriptor: StreamDescriptor | null;
|
|
678
|
+
/** Why the stream ended, when the server said so; null otherwise. Cleared
|
|
679
|
+
* when fresh playback commits (including a rejoin). See EndedReason. */
|
|
680
|
+
readonly endedReason: EndedReason | null;
|
|
650
681
|
play(): Promise<void>;
|
|
651
682
|
pause(): void;
|
|
652
683
|
setMuted(muted: boolean): void;
|
|
@@ -696,7 +727,15 @@ export declare interface PlayerEventMap {
|
|
|
696
727
|
playing: void;
|
|
697
728
|
buffering: void;
|
|
698
729
|
paused: void;
|
|
699
|
-
|
|
730
|
+
/**
|
|
731
|
+
* End of stream. The payload says WHY when the server told us
|
|
732
|
+
* (`interrupted` = the match is still running and play should resume, so
|
|
733
|
+
* do NOT say "ended"; `match_ended` = a real end); `undefined` means the
|
|
734
|
+
* end came from the HLS staleness watchdog or a server predating the
|
|
735
|
+
* field — treat it as a real end, the pre-existing behavior. Fires again
|
|
736
|
+
* with `match_ended` if an interruption later turns out to be the end.
|
|
737
|
+
*/
|
|
738
|
+
ended: EndedReason | undefined;
|
|
700
739
|
error: PlaybackError;
|
|
701
740
|
/** Non-fatal condition (e.g. FairPlay-not-implemented passthrough, a failed license refresh). Playback continues. */
|
|
702
741
|
warning: PlaybackError;
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,16 @@ export declare interface CreatePlayerOptions {
|
|
|
125
125
|
statusOverlays?: boolean;
|
|
126
126
|
/** Message on the end-of-stream card. Default: "This stream has ended". */
|
|
127
127
|
endedMessage?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Message shown instead of {@link endedMessage} when the stream stopped but
|
|
130
|
+
* the match is still running (the server's `interrupted` reason — an ingest
|
|
131
|
+
* outage that outlived the bridge's reconnect hold). Default:
|
|
132
|
+
* "Reconnecting — please stand by" — a promise the player keeps while
|
|
133
|
+
* {@link rejoinOnLive} is on (the default; it also needs
|
|
134
|
+
* {@link liveStateEvents}). Override it if you disable auto-rejoin, since
|
|
135
|
+
* nothing will then re-attach on its own.
|
|
136
|
+
*/
|
|
137
|
+
interruptedMessage?: string;
|
|
128
138
|
/** Message on the fatal-error card. Default: the error's own message. */
|
|
129
139
|
errorMessage?: string;
|
|
130
140
|
/**
|
|
@@ -304,6 +314,22 @@ export declare interface EmbedHandle {
|
|
|
304
314
|
destroy(): void;
|
|
305
315
|
}
|
|
306
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Why a stream ended (havik-streams #191). `ended` alone cannot tell a viewer
|
|
319
|
+
* whether the MATCH finished or only its stream dropped — two situations that
|
|
320
|
+
* deserve opposite messaging:
|
|
321
|
+
*
|
|
322
|
+
* - `interrupted` — the match is still running; no session is serving (an
|
|
323
|
+
* ingest outage that outlived the bridge's reconnect grace). Play is
|
|
324
|
+
* expected to resume, so surface "reconnecting", never "this has ended".
|
|
325
|
+
* - `match_ended` — the match itself finished. A real end.
|
|
326
|
+
*
|
|
327
|
+
* Absent on every other state, and absent entirely from servers predating the
|
|
328
|
+
* field — so treat `undefined` as "unknown, assume a real end" (what clients
|
|
329
|
+
* did before it existed).
|
|
330
|
+
*/
|
|
331
|
+
export declare type EndedReason = 'interrupted' | 'match_ended';
|
|
332
|
+
|
|
307
333
|
/**
|
|
308
334
|
* Enter fullscreen for `video`, walking the capability ladder above.
|
|
309
335
|
* `container` is the element to present in element fullscreen (defaults to
|
|
@@ -525,6 +551,8 @@ export declare type LiveState = 'live' | 'upcoming' | 'ended' | 'gone';
|
|
|
525
551
|
export declare interface LiveStateEvent {
|
|
526
552
|
matchUrn: string;
|
|
527
553
|
state: LiveState;
|
|
554
|
+
/** Set only on `ended`; see {@link EndedReason}. */
|
|
555
|
+
reason?: EndedReason;
|
|
528
556
|
/** RFC3339 server timestamp the transition was stamped at. */
|
|
529
557
|
serverTime: string;
|
|
530
558
|
}
|
|
@@ -647,6 +675,9 @@ export declare interface PlaybackStats {
|
|
|
647
675
|
export declare interface Player {
|
|
648
676
|
readonly state: PlayerState;
|
|
649
677
|
readonly descriptor: StreamDescriptor | null;
|
|
678
|
+
/** Why the stream ended, when the server said so; null otherwise. Cleared
|
|
679
|
+
* when fresh playback commits (including a rejoin). See EndedReason. */
|
|
680
|
+
readonly endedReason: EndedReason | null;
|
|
650
681
|
play(): Promise<void>;
|
|
651
682
|
pause(): void;
|
|
652
683
|
setMuted(muted: boolean): void;
|
|
@@ -696,7 +727,15 @@ export declare interface PlayerEventMap {
|
|
|
696
727
|
playing: void;
|
|
697
728
|
buffering: void;
|
|
698
729
|
paused: void;
|
|
699
|
-
|
|
730
|
+
/**
|
|
731
|
+
* End of stream. The payload says WHY when the server told us
|
|
732
|
+
* (`interrupted` = the match is still running and play should resume, so
|
|
733
|
+
* do NOT say "ended"; `match_ended` = a real end); `undefined` means the
|
|
734
|
+
* end came from the HLS staleness watchdog or a server predating the
|
|
735
|
+
* field — treat it as a real end, the pre-existing behavior. Fires again
|
|
736
|
+
* with `match_ended` if an interruption later turns out to be the end.
|
|
737
|
+
*/
|
|
738
|
+
ended: EndedReason | undefined;
|
|
700
739
|
error: PlaybackError;
|
|
701
740
|
/** Non-fatal condition (e.g. FairPlay-not-implemented passthrough, a failed license refresh). Playback continues. */
|
|
702
741
|
warning: PlaybackError;
|
package/dist/index.js
CHANGED
|
@@ -1408,7 +1408,7 @@ var qe = class {
|
|
|
1408
1408
|
keepalive: !0
|
|
1409
1409
|
}).catch(() => {});
|
|
1410
1410
|
}
|
|
1411
|
-
}, Je = "1.
|
|
1411
|
+
}, Je = "1.18.0", W = {
|
|
1412
1412
|
accent: "#E1B600",
|
|
1413
1413
|
accentText: "#1B1C23",
|
|
1414
1414
|
background: "#0e0f13",
|
|
@@ -1505,18 +1505,20 @@ var tt = class {
|
|
|
1505
1505
|
menu;
|
|
1506
1506
|
statusBox = null;
|
|
1507
1507
|
statusKind = null;
|
|
1508
|
+
statusMsg = null;
|
|
1508
1509
|
hasPlayed = !1;
|
|
1509
1510
|
lastError = null;
|
|
1510
1511
|
logoUrl;
|
|
1511
1512
|
statusOverlays;
|
|
1512
1513
|
endedMessage;
|
|
1514
|
+
interruptedMessage;
|
|
1513
1515
|
errorMessage;
|
|
1514
1516
|
hideTimer;
|
|
1515
1517
|
openMenu = null;
|
|
1516
1518
|
scrubbing = !1;
|
|
1517
1519
|
offFns = [];
|
|
1518
1520
|
constructor(e) {
|
|
1519
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Qe, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Xe(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
|
|
1521
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Qe, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, Xe(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
|
|
1520
1522
|
let t = (e, t, n) => {
|
|
1521
1523
|
let r = this.doc.createElement(e);
|
|
1522
1524
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1570,7 +1572,7 @@ var tt = class {
|
|
|
1570
1572
|
}), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
|
|
1571
1573
|
let t = Number(this.volSlider.value);
|
|
1572
1574
|
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
1573
|
-
}), this.on(this.liveGo, "click", () => this.player.seekToLive()), this.on(this.pipBtn, "click", () => void this.togglePip()), this.on(this.fsBtn, "click", () => void this.toggleFullscreen()), this.on(this.ccBtn, "click", () => this.toggleMenu("cc")), this.on(this.audioBtn, "click", () => this.toggleMenu("audio")), this.on(this.qualityBtn, "click", () => this.toggleMenu("quality")), this.on(this.seek, "pointerdown", (e) => this.onSeekDown(e)), this.on(this.seek, "keydown", (e) => this.onSeekKey(e)), this.on(e, "play", () => this.syncPlayPause()), this.on(e, "pause", () => this.syncPlayPause()), this.on(e, "volumechange", () => this.syncVolume()), this.on(e, "timeupdate", () => this.syncProgress()), this.on(e, "progress", () => this.syncProgress()), this.on(e, "durationchange", () => this.syncProgress()), this.offFns.push(this.player.on("statechange", () => this.syncState())), this.offFns.push(this.player.on("ready", () => this.refreshTracks())), this.offFns.push(this.player.on("qualitychange", () => this.onTracksChanged())), this.offFns.push(this.player.on("audiotrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("texttrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("pipchange", (e) => this.syncPip(e))), this.offFns.push(this.player.on("stats", () => this.syncLive())), this.offFns.push(this.player.on("error", (e) => {
|
|
1575
|
+
}), this.on(this.liveGo, "click", () => this.player.seekToLive()), this.on(this.pipBtn, "click", () => void this.togglePip()), this.on(this.fsBtn, "click", () => void this.toggleFullscreen()), this.on(this.ccBtn, "click", () => this.toggleMenu("cc")), this.on(this.audioBtn, "click", () => this.toggleMenu("audio")), this.on(this.qualityBtn, "click", () => this.toggleMenu("quality")), this.on(this.seek, "pointerdown", (e) => this.onSeekDown(e)), this.on(this.seek, "keydown", (e) => this.onSeekKey(e)), this.on(e, "play", () => this.syncPlayPause()), this.on(e, "pause", () => this.syncPlayPause()), this.on(e, "volumechange", () => this.syncVolume()), this.on(e, "timeupdate", () => this.syncProgress()), this.on(e, "progress", () => this.syncProgress()), this.on(e, "durationchange", () => this.syncProgress()), this.offFns.push(this.player.on("statechange", () => this.syncState())), this.offFns.push(this.player.on("ended", () => this.syncState())), this.offFns.push(this.player.on("ready", () => this.refreshTracks())), this.offFns.push(this.player.on("qualitychange", () => this.onTracksChanged())), this.offFns.push(this.player.on("audiotrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("texttrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("pipchange", (e) => this.syncPip(e))), this.offFns.push(this.player.on("stats", () => this.syncLive())), this.offFns.push(this.player.on("error", (e) => {
|
|
1574
1576
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
1575
1577
|
})), this.on(this.root, "pointermove", () => this.poke()), this.on(this.root, "pointerleave", () => this.scheduleHide(0)), this.on(this.root, "focusin", () => this.poke()), this.on(this.root, "keydown", (e) => this.onKey(e)), this.on(this.doc, "pointerdown", (e) => this.onDocPointer(e), { capture: !0 }), this.on(this.doc, "fullscreenchange", () => this.syncFullscreen()), this.on(this.doc, "webkitfullscreenchange", () => this.syncFullscreen()), this.on(e, "webkitbeginfullscreen", () => this.syncFullscreen()), this.on(e, "webkitendfullscreen", () => this.syncFullscreen()), this.syncState();
|
|
1576
1578
|
}
|
|
@@ -1591,24 +1593,28 @@ var tt = class {
|
|
|
1591
1593
|
let t = null;
|
|
1592
1594
|
this.statusOverlays && (e === "ended" ? t = "ended" : e === "error" ? t = "error" : e === "waiting" ? t = "waiting" : !this.hasPlayed && (e === "idle" || e === "paused") && (t = "preplay")), this.renderStatus(t);
|
|
1593
1595
|
}
|
|
1596
|
+
statusMessageFor(e) {
|
|
1597
|
+
return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage ?? this.lastError?.message ?? "Playback unavailable" : null;
|
|
1598
|
+
}
|
|
1594
1599
|
renderStatus(e) {
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1600
|
+
let t = this.statusMessageFor(e);
|
|
1601
|
+
if (e === this.statusKind && t === this.statusMsg || (this.statusKind = e, this.statusMsg = t, this.statusBox?.remove(), this.statusBox = null, this.root.classList.toggle("havik-has-status", !!e), !e)) return;
|
|
1602
|
+
let n = this.doc.createElement("div");
|
|
1603
|
+
if (n.className = `havik-status havik-status--${e}`, this.logoUrl) {
|
|
1598
1604
|
let e = this.doc.createElement("div");
|
|
1599
|
-
e.className = "havik-status__logo",
|
|
1605
|
+
e.className = "havik-status__logo", n.append(e);
|
|
1600
1606
|
}
|
|
1601
1607
|
if (e === "preplay") {
|
|
1602
1608
|
let e = this.doc.createElement("button");
|
|
1603
|
-
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()),
|
|
1609
|
+
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
|
|
1604
1610
|
} else {
|
|
1605
|
-
let
|
|
1606
|
-
if (
|
|
1611
|
+
let r = this.doc.createElement("div");
|
|
1612
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !$e.has(this.lastError.code)) {
|
|
1607
1613
|
let e = this.doc.createElement("button");
|
|
1608
|
-
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()),
|
|
1614
|
+
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
|
|
1609
1615
|
}
|
|
1610
1616
|
}
|
|
1611
|
-
this.root.append(
|
|
1617
|
+
this.root.append(n), this.statusBox = n, this.show();
|
|
1612
1618
|
}
|
|
1613
1619
|
syncVolume() {
|
|
1614
1620
|
let e = this.video, t = e.muted || e.volume === 0;
|
|
@@ -1846,6 +1852,7 @@ var lt = class {
|
|
|
1846
1852
|
loadGen = 0;
|
|
1847
1853
|
liveStateSub = null;
|
|
1848
1854
|
endedHandled = !1;
|
|
1855
|
+
_endedReason = null;
|
|
1849
1856
|
rejoinTimer = null;
|
|
1850
1857
|
armingSub = null;
|
|
1851
1858
|
armSseTimer = null;
|
|
@@ -1862,6 +1869,9 @@ var lt = class {
|
|
|
1862
1869
|
get descriptor() {
|
|
1863
1870
|
return this._descriptor;
|
|
1864
1871
|
}
|
|
1872
|
+
get endedReason() {
|
|
1873
|
+
return this._endedReason;
|
|
1874
|
+
}
|
|
1865
1875
|
async start() {
|
|
1866
1876
|
this.opts.muted && (this.opts.video.muted = !0), this.opts.poster && (this.opts.video.poster = this.opts.poster), this.mountControls(), this.attachPipListeners(), this.setState("loading");
|
|
1867
1877
|
let e = ++this.loadGen, t = this.resolveOptions();
|
|
@@ -1936,7 +1946,13 @@ var lt = class {
|
|
|
1936
1946
|
}));
|
|
1937
1947
|
}, g = () => {
|
|
1938
1948
|
a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
|
|
1939
|
-
a || p()
|
|
1949
|
+
if (!(a || p())) {
|
|
1950
|
+
if (e instanceof n && o(e.code)) {
|
|
1951
|
+
h();
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
a = !0, this.closeArming(), c.abort(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e)));
|
|
1955
|
+
}
|
|
1940
1956
|
});
|
|
1941
1957
|
}, _ = (e) => {
|
|
1942
1958
|
this.armSseTimer && clearTimeout(this.armSseTimer), this.armSseTimer = setTimeout(() => {
|
|
@@ -1968,7 +1984,7 @@ var lt = class {
|
|
|
1968
1984
|
}
|
|
1969
1985
|
async onResolved(e, t) {
|
|
1970
1986
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1971
|
-
this._descriptor = e, this.endedHandled = !1;
|
|
1987
|
+
this._descriptor = e, this.endedHandled = !1, this._endedReason = null;
|
|
1972
1988
|
let n = await s(this.opts.credential);
|
|
1973
1989
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1974
1990
|
this.setState("loading");
|
|
@@ -2067,7 +2083,7 @@ var lt = class {
|
|
|
2067
2083
|
matchUrn: this.opts.matchUrn,
|
|
2068
2084
|
credential: this.opts.credential,
|
|
2069
2085
|
onState: (t) => {
|
|
2070
|
-
this.destroyed || e !== this.loadGen || (t.state === "gone" ? this.onStreamEnded(!0) : t.state === "ended" ? this.onStreamEnded() : t.state === "live" && this.endedHandled && this.scheduleRejoin(e));
|
|
2086
|
+
this.destroyed || e !== this.loadGen || (t.state === "gone" ? this.onStreamEnded(!0, t.reason ?? "match_ended") : t.state === "ended" ? this.onStreamEnded(!1, t.reason) : t.state === "live" && this.endedHandled && this.scheduleRejoin(e));
|
|
2071
2087
|
},
|
|
2072
2088
|
onAlive: () => {
|
|
2073
2089
|
r = 0, i = !1;
|
|
@@ -2080,8 +2096,14 @@ var lt = class {
|
|
|
2080
2096
|
closeLiveState() {
|
|
2081
2097
|
this.liveStateSub?.close(), this.liveStateSub = null;
|
|
2082
2098
|
}
|
|
2083
|
-
onStreamEnded(e = !1) {
|
|
2084
|
-
|
|
2099
|
+
onStreamEnded(e = !1, t) {
|
|
2100
|
+
if (!this.destroyed) {
|
|
2101
|
+
if ((e || !this.rejoinEnabled()) && (this.cancelRejoin(), this.closeLiveState()), this.endedHandled) {
|
|
2102
|
+
t === "match_ended" && this._endedReason !== "match_ended" && (this._endedReason = t, this.emit("ended", t));
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
this.endedHandled = !0, this._endedReason = t ?? null, this.stopRefresh(), this.engine?.stop(), this.setState("ended");
|
|
2106
|
+
}
|
|
2085
2107
|
}
|
|
2086
2108
|
rejoinEnabled() {
|
|
2087
2109
|
return this.opts.rejoinOnLive !== !1 && this.opts.liveStateEvents !== !1;
|
|
@@ -2120,6 +2142,7 @@ var lt = class {
|
|
|
2120
2142
|
logoUrl: this.opts.theme?.logoUrl,
|
|
2121
2143
|
statusOverlays: this.opts.statusOverlays,
|
|
2122
2144
|
endedMessage: this.opts.endedMessage,
|
|
2145
|
+
interruptedMessage: this.opts.interruptedMessage,
|
|
2123
2146
|
errorMessage: this.opts.errorMessage
|
|
2124
2147
|
});
|
|
2125
2148
|
}
|
|
@@ -2277,7 +2300,7 @@ var lt = class {
|
|
|
2277
2300
|
case "paused":
|
|
2278
2301
|
this.emit("paused");
|
|
2279
2302
|
break;
|
|
2280
|
-
case "ended": this.emit("ended");
|
|
2303
|
+
case "ended": this.emit("ended", this._endedReason ?? void 0);
|
|
2281
2304
|
}
|
|
2282
2305
|
}
|
|
2283
2306
|
emit(e, t) {
|