@oddin-gg/havik-player 1.0.5 → 1.1.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/README.md +8 -1
- 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 +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +81 -73
- package/dist/index.js.map +1 -1
- package/docs/API.md +40 -27
- package/embed/index.html +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -126,6 +126,17 @@ export declare interface CreatePlayerOptions {
|
|
|
126
126
|
endedMessage?: string;
|
|
127
127
|
/** Message on the fatal-error card. Default: the error's own message. */
|
|
128
128
|
errorMessage?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Turn on hls.js's internal console logging — segment/part scheduling, ABR
|
|
131
|
+
* switches, and EME key-session + license traffic. Default: false.
|
|
132
|
+
*
|
|
133
|
+
* For diagnosing a playback problem with Oddin support; leave it off in
|
|
134
|
+
* production (it logs on every fragment, and the EME lines include license
|
|
135
|
+
* request/response metadata). hls.js engine only — a no-op on the native
|
|
136
|
+
* engine, which has no equivalent. Equivalent to `hlsConfig: { debug: true }`,
|
|
137
|
+
* which still wins if you set both.
|
|
138
|
+
*/
|
|
139
|
+
debug?: boolean;
|
|
129
140
|
/** Escape hatch for low-level hls.js tuning. */
|
|
130
141
|
hlsConfig?: Partial<HlsConfig>;
|
|
131
142
|
/** Stats sampling cadence. Default: 2000ms. */
|
package/dist/index.d.ts
CHANGED
|
@@ -126,6 +126,17 @@ export declare interface CreatePlayerOptions {
|
|
|
126
126
|
endedMessage?: string;
|
|
127
127
|
/** Message on the fatal-error card. Default: the error's own message. */
|
|
128
128
|
errorMessage?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Turn on hls.js's internal console logging — segment/part scheduling, ABR
|
|
131
|
+
* switches, and EME key-session + license traffic. Default: false.
|
|
132
|
+
*
|
|
133
|
+
* For diagnosing a playback problem with Oddin support; leave it off in
|
|
134
|
+
* production (it logs on every fragment, and the EME lines include license
|
|
135
|
+
* request/response metadata). hls.js engine only — a no-op on the native
|
|
136
|
+
* engine, which has no equivalent. Equivalent to `hlsConfig: { debug: true }`,
|
|
137
|
+
* which still wins if you set both.
|
|
138
|
+
*/
|
|
139
|
+
debug?: boolean;
|
|
129
140
|
/** Escape hatch for low-level hls.js tuning. */
|
|
130
141
|
hlsConfig?: Partial<HlsConfig>;
|
|
131
142
|
/** Stats sampling cadence. Default: 2000ms. */
|
package/dist/index.js
CHANGED
|
@@ -510,21 +510,21 @@ function I(e, t) {
|
|
|
510
510
|
function L() {
|
|
511
511
|
return e.isSupported();
|
|
512
512
|
}
|
|
513
|
-
var R = 3, z = 5, B = 3, V = 3e4, H = 12e3, U = 3e3, de = 6e3, fe = 3, pe = 9;
|
|
514
|
-
function
|
|
513
|
+
var R = 3, z = 5, B = 3, V = 3e4, H = 12e3, U = 3e3, de = 6e3, fe = 3, pe = 9, me = .5;
|
|
514
|
+
function he(e) {
|
|
515
515
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
516
516
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
519
519
|
e.liveSyncDuration ??= fe;
|
|
520
520
|
let t = Math.max(12, e.liveSyncDuration + pe);
|
|
521
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
521
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), ge(e, e.liveSyncDuration);
|
|
522
522
|
}
|
|
523
|
-
function
|
|
523
|
+
function ge(e, t) {
|
|
524
524
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
525
525
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
526
526
|
}
|
|
527
|
-
var
|
|
527
|
+
var _e = class {
|
|
528
528
|
name = "hls";
|
|
529
529
|
hls = null;
|
|
530
530
|
deps;
|
|
@@ -537,6 +537,7 @@ var ge = class {
|
|
|
537
537
|
networkRetryTimer = null;
|
|
538
538
|
licenseRetryTimer = null;
|
|
539
539
|
onVisibility = null;
|
|
540
|
+
onSeeking = null;
|
|
540
541
|
sawLive = !1;
|
|
541
542
|
lastEndSN = -1;
|
|
542
543
|
lastAdvanceMs = 0;
|
|
@@ -565,10 +566,19 @@ var ge = class {
|
|
|
565
566
|
if (typeof t != "number") return;
|
|
566
567
|
let n = typeof e.targetLatency == "number" && e.targetLatency > 0 ? e.targetLatency : 3;
|
|
567
568
|
t - r.currentTime > Math.max(n * 3, 8) && (r.currentTime = t);
|
|
568
|
-
}, document.addEventListener("visibilitychange", this.onVisibility));
|
|
569
|
+
}, document.addEventListener("visibilitychange", this.onVisibility)), this.onSeeking = () => this.clampOverSeek(), r.addEventListener("seeking", this.onSeeking);
|
|
570
|
+
}
|
|
571
|
+
clampOverSeek() {
|
|
572
|
+
let e = this.hls;
|
|
573
|
+
if (!e || this.chaseSuspended || (e.levels[e.currentLevel] ?? e.levels[e.loadLevel])?.details?.live !== !0) return;
|
|
574
|
+
let t = e.liveSyncPosition;
|
|
575
|
+
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
576
|
+
let { video: n } = this.deps;
|
|
577
|
+
n.currentTime <= t + me || (n.currentTime = t);
|
|
569
578
|
}
|
|
570
579
|
startHls() {
|
|
571
|
-
let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o,
|
|
580
|
+
let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps, u = n.drmEnabled && !!n.drm?.widevine?.licenseUrl, d = {
|
|
581
|
+
debug: s === !0,
|
|
572
582
|
enableWorker: !o,
|
|
573
583
|
lowLatencyMode: o,
|
|
574
584
|
backBufferLength: 90,
|
|
@@ -576,7 +586,7 @@ var ge = class {
|
|
|
576
586
|
maxLiveSyncPlaybackRate: 1.5,
|
|
577
587
|
abrEwmaDefaultEstimate: 1e6,
|
|
578
588
|
capLevelToPlayerSize: !0,
|
|
579
|
-
...
|
|
589
|
+
...u ? {
|
|
580
590
|
emeEnabled: !0,
|
|
581
591
|
drmSystems: { "com.widevine.alpha": { licenseUrl: this.licenseUrl } },
|
|
582
592
|
drmSystemOptions: {
|
|
@@ -589,33 +599,33 @@ var ge = class {
|
|
|
589
599
|
e.open("POST", this.licenseUrl ?? t, !0), e.setRequestHeader("x-api-key", r.apiKey), e.setRequestHeader("X-Match-Urn", n.matchUrn), e.setRequestHeader("X-Device-Id", i), a && e.setRequestHeader("X-User-Id", a);
|
|
590
600
|
}
|
|
591
601
|
} : {},
|
|
592
|
-
...
|
|
602
|
+
...c
|
|
593
603
|
};
|
|
594
|
-
|
|
595
|
-
let
|
|
596
|
-
this.hls =
|
|
597
|
-
this.mediaRecoveries = 0, this.networkRecoveries = 0,
|
|
598
|
-
}),
|
|
604
|
+
he(d);
|
|
605
|
+
let f = new e(d);
|
|
606
|
+
this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
607
|
+
this.mediaRecoveries = 0, this.networkRecoveries = 0, l({ type: "ready" });
|
|
608
|
+
}), f.on(e.Events.FRAG_BUFFERED, () => {
|
|
599
609
|
(this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
|
|
600
610
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.recoveryResetTimer = null;
|
|
601
611
|
}, V));
|
|
602
|
-
}),
|
|
612
|
+
}), f.on(e.Events.LEVEL_LOADED, (e, t) => {
|
|
603
613
|
if (this.detectedLowLatency) return;
|
|
604
614
|
let n = t.details;
|
|
605
|
-
(n?.partList?.length || (n?.partTarget ?? 0) > 0) && (this.detectedLowLatency = !0,
|
|
615
|
+
(n?.partList?.length || (n?.partTarget ?? 0) > 0) && (this.detectedLowLatency = !0, l({
|
|
606
616
|
type: "lowLatencyDetected",
|
|
607
617
|
value: !0
|
|
608
618
|
}));
|
|
609
|
-
}),
|
|
619
|
+
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => this.checkEndStale(), U), f.on(e.Events.LEVEL_SWITCHED, (e, t) => l({
|
|
610
620
|
type: "qualitychange",
|
|
611
621
|
index: t.level
|
|
612
|
-
})),
|
|
622
|
+
})), f.on(e.Events.AUDIO_TRACK_SWITCHED, (e, t) => l({
|
|
613
623
|
type: "audiotrackchange",
|
|
614
624
|
id: t.id
|
|
615
|
-
})),
|
|
625
|
+
})), f.on(e.Events.SUBTITLE_TRACK_SWITCH, (e, t) => l({
|
|
616
626
|
type: "texttrackchange",
|
|
617
627
|
id: t.id
|
|
618
|
-
})),
|
|
628
|
+
})), f.on(e.Events.ERROR, (e, t) => this.onError(t)), f.loadSource(n.manifestUrl), f.attachMedia(t);
|
|
619
629
|
}
|
|
620
630
|
getQualityLevels() {
|
|
621
631
|
return this.hls ? this.hls.levels.map((e, t) => ({
|
|
@@ -731,7 +741,7 @@ var ge = class {
|
|
|
731
741
|
} catch {}
|
|
732
742
|
}
|
|
733
743
|
destroy() {
|
|
734
|
-
if (this.clearRecoveryReset(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
744
|
+
if (this.clearRecoveryReset(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
735
745
|
try {
|
|
736
746
|
this.hls.destroy();
|
|
737
747
|
} catch (e) {
|
|
@@ -815,7 +825,7 @@ var ge = class {
|
|
|
815
825
|
};
|
|
816
826
|
//#endregion
|
|
817
827
|
//#region src/core/engine/native.ts
|
|
818
|
-
function
|
|
828
|
+
function ve(e) {
|
|
819
829
|
return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
|
|
820
830
|
}
|
|
821
831
|
var W = class {
|
|
@@ -946,15 +956,15 @@ function q(e, t) {
|
|
|
946
956
|
}
|
|
947
957
|
//#endregion
|
|
948
958
|
//#region src/ui/styles.ts
|
|
949
|
-
var J = "havik-player-styles",
|
|
950
|
-
function
|
|
959
|
+
var J = "havik-player-styles", ye = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:rgba(255,255,255,.3);border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:70px;opacity:1}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
|
|
960
|
+
function be(e = document) {
|
|
951
961
|
if (e.getElementById(J)) return;
|
|
952
962
|
let t = e.createElement("style");
|
|
953
|
-
t.id = J, t.textContent =
|
|
963
|
+
t.id = J, t.textContent = ye, e.head.appendChild(t);
|
|
954
964
|
}
|
|
955
965
|
//#endregion
|
|
956
966
|
//#region src/ui/icons.ts
|
|
957
|
-
var
|
|
967
|
+
var xe = {
|
|
958
968
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
959
969
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
960
970
|
volumeHigh: "<path d=\"M3 10v4h4l5 5V5L7 10H3zm13.5 2a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4zm-2.5-9v2.06A7 7 0 0 1 14 19v2a9 9 0 0 0 0-18z\"/>",
|
|
@@ -968,20 +978,20 @@ var be = {
|
|
|
968
978
|
retry: "<path d=\"M17.65 6.35A8 8 0 1 0 19.73 13h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>"
|
|
969
979
|
};
|
|
970
980
|
function Y(e) {
|
|
971
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
981
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${xe[e]}</svg>`;
|
|
972
982
|
}
|
|
973
983
|
//#endregion
|
|
974
984
|
//#region src/ui/controls.ts
|
|
975
|
-
var
|
|
985
|
+
var Se = 3e3, Ce = /* @__PURE__ */ new Set([
|
|
976
986
|
"GONE",
|
|
977
987
|
"NOT_FOUND",
|
|
978
988
|
"INVALID_URN",
|
|
979
989
|
"UNAUTHORIZED"
|
|
980
990
|
]);
|
|
981
|
-
function
|
|
982
|
-
return new
|
|
991
|
+
function we(e) {
|
|
992
|
+
return new Te(e);
|
|
983
993
|
}
|
|
984
|
-
var
|
|
994
|
+
var Te = class {
|
|
985
995
|
root;
|
|
986
996
|
video;
|
|
987
997
|
player;
|
|
@@ -1021,7 +1031,7 @@ var we = class {
|
|
|
1021
1031
|
scrubbing = !1;
|
|
1022
1032
|
offFns = [];
|
|
1023
1033
|
constructor(e) {
|
|
1024
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
1034
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Se, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, be(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");
|
|
1025
1035
|
let t = (e, t, n) => {
|
|
1026
1036
|
let r = this.doc.createElement(e);
|
|
1027
1037
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1108,7 +1118,7 @@ var we = class {
|
|
|
1108
1118
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Y("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
|
|
1109
1119
|
} else {
|
|
1110
1120
|
let n = this.doc.createElement("div");
|
|
1111
|
-
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !
|
|
1121
|
+
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Ce.has(this.lastError.code)) {
|
|
1112
1122
|
let e = this.doc.createElement("button");
|
|
1113
1123
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Y("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
|
|
1114
1124
|
}
|
|
@@ -1120,17 +1130,14 @@ var we = class {
|
|
|
1120
1130
|
this.setIcon(this.muteBtn, t ? "volumeMute" : "volumeHigh"), this.muteBtn.setAttribute("aria-label", t ? "Unmute" : "Mute"), this.volSlider.value = String(t ? 0 : e.volume);
|
|
1121
1131
|
}
|
|
1122
1132
|
seekableRange() {
|
|
1123
|
-
let e = this.video;
|
|
1124
|
-
if (e.seekable && e.seekable.length > 0)
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
return Number.isFinite(e.duration) && e.duration > 0 ? {
|
|
1132
|
-
start: 0,
|
|
1133
|
-
end: e.duration
|
|
1133
|
+
let e = this.video, t, n;
|
|
1134
|
+
if (e.seekable && e.seekable.length > 0) t = e.seekable.start(0), n = e.seekable.end(e.seekable.length - 1);
|
|
1135
|
+
else if (Number.isFinite(e.duration) && e.duration > 0) t = 0, n = e.duration;
|
|
1136
|
+
else return null;
|
|
1137
|
+
let r = this.player.getStats();
|
|
1138
|
+
return r.isLive !== !1 && r.targetLatencySeconds && r.targetLatencySeconds > 0 && (n = Math.max(t, n - r.targetLatencySeconds)), n > t ? {
|
|
1139
|
+
start: t,
|
|
1140
|
+
end: n
|
|
1134
1141
|
} : null;
|
|
1135
1142
|
}
|
|
1136
1143
|
syncProgress() {
|
|
@@ -1197,17 +1204,17 @@ var we = class {
|
|
|
1197
1204
|
renderQualityMenu() {
|
|
1198
1205
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
1199
1206
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
1200
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
1207
|
+
for (let n of e) this.menu.append(this.menuItem(Ee(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
1201
1208
|
}
|
|
1202
1209
|
renderAudioMenu() {
|
|
1203
1210
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
1204
1211
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
1205
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1212
|
+
for (let t of e) this.menu.append(this.menuItem(De(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
1206
1213
|
}
|
|
1207
1214
|
renderCcMenu() {
|
|
1208
1215
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
1209
1216
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
1210
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1217
|
+
for (let t of e) this.menu.append(this.menuItem(Oe(t), !1, () => this.player.setTextTrack(t.id)));
|
|
1211
1218
|
}
|
|
1212
1219
|
onTracksChanged() {
|
|
1213
1220
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -1313,27 +1320,27 @@ function X(e) {
|
|
|
1313
1320
|
let t = Math.floor(e % 60), n = Math.floor(e / 60 % 60), r = Math.floor(e / 3600), i = r > 0 ? String(n).padStart(2, "0") : String(n), a = String(t).padStart(2, "0");
|
|
1314
1321
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
1315
1322
|
}
|
|
1316
|
-
function
|
|
1323
|
+
function Ee(e) {
|
|
1317
1324
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
1318
1325
|
}
|
|
1319
|
-
function
|
|
1326
|
+
function De(e) {
|
|
1320
1327
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1321
1328
|
}
|
|
1322
|
-
function
|
|
1329
|
+
function Oe(e) {
|
|
1323
1330
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1324
1331
|
}
|
|
1325
1332
|
//#endregion
|
|
1326
1333
|
//#region src/managed/index.ts
|
|
1327
|
-
var
|
|
1334
|
+
var ke = 6e3, Z = 45e3, Ae = 2e3, je = 3, Me = 2e3, Ne = 480 * 1e3;
|
|
1328
1335
|
async function Q(e) {
|
|
1329
1336
|
if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
1330
1337
|
if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
|
|
1331
1338
|
if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
1332
1339
|
if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
|
|
1333
|
-
let n = new
|
|
1340
|
+
let n = new Pe(e);
|
|
1334
1341
|
return await n.start(), n;
|
|
1335
1342
|
}
|
|
1336
|
-
var
|
|
1343
|
+
var Pe = class {
|
|
1337
1344
|
opts;
|
|
1338
1345
|
abort = new AbortController();
|
|
1339
1346
|
listeners = {};
|
|
@@ -1412,13 +1419,13 @@ var Ne = class {
|
|
|
1412
1419
|
!r && !s() && l();
|
|
1413
1420
|
}, e);
|
|
1414
1421
|
};
|
|
1415
|
-
d(
|
|
1422
|
+
d(ke), this.armingSub = w({
|
|
1416
1423
|
eventsBaseUrl: n,
|
|
1417
1424
|
matchUrn: this.opts.matchUrn,
|
|
1418
1425
|
credential: this.opts.credential,
|
|
1419
|
-
onAlive: () => d(
|
|
1426
|
+
onAlive: () => d(Z),
|
|
1420
1427
|
onState: (e) => {
|
|
1421
|
-
r || s() || (e.state === "live" || e.state === "gone") && setTimeout(u, Math.random() *
|
|
1428
|
+
r || s() || (e.state === "live" || e.state === "gone") && setTimeout(u, Math.random() * Ae);
|
|
1422
1429
|
},
|
|
1423
1430
|
onError: () => l()
|
|
1424
1431
|
});
|
|
@@ -1459,12 +1466,13 @@ var Ne = class {
|
|
|
1459
1466
|
userId: this.opts.userId,
|
|
1460
1467
|
lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
|
|
1461
1468
|
snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
|
|
1469
|
+
debug: this.opts.debug,
|
|
1462
1470
|
hlsConfig: r,
|
|
1463
1471
|
emit: (e) => this.onEngineEvent(e)
|
|
1464
1472
|
}, a = this.opts.engine ?? "auto";
|
|
1465
1473
|
if (a === "native") return new W(i);
|
|
1466
|
-
if (a === "hls" || L()) return new
|
|
1467
|
-
if (
|
|
1474
|
+
if (a === "hls" || L()) return new _e(i);
|
|
1475
|
+
if (ve(this.opts.video)) return new W(i);
|
|
1468
1476
|
throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
1469
1477
|
}
|
|
1470
1478
|
onEngineEvent(e) {
|
|
@@ -1509,7 +1517,7 @@ var Ne = class {
|
|
|
1509
1517
|
r = 0, i = !1;
|
|
1510
1518
|
},
|
|
1511
1519
|
onError: () => {
|
|
1512
|
-
this.destroyed || e !== this.loadGen || i || (r += 1, r >=
|
|
1520
|
+
this.destroyed || e !== this.loadGen || i || (r += 1, r >= je && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
|
|
1513
1521
|
}
|
|
1514
1522
|
});
|
|
1515
1523
|
}
|
|
@@ -1531,7 +1539,7 @@ var Ne = class {
|
|
|
1531
1539
|
}
|
|
1532
1540
|
t.controls = !1;
|
|
1533
1541
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
1534
|
-
r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar =
|
|
1542
|
+
r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar = we({
|
|
1535
1543
|
root: n,
|
|
1536
1544
|
video: t,
|
|
1537
1545
|
player: this,
|
|
@@ -1554,7 +1562,7 @@ var Ne = class {
|
|
|
1554
1562
|
}
|
|
1555
1563
|
startStats() {
|
|
1556
1564
|
this.stopStats();
|
|
1557
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
1565
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Me);
|
|
1558
1566
|
this.statsTimer = setInterval(() => {
|
|
1559
1567
|
this.engine && this.emit("stats", this.engine.getStats());
|
|
1560
1568
|
}, e);
|
|
@@ -1564,7 +1572,7 @@ var Ne = class {
|
|
|
1564
1572
|
}
|
|
1565
1573
|
scheduleLicenseRefresh() {
|
|
1566
1574
|
this.stopRefresh();
|
|
1567
|
-
let e = this.opts.licenseRefreshMs ??
|
|
1575
|
+
let e = this.opts.licenseRefreshMs ?? Ne;
|
|
1568
1576
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
1569
1577
|
}
|
|
1570
1578
|
stopRefresh() {
|
|
@@ -1699,7 +1707,7 @@ function $(e) {
|
|
|
1699
1707
|
}
|
|
1700
1708
|
//#endregion
|
|
1701
1709
|
//#region src/embed/host.ts
|
|
1702
|
-
function
|
|
1710
|
+
function Fe(e) {
|
|
1703
1711
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
1704
1712
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
1705
1713
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -1758,8 +1766,8 @@ function Pe(e) {
|
|
|
1758
1766
|
}
|
|
1759
1767
|
//#endregion
|
|
1760
1768
|
//#region src/embed/page.ts
|
|
1761
|
-
var
|
|
1762
|
-
function
|
|
1769
|
+
var Ie = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
1770
|
+
function Le() {
|
|
1763
1771
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
1764
1772
|
if (e) return {
|
|
1765
1773
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -1781,9 +1789,9 @@ function Ie() {
|
|
|
1781
1789
|
waitForLive: !0
|
|
1782
1790
|
};
|
|
1783
1791
|
}
|
|
1784
|
-
async function
|
|
1785
|
-
let e =
|
|
1786
|
-
|
|
1792
|
+
async function Re() {
|
|
1793
|
+
let e = Le();
|
|
1794
|
+
Be();
|
|
1787
1795
|
let n = document.createElement("video");
|
|
1788
1796
|
n.setAttribute("playsinline", ""), n.muted = e.muted ?? !0, n.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(n);
|
|
1789
1797
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -1838,7 +1846,7 @@ async function Le() {
|
|
|
1838
1846
|
code: e.code,
|
|
1839
1847
|
message: e.message
|
|
1840
1848
|
})), e.on("autoplayblocked", () => {
|
|
1841
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
1849
|
+
i({ type: "oddin:autoplayblocked" }), ze(n, e);
|
|
1842
1850
|
});
|
|
1843
1851
|
}, s = async (r) => {
|
|
1844
1852
|
a?.destroy(), a = null;
|
|
@@ -1904,7 +1912,7 @@ async function Le() {
|
|
|
1904
1912
|
a?.retry();
|
|
1905
1913
|
break;
|
|
1906
1914
|
case "oddin:load":
|
|
1907
|
-
typeof e.matchUrn == "string" &&
|
|
1915
|
+
typeof e.matchUrn == "string" && Ie.test(e.matchUrn) && s(e.matchUrn);
|
|
1908
1916
|
break;
|
|
1909
1917
|
case "oddin:destroy":
|
|
1910
1918
|
c();
|
|
@@ -1918,18 +1926,18 @@ async function Le() {
|
|
|
1918
1926
|
protocol: 1
|
|
1919
1927
|
});
|
|
1920
1928
|
}
|
|
1921
|
-
function
|
|
1929
|
+
function ze(e, t) {
|
|
1922
1930
|
if (document.getElementById("havik-tap")) return;
|
|
1923
1931
|
let n = document.createElement("button");
|
|
1924
1932
|
n.id = "havik-tap", n.textContent = "▶ Tap to play", n.style.cssText = "position:fixed;inset:0;width:100%;height:100%;border:0;background:rgba(0,0,0,.55);color:#fff;font:600 18px system-ui,sans-serif;cursor:pointer;z-index:9", n.addEventListener("click", () => {
|
|
1925
1933
|
n.remove(), t.play();
|
|
1926
1934
|
}), document.body.appendChild(n);
|
|
1927
1935
|
}
|
|
1928
|
-
function
|
|
1936
|
+
function Be() {
|
|
1929
1937
|
let e = document.createElement("style");
|
|
1930
1938
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
1931
1939
|
}
|
|
1932
1940
|
//#endregion
|
|
1933
|
-
export { G as ODDIN_THEME, t as PlaybackError, q as applyTheme,
|
|
1941
|
+
export { G as ODDIN_THEME, t as PlaybackError, q as applyTheme, Re as bootEmbed, k as clearDeviceId, Q as createPlayer, T as deriveEventsBaseUrl, oe as fetchCatalog, ce as fetchTournament, ue as flattenMatches, O as getDeviceId, N as isLowLatencyManifest, $ as isOddinMessage, j as licenseRequestHeaders, Fe as mountEmbed, y as pollToLive, x as resolveStream, w as subscribeLiveState, C as watchStatus };
|
|
1934
1942
|
|
|
1935
1943
|
//# sourceMappingURL=index.js.map
|