@oddin-gg/havik-player 2.16.4 → 2.16.5
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.js +127 -98
- package/dist/index.js.map +1 -1
- package/docs/API.md +12 -0
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1964,19 +1964,34 @@ var Wt = class {
|
|
|
1964
1964
|
let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
|
|
1965
1965
|
return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
|
|
1966
1966
|
}
|
|
1967
|
-
}
|
|
1967
|
+
};
|
|
1968
|
+
//#endregion
|
|
1969
|
+
//#region src/core/drm/signedUrl.ts
|
|
1970
|
+
function Gt(e, t = Date.now()) {
|
|
1971
|
+
if (!e) return !1;
|
|
1972
|
+
let n;
|
|
1973
|
+
try {
|
|
1974
|
+
n = new URL(e).searchParams.get("exp");
|
|
1975
|
+
} catch {
|
|
1976
|
+
return !1;
|
|
1977
|
+
}
|
|
1978
|
+
return !n || !/^\d+$/.test(n) ? !1 : Number(n) * 1e3 <= t;
|
|
1979
|
+
}
|
|
1980
|
+
//#endregion
|
|
1981
|
+
//#region src/core/analytics.ts
|
|
1982
|
+
var Kt = 15e3, qt = 6e5, Jt = 3e5, Yt = 100, q = 1, Xt = 1e3, Zt = 3e4, Qt = 1e3, $t = {
|
|
1968
1983
|
inline: "PRESENTATION_INLINE",
|
|
1969
1984
|
fullscreen: "PRESENTATION_FULLSCREEN",
|
|
1970
1985
|
pip: "PRESENTATION_PIP"
|
|
1971
|
-
},
|
|
1986
|
+
}, en = {
|
|
1972
1987
|
widevine: "KEY_SYSTEM_WIDEVINE",
|
|
1973
1988
|
fairplay: "KEY_SYSTEM_FAIRPLAY",
|
|
1974
1989
|
none: "KEY_SYSTEM_NONE"
|
|
1975
1990
|
};
|
|
1976
|
-
function
|
|
1991
|
+
function tn(e, t) {
|
|
1977
1992
|
return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
|
|
1978
1993
|
}
|
|
1979
|
-
var
|
|
1994
|
+
var nn = /* @__PURE__ */ new Set([
|
|
1980
1995
|
"INVALID_URN",
|
|
1981
1996
|
"NOT_FOUND",
|
|
1982
1997
|
"GONE",
|
|
@@ -1996,7 +2011,7 @@ var tn = /* @__PURE__ */ new Set([
|
|
|
1996
2011
|
ended: "PLAYER_STATE_ENDED",
|
|
1997
2012
|
error: "PLAYER_STATE_ERROR"
|
|
1998
2013
|
};
|
|
1999
|
-
function
|
|
2014
|
+
function rn(e) {
|
|
2000
2015
|
try {
|
|
2001
2016
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
2002
2017
|
return !n || r.length === 0 || n !== "feed" && !n.startsWith("feed-") ? void 0 : (t.hostname = [n.replace(/^feed/, "beacons"), ...r].join("."), t.pathname = "", t.search = "", t.origin);
|
|
@@ -2004,8 +2019,8 @@ function nn(e) {
|
|
|
2004
2019
|
return;
|
|
2005
2020
|
}
|
|
2006
2021
|
}
|
|
2007
|
-
var
|
|
2008
|
-
function
|
|
2022
|
+
var an = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), on = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), sn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
|
|
2023
|
+
function cn(e) {
|
|
2009
2024
|
if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
|
|
2010
2025
|
let t;
|
|
2011
2026
|
try {
|
|
@@ -2014,19 +2029,19 @@ function sn(e) {
|
|
|
2014
2029
|
} catch {
|
|
2015
2030
|
return;
|
|
2016
2031
|
}
|
|
2017
|
-
if (
|
|
2018
|
-
Y.add(t), g(
|
|
2032
|
+
if (on.has(t) && !Y.has(t)) {
|
|
2033
|
+
Y.add(t), g(sn(new URL(t).origin));
|
|
2019
2034
|
for (let e of X) e.noteCspBlocked();
|
|
2020
2035
|
}
|
|
2021
2036
|
}
|
|
2022
|
-
function
|
|
2037
|
+
function ln(e, t) {
|
|
2023
2038
|
return g(t, e), { disabledReason: t };
|
|
2024
2039
|
}
|
|
2025
|
-
function
|
|
2026
|
-
if (n) return t && !
|
|
2040
|
+
function un(e, t, n) {
|
|
2041
|
+
if (n) return t && !an(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
|
|
2027
2042
|
if (t) return { endpoint: t };
|
|
2028
|
-
if (t !== void 0) return
|
|
2029
|
-
let r =
|
|
2043
|
+
if (t !== void 0) return ln("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
|
|
2044
|
+
let r = rn(e);
|
|
2030
2045
|
if (r) return { endpoint: r };
|
|
2031
2046
|
let i;
|
|
2032
2047
|
try {
|
|
@@ -2034,9 +2049,9 @@ function ln(e, t, n) {
|
|
|
2034
2049
|
} catch {
|
|
2035
2050
|
i = e;
|
|
2036
2051
|
}
|
|
2037
|
-
return
|
|
2052
|
+
return ln(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
|
|
2038
2053
|
}
|
|
2039
|
-
var
|
|
2054
|
+
var dn = class {
|
|
2040
2055
|
opts;
|
|
2041
2056
|
url;
|
|
2042
2057
|
cspKey;
|
|
@@ -2118,8 +2133,8 @@ var un = class {
|
|
|
2118
2133
|
constructor(e) {
|
|
2119
2134
|
this.opts = {
|
|
2120
2135
|
...e,
|
|
2121
|
-
heartbeatMs: e.heartbeatMs ??
|
|
2122
|
-
idleMs: e.idleMs ??
|
|
2136
|
+
heartbeatMs: e.heartbeatMs ?? Kt,
|
|
2137
|
+
idleMs: e.idleMs ?? qt
|
|
2123
2138
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
|
|
2124
2139
|
try {
|
|
2125
2140
|
let e = new URL(this.url);
|
|
@@ -2136,11 +2151,11 @@ var un = class {
|
|
|
2136
2151
|
sdkVersion: this.opts.sdkVersion,
|
|
2137
2152
|
...this.opts.environment
|
|
2138
2153
|
};
|
|
2139
|
-
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(),
|
|
2154
|
+
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Xt), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && on.add(this.cspKey), document.addEventListener("securitypolicyviolation", cn), X.add(this), this.noteCspBlocked(), this.onVisibility());
|
|
2140
2155
|
}
|
|
2141
2156
|
checkSuspension(e) {
|
|
2142
2157
|
if (this.endedFlag || !this.opts.idleMs) return !1;
|
|
2143
|
-
let t = Math.max(this.opts.idleMs,
|
|
2158
|
+
let t = Math.max(this.opts.idleMs, Jt);
|
|
2144
2159
|
return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
|
|
2145
2160
|
}
|
|
2146
2161
|
syncIdle(e) {
|
|
@@ -2163,12 +2178,12 @@ var un = class {
|
|
|
2163
2178
|
let a = {
|
|
2164
2179
|
fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
2165
2180
|
toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
2166
|
-
presentation:
|
|
2181
|
+
presentation: $t[this.presentation]
|
|
2167
2182
|
}, o = this.causeOf(e, t, i);
|
|
2168
2183
|
o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
|
|
2169
2184
|
}
|
|
2170
2185
|
seekInduced(e) {
|
|
2171
|
-
return this.lastSeekMs >= 0 && e - this.lastSeekMs <
|
|
2186
|
+
return this.lastSeekMs >= 0 && e - this.lastSeekMs < Qt;
|
|
2172
2187
|
}
|
|
2173
2188
|
causeOf(e, t, n) {
|
|
2174
2189
|
if (t === "error") return "TRANSITION_CAUSE_ERROR";
|
|
@@ -2242,7 +2257,7 @@ var un = class {
|
|
|
2242
2257
|
let r = n ?? "";
|
|
2243
2258
|
if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
|
|
2244
2259
|
if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
|
|
2245
|
-
if (
|
|
2260
|
+
if (nn.has(e)) return "ERROR_STAGE_RESOLVE";
|
|
2246
2261
|
let i = t ?? "";
|
|
2247
2262
|
return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
|
|
2248
2263
|
}
|
|
@@ -2289,14 +2304,14 @@ var un = class {
|
|
|
2289
2304
|
if (this.lastRequestCount = g, _ > 0) {
|
|
2290
2305
|
o.requestCountDelta = _;
|
|
2291
2306
|
let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
|
|
2292
|
-
n.length > 0 && (o.requestTimeP50Ms =
|
|
2307
|
+
n.length > 0 && (o.requestTimeP50Ms = tn(n, 50), o.requestTimeP95Ms = tn(n, 95));
|
|
2293
2308
|
}
|
|
2294
2309
|
typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
|
|
2295
2310
|
}
|
|
2296
2311
|
maybeEmitMediaInfo(e) {
|
|
2297
2312
|
if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
|
|
2298
2313
|
let t = {};
|
|
2299
|
-
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem =
|
|
2314
|
+
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = en[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
|
|
2300
2315
|
let n = JSON.stringify(t);
|
|
2301
2316
|
n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
|
|
2302
2317
|
}
|
|
@@ -2316,7 +2331,7 @@ var un = class {
|
|
|
2316
2331
|
let c = r?.positionMs ?? 0;
|
|
2317
2332
|
c > 0 && (i.exitPositionMs = Math.round(c)), this.activeTotalMs > 0 && (i.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (i.mutedTimeMs = Math.round(this.mutedTotalMs));
|
|
2318
2333
|
let l = e !== "END_REASON_ENDED";
|
|
2319
|
-
l && this.lastStallMs > 0 && n - this.lastStallMs <=
|
|
2334
|
+
l && this.lastStallMs > 0 && n - this.lastStallMs <= Zt && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Zt && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
|
|
2320
2335
|
let u = r?.decodedFrames ?? this.lastDecoded;
|
|
2321
2336
|
u > 0 && (i.decodedFramesTotal = u), e === "END_REASON_STOPPED" && this.startupTimeMs === 0 && this.lastErrorStage === null && (e = "END_REASON_ABORTED", i.reason = e), this.startupTimeMs === 0 && e !== "END_REASON_ABORTED" && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
|
|
2322
2337
|
}
|
|
@@ -2336,13 +2351,13 @@ var un = class {
|
|
|
2336
2351
|
};
|
|
2337
2352
|
}
|
|
2338
2353
|
noteCspBlocked() {
|
|
2339
|
-
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(
|
|
2354
|
+
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(sn(new URL(this.url).origin)));
|
|
2340
2355
|
}
|
|
2341
2356
|
cspBlocked() {
|
|
2342
2357
|
return !!this.cspKey && Y.has(this.cspKey);
|
|
2343
2358
|
}
|
|
2344
2359
|
enqueue(e) {
|
|
2345
|
-
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
2360
|
+
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Yt;) {
|
|
2346
2361
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
2347
2362
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
2348
2363
|
}
|
|
@@ -2369,7 +2384,7 @@ var un = class {
|
|
|
2369
2384
|
};
|
|
2370
2385
|
//#endregion
|
|
2371
2386
|
//#region src/core/environment.ts
|
|
2372
|
-
function
|
|
2387
|
+
function fn() {
|
|
2373
2388
|
if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
|
|
2374
2389
|
let e;
|
|
2375
2390
|
try {
|
|
@@ -2394,14 +2409,14 @@ function dn() {
|
|
|
2394
2409
|
integration: "INTEGRATION_KIND_IFRAME"
|
|
2395
2410
|
};
|
|
2396
2411
|
}
|
|
2397
|
-
function
|
|
2412
|
+
function pn() {
|
|
2398
2413
|
if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
|
|
2399
2414
|
let e = navigator.userAgentData, t = e?.platform ?? "";
|
|
2400
2415
|
if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
|
|
2401
2416
|
let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
|
|
2402
2417
|
return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
|
|
2403
2418
|
}
|
|
2404
|
-
function
|
|
2419
|
+
function mn() {
|
|
2405
2420
|
let e = typeof navigator < "u" ? navigator.connection : void 0;
|
|
2406
2421
|
if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
|
|
2407
2422
|
let t = {
|
|
@@ -2420,17 +2435,17 @@ function pn() {
|
|
|
2420
2435
|
}
|
|
2421
2436
|
return typeof e.downlink == "number" && e.downlink > 0 && (i.downlinkKbps = Math.round(e.downlink * 1e3)), typeof e.rtt == "number" && e.rtt > 0 && (i.rttMs = Math.round(e.rtt)), e.saveData && (i.saveData = !0), i;
|
|
2422
2437
|
}
|
|
2423
|
-
function
|
|
2424
|
-
let t =
|
|
2425
|
-
deviceClass:
|
|
2438
|
+
function hn(e) {
|
|
2439
|
+
let t = fn(), n = {
|
|
2440
|
+
deviceClass: pn(),
|
|
2426
2441
|
integration: t.integration,
|
|
2427
|
-
...
|
|
2442
|
+
...mn()
|
|
2428
2443
|
};
|
|
2429
2444
|
t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
|
|
2430
2445
|
let r = e.getBoundingClientRect?.();
|
|
2431
2446
|
return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
|
|
2432
2447
|
}
|
|
2433
|
-
async function
|
|
2448
|
+
async function gn() {
|
|
2434
2449
|
let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
|
|
2435
2450
|
if (!e?.getHighEntropyValues) return {};
|
|
2436
2451
|
try {
|
|
@@ -2442,7 +2457,7 @@ async function hn() {
|
|
|
2442
2457
|
}
|
|
2443
2458
|
//#endregion
|
|
2444
2459
|
//#region src/core/version.ts
|
|
2445
|
-
var
|
|
2460
|
+
var _n = "2.16.5", vn = {
|
|
2446
2461
|
accent: "#E1B600",
|
|
2447
2462
|
accentText: "#1B1C23",
|
|
2448
2463
|
background: "#0e0f13",
|
|
@@ -2453,7 +2468,7 @@ var gn = "2.16.4", _n = {
|
|
|
2453
2468
|
border: "rgba(255, 255, 255, 0.1)",
|
|
2454
2469
|
radius: "8px",
|
|
2455
2470
|
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
2456
|
-
},
|
|
2471
|
+
}, yn = {
|
|
2457
2472
|
accent: "--havik-accent",
|
|
2458
2473
|
accentText: "--havik-accent-text",
|
|
2459
2474
|
background: "--havik-bg",
|
|
@@ -2467,24 +2482,24 @@ var gn = "2.16.4", _n = {
|
|
|
2467
2482
|
};
|
|
2468
2483
|
function Z(e, t) {
|
|
2469
2484
|
let n = {
|
|
2470
|
-
...
|
|
2485
|
+
...vn,
|
|
2471
2486
|
...t
|
|
2472
2487
|
};
|
|
2473
|
-
for (let t of Object.keys(
|
|
2488
|
+
for (let t of Object.keys(yn)) e.style.setProperty(yn[t], n[t]);
|
|
2474
2489
|
n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
|
|
2475
2490
|
}
|
|
2476
2491
|
//#endregion
|
|
2477
2492
|
//#region src/ui/styles.ts
|
|
2478
|
-
var
|
|
2479
|
-
function
|
|
2480
|
-
let n = e.querySelector(`style#${
|
|
2481
|
-
if (n && (!t ||
|
|
2493
|
+
var bn = "havik-player-styles", xn = "\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 touch-action:manipulation}\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;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/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\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/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n 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", Sn = /* @__PURE__ */ new WeakMap();
|
|
2494
|
+
function Cn(e = document, t) {
|
|
2495
|
+
let n = e.querySelector(`style#${bn}`);
|
|
2496
|
+
if (n && (!t || Sn.get(n) === t)) return;
|
|
2482
2497
|
let r = e.createElement("style");
|
|
2483
|
-
r.id =
|
|
2498
|
+
r.id = bn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = xn, Sn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
|
|
2484
2499
|
}
|
|
2485
2500
|
//#endregion
|
|
2486
2501
|
//#region src/ui/icons.ts
|
|
2487
|
-
var
|
|
2502
|
+
var wn = {
|
|
2488
2503
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
2489
2504
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
2490
2505
|
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\"/>",
|
|
@@ -2498,20 +2513,20 @@ var Cn = {
|
|
|
2498
2513
|
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\"/>"
|
|
2499
2514
|
};
|
|
2500
2515
|
function Q(e) {
|
|
2501
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
2516
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${wn[e]}</svg>`;
|
|
2502
2517
|
}
|
|
2503
2518
|
//#endregion
|
|
2504
2519
|
//#region src/ui/controls.ts
|
|
2505
|
-
var
|
|
2520
|
+
var Tn = 3e3, En = 250, Dn = /* @__PURE__ */ new Set([
|
|
2506
2521
|
"GONE",
|
|
2507
2522
|
"NOT_FOUND",
|
|
2508
2523
|
"INVALID_URN",
|
|
2509
2524
|
"UNAUTHORIZED"
|
|
2510
2525
|
]);
|
|
2511
|
-
function
|
|
2512
|
-
return new
|
|
2526
|
+
function On(e) {
|
|
2527
|
+
return new kn(e);
|
|
2513
2528
|
}
|
|
2514
|
-
var
|
|
2529
|
+
var kn = class {
|
|
2515
2530
|
root;
|
|
2516
2531
|
video;
|
|
2517
2532
|
player;
|
|
@@ -2557,7 +2572,7 @@ var On = class {
|
|
|
2557
2572
|
scrubbing = !1;
|
|
2558
2573
|
offFns = [];
|
|
2559
2574
|
constructor(e) {
|
|
2560
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
2575
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Tn, 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, Cn(this.doc, e.cspNonce), 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");
|
|
2561
2576
|
let t = (e, t, n) => {
|
|
2562
2577
|
let r = this.doc.createElement(e);
|
|
2563
2578
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -2643,7 +2658,7 @@ var On = class {
|
|
|
2643
2658
|
let t = this.surfaceWasHidden;
|
|
2644
2659
|
this.tapTimer = setTimeout(() => {
|
|
2645
2660
|
this.tapTimer = void 0, this.toggleBar(t);
|
|
2646
|
-
},
|
|
2661
|
+
}, En);
|
|
2647
2662
|
}
|
|
2648
2663
|
toggleBar(e) {
|
|
2649
2664
|
e ? this.poke() : this.hideIfIdle();
|
|
@@ -2678,7 +2693,7 @@ var On = class {
|
|
|
2678
2693
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
|
|
2679
2694
|
} else {
|
|
2680
2695
|
let r = this.doc.createElement("div");
|
|
2681
|
-
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !
|
|
2696
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Dn.has(this.lastError.code)) {
|
|
2682
2697
|
let e = this.doc.createElement("button");
|
|
2683
2698
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
|
|
2684
2699
|
}
|
|
@@ -2719,7 +2734,7 @@ var On = class {
|
|
|
2719
2734
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
2720
2735
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
2721
2736
|
}
|
|
2722
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
2737
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${An(this.video.currentTime)} / ${An(this.video.duration)}`);
|
|
2723
2738
|
}
|
|
2724
2739
|
syncLive() {
|
|
2725
2740
|
let e = this.player.getStats(), t = e.isLive !== !1;
|
|
@@ -2769,17 +2784,17 @@ var On = class {
|
|
|
2769
2784
|
renderQualityMenu() {
|
|
2770
2785
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2771
2786
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
2772
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
2787
|
+
for (let n of e) this.menu.append(this.menuItem(jn(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
2773
2788
|
}
|
|
2774
2789
|
renderAudioMenu() {
|
|
2775
2790
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2776
2791
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2777
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2792
|
+
for (let t of e) this.menu.append(this.menuItem(Mn(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
2778
2793
|
}
|
|
2779
2794
|
renderCcMenu() {
|
|
2780
2795
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2781
2796
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
2782
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2797
|
+
for (let t of e) this.menu.append(this.menuItem(Nn(t), !1, () => this.player.setTextTrack(t.id)));
|
|
2783
2798
|
}
|
|
2784
2799
|
onTracksChanged() {
|
|
2785
2800
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -2880,41 +2895,41 @@ var On = class {
|
|
|
2880
2895
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
2881
2896
|
}
|
|
2882
2897
|
};
|
|
2883
|
-
function
|
|
2898
|
+
function An(e) {
|
|
2884
2899
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
2885
2900
|
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");
|
|
2886
2901
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
2887
2902
|
}
|
|
2888
|
-
function
|
|
2903
|
+
function jn(e) {
|
|
2889
2904
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
2890
2905
|
}
|
|
2891
|
-
function
|
|
2906
|
+
function Mn(e) {
|
|
2892
2907
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2893
2908
|
}
|
|
2894
|
-
function
|
|
2909
|
+
function Nn(e) {
|
|
2895
2910
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2896
2911
|
}
|
|
2897
2912
|
//#endregion
|
|
2898
2913
|
//#region src/managed/index.ts
|
|
2899
|
-
var
|
|
2900
|
-
function
|
|
2914
|
+
var Pn = 6e3, Fn = 45e3, In = 2e3, Ln = 3, Rn = 2e3, zn = 48e4, Bn = "keySystemLicenseRequestFailed";
|
|
2915
|
+
function Vn(e) {
|
|
2901
2916
|
if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
|
|
2902
2917
|
if (e.env !== void 0) return Ke(e.env);
|
|
2903
2918
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
2904
2919
|
return e.baseUrl;
|
|
2905
2920
|
}
|
|
2906
|
-
async function
|
|
2921
|
+
async function Hn(e) {
|
|
2907
2922
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
2908
|
-
let t =
|
|
2923
|
+
let t = Vn(e);
|
|
2909
2924
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
2910
2925
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
2911
|
-
let r = new
|
|
2926
|
+
let r = new Un({
|
|
2912
2927
|
...e,
|
|
2913
2928
|
baseUrl: t
|
|
2914
2929
|
});
|
|
2915
2930
|
return await r.start(), r;
|
|
2916
2931
|
}
|
|
2917
|
-
var
|
|
2932
|
+
var Un = class {
|
|
2918
2933
|
opts;
|
|
2919
2934
|
abort = new AbortController();
|
|
2920
2935
|
listeners = {};
|
|
@@ -2925,6 +2940,7 @@ var Vn = class {
|
|
|
2925
2940
|
detachPip;
|
|
2926
2941
|
statsTimer;
|
|
2927
2942
|
refreshTimer;
|
|
2943
|
+
staleLicenseReloaded = !1;
|
|
2928
2944
|
_state = "idle";
|
|
2929
2945
|
_descriptor = null;
|
|
2930
2946
|
destroyed = !1;
|
|
@@ -3002,7 +3018,7 @@ var Vn = class {
|
|
|
3002
3018
|
setTimeout(() => {
|
|
3003
3019
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
3004
3020
|
phase: i ? "unavailable" : "tooEarly",
|
|
3005
|
-
retryInMs:
|
|
3021
|
+
retryInMs: Fn,
|
|
3006
3022
|
attempt: 1,
|
|
3007
3023
|
elapsedMs: 0,
|
|
3008
3024
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -3041,13 +3057,13 @@ var Vn = class {
|
|
|
3041
3057
|
!a && !p() && h();
|
|
3042
3058
|
}, e);
|
|
3043
3059
|
};
|
|
3044
|
-
_(
|
|
3060
|
+
_(Pn), this.armingSub = T({
|
|
3045
3061
|
eventsBaseUrl: t,
|
|
3046
3062
|
matchUrn: this.opts.matchUrn,
|
|
3047
3063
|
credential: this.opts.credential,
|
|
3048
|
-
onAlive: () => _(
|
|
3064
|
+
onAlive: () => _(Fn),
|
|
3049
3065
|
onState: (e) => {
|
|
3050
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
3066
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * In);
|
|
3051
3067
|
},
|
|
3052
3068
|
onError: () => h()
|
|
3053
3069
|
});
|
|
@@ -3092,19 +3108,19 @@ var Vn = class {
|
|
|
3092
3108
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
|
|
3093
3109
|
let i = e.analytics?.sid;
|
|
3094
3110
|
if (!i) return;
|
|
3095
|
-
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } =
|
|
3111
|
+
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = un(this.opts.baseUrl, a, e.analytics?.endpoint);
|
|
3096
3112
|
if (!o) {
|
|
3097
3113
|
s && setTimeout(() => {
|
|
3098
3114
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
|
|
3099
3115
|
}, 0);
|
|
3100
3116
|
return;
|
|
3101
3117
|
}
|
|
3102
|
-
let c = this.opts.video, l = new
|
|
3118
|
+
let c = this.opts.video, l = new dn({
|
|
3103
3119
|
endpoint: o,
|
|
3104
3120
|
sid: i,
|
|
3105
3121
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
3106
|
-
sdkVersion:
|
|
3107
|
-
environment:
|
|
3122
|
+
sdkVersion: _n,
|
|
3123
|
+
environment: hn(c),
|
|
3108
3124
|
idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
|
|
3109
3125
|
onIdle: () => this.onAnalyticsIdle(t),
|
|
3110
3126
|
onBlocked: (e) => setTimeout(() => {
|
|
@@ -3125,7 +3141,7 @@ var Vn = class {
|
|
|
3125
3141
|
let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
|
|
3126
3142
|
c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
|
|
3127
3143
|
c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
|
|
3128
|
-
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()),
|
|
3144
|
+
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), gn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
|
|
3129
3145
|
}
|
|
3130
3146
|
currentPresentation() {
|
|
3131
3147
|
let e = this.opts.video;
|
|
@@ -3166,7 +3182,7 @@ var Vn = class {
|
|
|
3166
3182
|
this.analyticsSession?.noteFirstBytes();
|
|
3167
3183
|
break;
|
|
3168
3184
|
case "drmReady":
|
|
3169
|
-
this.analyticsSession?.noteDrmReady();
|
|
3185
|
+
this.analyticsSession?.noteDrmReady(), this.staleLicenseReloaded = !1;
|
|
3170
3186
|
break;
|
|
3171
3187
|
case "ready":
|
|
3172
3188
|
this.analyticsSession?.noteReady(), this.emit("ready"), (this.opts.autoplay !== !1 || this.playRequested) && this.play();
|
|
@@ -3186,7 +3202,17 @@ var Vn = class {
|
|
|
3186
3202
|
case "ended":
|
|
3187
3203
|
this.onStreamEnded();
|
|
3188
3204
|
break;
|
|
3189
|
-
case "error":
|
|
3205
|
+
case "error": {
|
|
3206
|
+
let t = e.fatal && this.isStaleLicenseDenial(e);
|
|
3207
|
+
if (this.analyticsSession?.noteError(e.error.code, e.error.message, e.fatal && !t, e.error.httpStatus ?? 0, Math.round(this.opts.video.currentTime * 1e3), e.detail), t) {
|
|
3208
|
+
this.staleLicenseReloaded = !0;
|
|
3209
|
+
let t = this.loadGen;
|
|
3210
|
+
this.emit("warning", e.error), Promise.resolve().then(() => {
|
|
3211
|
+
if (!(this.destroyed || t !== this.loadGen) && this._state !== "error" && this._state !== "ended") return this.reload().catch(() => void 0);
|
|
3212
|
+
});
|
|
3213
|
+
} else e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
|
|
3214
|
+
break;
|
|
3215
|
+
}
|
|
3190
3216
|
}
|
|
3191
3217
|
}
|
|
3192
3218
|
startLiveState(e) {
|
|
@@ -3206,7 +3232,7 @@ var Vn = class {
|
|
|
3206
3232
|
r = 0, i = !1;
|
|
3207
3233
|
},
|
|
3208
3234
|
onError: () => {
|
|
3209
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
3235
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Ln && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} 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.`))));
|
|
3210
3236
|
}
|
|
3211
3237
|
});
|
|
3212
3238
|
}
|
|
@@ -3228,7 +3254,7 @@ var Vn = class {
|
|
|
3228
3254
|
scheduleRejoin(e) {
|
|
3229
3255
|
this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
|
|
3230
3256
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
3231
|
-
}, Math.random() *
|
|
3257
|
+
}, Math.random() * In));
|
|
3232
3258
|
}
|
|
3233
3259
|
cancelRejoin() {
|
|
3234
3260
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -3252,7 +3278,7 @@ var Vn = class {
|
|
|
3252
3278
|
}
|
|
3253
3279
|
t.controls = !1;
|
|
3254
3280
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3255
|
-
r && r.insertBefore(n, t), n.appendChild(t), Z(n, this.opts.theme), this.container = n, this.controlBar =
|
|
3281
|
+
r && r.insertBefore(n, t), n.appendChild(t), Z(n, this.opts.theme), this.container = n, this.controlBar = On({
|
|
3256
3282
|
root: n,
|
|
3257
3283
|
video: t,
|
|
3258
3284
|
player: this,
|
|
@@ -3283,7 +3309,7 @@ var Vn = class {
|
|
|
3283
3309
|
}
|
|
3284
3310
|
startStats() {
|
|
3285
3311
|
this.stopStats();
|
|
3286
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3312
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Rn);
|
|
3287
3313
|
this.statsTimer = setInterval(() => {
|
|
3288
3314
|
if (!this.engine) return;
|
|
3289
3315
|
let e = this.engine.getStats();
|
|
@@ -3293,9 +3319,12 @@ var Vn = class {
|
|
|
3293
3319
|
stopStats() {
|
|
3294
3320
|
this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
|
|
3295
3321
|
}
|
|
3322
|
+
isStaleLicenseDenial(e) {
|
|
3323
|
+
return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== Bn || e.error.httpStatus !== 403 ? !1 : Gt(this._descriptor?.drm?.widevine?.licenseUrl);
|
|
3324
|
+
}
|
|
3296
3325
|
scheduleLicenseRefresh() {
|
|
3297
3326
|
this.stopRefresh();
|
|
3298
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3327
|
+
let e = this.opts.licenseRefreshMs ?? zn;
|
|
3299
3328
|
e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3300
3329
|
}
|
|
3301
3330
|
stopRefresh() {
|
|
@@ -3450,7 +3479,7 @@ function $(e) {
|
|
|
3450
3479
|
}
|
|
3451
3480
|
//#endregion
|
|
3452
3481
|
//#region src/embed/host.ts
|
|
3453
|
-
function
|
|
3482
|
+
function Wn(e) {
|
|
3454
3483
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3455
3484
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3456
3485
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3509,15 +3538,15 @@ function Hn(e) {
|
|
|
3509
3538
|
}
|
|
3510
3539
|
//#endregion
|
|
3511
3540
|
//#region src/embed/page.ts
|
|
3512
|
-
var
|
|
3513
|
-
function
|
|
3541
|
+
var Gn = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3542
|
+
function Kn(e) {
|
|
3514
3543
|
if (typeof e == "boolean") return e;
|
|
3515
3544
|
if (typeof e == "object" && e) {
|
|
3516
3545
|
let t = e.endpoint;
|
|
3517
3546
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3518
3547
|
}
|
|
3519
3548
|
}
|
|
3520
|
-
function
|
|
3549
|
+
function qn() {
|
|
3521
3550
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3522
3551
|
if (e) return {
|
|
3523
3552
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3527,7 +3556,7 @@ function Gn() {
|
|
|
3527
3556
|
autoplay: e.autoplay ?? !0,
|
|
3528
3557
|
muted: e.muted ?? !0,
|
|
3529
3558
|
waitForLive: e.waitForLive ?? !0,
|
|
3530
|
-
analytics:
|
|
3559
|
+
analytics: Kn(e.analytics)
|
|
3531
3560
|
};
|
|
3532
3561
|
let t = new URLSearchParams(location.search);
|
|
3533
3562
|
return {
|
|
@@ -3540,9 +3569,9 @@ function Gn() {
|
|
|
3540
3569
|
waitForLive: !0
|
|
3541
3570
|
};
|
|
3542
3571
|
}
|
|
3543
|
-
async function
|
|
3544
|
-
let e =
|
|
3545
|
-
|
|
3572
|
+
async function Jn() {
|
|
3573
|
+
let e = qn();
|
|
3574
|
+
Xn();
|
|
3546
3575
|
let t = document.createElement("video");
|
|
3547
3576
|
t.setAttribute("playsinline", ""), t.muted = e.muted ?? !0, t.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(t);
|
|
3548
3577
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -3597,12 +3626,12 @@ async function Kn() {
|
|
|
3597
3626
|
code: e.code,
|
|
3598
3627
|
message: e.message
|
|
3599
3628
|
})), e.on("autoplayblocked", () => {
|
|
3600
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
3629
|
+
i({ type: "oddin:autoplayblocked" }), Yn(t, e);
|
|
3601
3630
|
});
|
|
3602
3631
|
}, s = async (r) => {
|
|
3603
3632
|
a?.destroy(), a = null;
|
|
3604
3633
|
try {
|
|
3605
|
-
let n = await
|
|
3634
|
+
let n = await Hn({
|
|
3606
3635
|
video: t,
|
|
3607
3636
|
baseUrl: e.baseUrl,
|
|
3608
3637
|
matchUrn: r,
|
|
@@ -3664,7 +3693,7 @@ async function Kn() {
|
|
|
3664
3693
|
a?.retry();
|
|
3665
3694
|
break;
|
|
3666
3695
|
case "oddin:load":
|
|
3667
|
-
typeof e.matchUrn == "string" &&
|
|
3696
|
+
typeof e.matchUrn == "string" && Gn.test(e.matchUrn) && s(e.matchUrn);
|
|
3668
3697
|
break;
|
|
3669
3698
|
case "oddin:destroy": c();
|
|
3670
3699
|
}
|
|
@@ -3676,18 +3705,18 @@ async function Kn() {
|
|
|
3676
3705
|
protocol: 1
|
|
3677
3706
|
});
|
|
3678
3707
|
}
|
|
3679
|
-
function
|
|
3708
|
+
function Yn(e, t) {
|
|
3680
3709
|
if (document.getElementById("havik-tap")) return;
|
|
3681
3710
|
let n = document.createElement("button");
|
|
3682
3711
|
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", () => {
|
|
3683
3712
|
n.remove(), t.play();
|
|
3684
3713
|
}), document.body.appendChild(n);
|
|
3685
3714
|
}
|
|
3686
|
-
function
|
|
3715
|
+
function Xn() {
|
|
3687
3716
|
let e = document.createElement("style");
|
|
3688
3717
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
3689
3718
|
}
|
|
3690
3719
|
//#endregion
|
|
3691
|
-
export {
|
|
3720
|
+
export { vn as ODDIN_THEME, n as PlaybackError, Z as applyTheme, Ke as baseUrlForEnv, Jn as bootEmbed, Te as clearDeviceId, Hn as createPlayer, E as deriveEventsBaseUrl, He as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, We as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, Wn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
|
|
3692
3721
|
|
|
3693
3722
|
//# sourceMappingURL=index.js.map
|