707 lines
26 KiB
JavaScript
707 lines
26 KiB
JavaScript
import Hls from "hls.js";
|
|
import videojs from "video.js";
|
|
import "./plugins/register";
|
|
import { selectPlaybackEngine, } from "./engine-selector";
|
|
import { formatTime } from "./format-time";
|
|
import { resolveVideoPlayerToken } from "./token-provider";
|
|
const DEFAULT_SOURCE_TYPE = "application/x-mpegurl";
|
|
const detectIOS = () => {
|
|
if (typeof navigator === "undefined") {
|
|
return false;
|
|
}
|
|
const userAgent = navigator.userAgent || "";
|
|
return /iPad|iPhone|iPod/.test(userAgent);
|
|
};
|
|
const createAuthPlaylistLoader = ({ debug, }) => {
|
|
const BaseLoader = Hls.DefaultConfig.loader;
|
|
return class AuthPlaylistLoader extends BaseLoader {
|
|
constructor(config) {
|
|
super({ ...config, debug: debug ?? false });
|
|
}
|
|
load(context, config, callbacks) {
|
|
const start = async () => {
|
|
try {
|
|
const token = await resolveVideoPlayerToken();
|
|
if (token) {
|
|
context.headers = {
|
|
...(context.headers ?? {}),
|
|
Authorization: `Bearer ${token}`,
|
|
};
|
|
}
|
|
}
|
|
catch (error) {
|
|
if (debug) {
|
|
console.warn("[VideoRuntime:HLS] Failed to append auth header to playlist request", error);
|
|
}
|
|
}
|
|
finally {
|
|
super.load(context, config, callbacks);
|
|
}
|
|
};
|
|
void start().catch(error => {
|
|
if (debug) {
|
|
console.error("[VideoRuntime:HLS] Playlist loader start failed", error);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
};
|
|
const normalizeSource = (source) => ({
|
|
src: source?.src ?? "",
|
|
type: source?.type ?? DEFAULT_SOURCE_TYPE,
|
|
});
|
|
const normalizeOptions = (options, previous) => ({
|
|
source: normalizeSource(options.source ?? previous?.source),
|
|
strategy: options.strategy ?? previous?.strategy ?? "auto",
|
|
preload: options.preload ?? previous?.preload ?? "auto",
|
|
autoplay: options.autoplay ?? previous?.autoplay ?? false,
|
|
controls: options.controls ?? previous?.controls ?? true,
|
|
responsive: options.responsive ?? previous?.responsive ?? true,
|
|
aspectRatio: options.aspectRatio ?? previous?.aspectRatio,
|
|
fluid: options.fluid ?? previous?.fluid ?? true,
|
|
muted: options.muted ?? previous?.muted ?? false,
|
|
poster: options.poster ?? previous?.poster,
|
|
preferHQ: options.preferHQ ?? previous?.preferHQ ?? false,
|
|
debug: options.debug ?? previous?.debug ?? false,
|
|
initialTime: options.initialTime ?? previous?.initialTime ?? 0,
|
|
isIOS: options.isIOS ?? previous?.isIOS,
|
|
isMobile: options.isMobile ?? previous?.isMobile,
|
|
full: options.full ?? previous?.full ?? false,
|
|
withRewind: options.withRewind ?? previous?.withRewind ?? true,
|
|
skipSeconds: options.skipSeconds ?? previous?.skipSeconds ?? 10,
|
|
classNames: options.classNames ?? previous?.classNames ?? [],
|
|
onPlayerReady: options.onPlayerReady ?? previous?.onPlayerReady,
|
|
});
|
|
export class VideoPlayerRuntime {
|
|
constructor() {
|
|
this.containerRef = null;
|
|
this.videoRef = null;
|
|
this.playerRef = null;
|
|
this.hlsRef = null;
|
|
this.options = null;
|
|
this.currentEngine = null;
|
|
this.currentSource = null;
|
|
this.vhsAuthTokenRef = null;
|
|
this.vhsRequestCleanupRef = null;
|
|
this.visibilityObserverRef = null;
|
|
this.originalPlayRef = null;
|
|
this.hlsLoaded = false;
|
|
this.eventListeners = new Map();
|
|
}
|
|
async init(options) {
|
|
this.dispose();
|
|
this.containerRef = options.container;
|
|
this.options = normalizeOptions(options);
|
|
this.createVideoElement();
|
|
this.createPlayer();
|
|
const state = await this.loadCurrentSource(null);
|
|
this.emit("ready", {
|
|
engine: state.engine,
|
|
source: state.source,
|
|
player: this.playerRef,
|
|
});
|
|
this.options.onPlayerReady?.(this.playerRef, state);
|
|
return state;
|
|
}
|
|
async update(options) {
|
|
if (!this.options || !this.playerRef) {
|
|
return this.getState();
|
|
}
|
|
const previous = this.options;
|
|
const nextOptions = normalizeOptions(options, previous);
|
|
this.options = nextOptions;
|
|
this.syncPlayerDisplayOptions(previous, nextOptions);
|
|
const sourceChanged = nextOptions.source.src !== previous.source.src ||
|
|
nextOptions.source.type !== previous.source.type;
|
|
const engineDependsOnChanged = nextOptions.strategy !== previous.strategy ||
|
|
nextOptions.isIOS !== previous.isIOS;
|
|
if (sourceChanged || engineDependsOnChanged) {
|
|
if (sourceChanged) {
|
|
this.emit("sourcechange", {
|
|
previous: previous.source,
|
|
next: nextOptions.source,
|
|
engine: this.currentEngine,
|
|
});
|
|
}
|
|
return this.loadCurrentSource(this.currentEngine);
|
|
}
|
|
if (nextOptions.poster !== previous.poster) {
|
|
this.syncPoster(nextOptions.poster);
|
|
}
|
|
return this.getState();
|
|
}
|
|
on(event, handler) {
|
|
if (!this.eventListeners.has(event)) {
|
|
this.eventListeners.set(event, new Set());
|
|
}
|
|
const listeners = this.eventListeners.get(event);
|
|
listeners.add(handler);
|
|
return () => {
|
|
listeners.delete(handler);
|
|
};
|
|
}
|
|
dispose() {
|
|
this.emit("dispose", {});
|
|
this.resetDeferredHlsLoading();
|
|
this.teardownHls();
|
|
this.vhsRequestCleanupRef?.();
|
|
this.vhsRequestCleanupRef = null;
|
|
this.vhsAuthTokenRef = null;
|
|
if (this.playerRef) {
|
|
this.playerRef.dispose();
|
|
}
|
|
if (this.containerRef) {
|
|
this.containerRef.innerHTML = "";
|
|
}
|
|
this.containerRef = null;
|
|
this.videoRef = null;
|
|
this.playerRef = null;
|
|
this.options = null;
|
|
this.currentEngine = null;
|
|
this.currentSource = null;
|
|
this.hlsLoaded = false;
|
|
}
|
|
getState() {
|
|
return {
|
|
initialized: Boolean(this.playerRef),
|
|
engine: this.currentEngine,
|
|
source: this.currentSource,
|
|
};
|
|
}
|
|
getPlayer() {
|
|
return this.playerRef;
|
|
}
|
|
emit(event, payload) {
|
|
const listeners = this.eventListeners.get(event);
|
|
if (!listeners?.size) {
|
|
return;
|
|
}
|
|
listeners.forEach(listener => {
|
|
try {
|
|
listener(payload);
|
|
}
|
|
catch (error) {
|
|
console.error("[VideoRuntime] Listener failed", {
|
|
event,
|
|
error,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
tryPlay(player) {
|
|
const playResult = player.play();
|
|
if (playResult && typeof playResult.catch === "function") {
|
|
void playResult.catch(() => undefined);
|
|
}
|
|
}
|
|
resolveEngine(options) {
|
|
return selectPlaybackEngine({
|
|
src: options.source.src,
|
|
type: options.source.type,
|
|
strategy: options.strategy,
|
|
hlsSupported: Hls.isSupported(),
|
|
isIOS: options.isIOS ?? detectIOS(),
|
|
});
|
|
}
|
|
createVideoElement() {
|
|
if (!this.containerRef || !this.options) {
|
|
return;
|
|
}
|
|
const videoElement = document.createElement("video");
|
|
videoElement.classList.add("video-js", "vjs-tach-skin");
|
|
if (this.options.isMobile) {
|
|
videoElement.classList.add("vjs-mobile-ui");
|
|
}
|
|
if (!this.options.withRewind) {
|
|
videoElement.classList.add("vjs-disable-rewind");
|
|
}
|
|
videoElement.setAttribute("playsinline", "true");
|
|
if (this.options.poster) {
|
|
videoElement.setAttribute("poster", this.options.poster);
|
|
}
|
|
this.options.classNames.forEach(className => {
|
|
videoElement.classList.add(className);
|
|
});
|
|
this.containerRef.innerHTML = "";
|
|
this.containerRef.appendChild(videoElement);
|
|
this.videoRef = videoElement;
|
|
}
|
|
createPlayer() {
|
|
if (!this.videoRef || !this.options) {
|
|
throw new Error("[VideoRuntime] Unable to create player without video element");
|
|
}
|
|
const videoJsAny = videojs;
|
|
videoJsAny.formatTime = formatTime;
|
|
videoJsAny.setFormatTime?.(formatTime);
|
|
const player = (this.playerRef = videojs(this.videoRef, {
|
|
autoplay: this.options.autoplay,
|
|
controls: this.options.controls,
|
|
preload: this.options.preload === "visibility" ? "none" : this.options.preload,
|
|
fluid: this.options.fluid,
|
|
responsive: this.options.responsive,
|
|
aspectRatio: this.options.aspectRatio,
|
|
muted: this.options.muted,
|
|
}));
|
|
this.attachCompatibilityApi(player);
|
|
player.on("error", () => {
|
|
this.emit("error", {
|
|
scope: "player",
|
|
error: player.error?.() ?? new Error("Unknown player error"),
|
|
});
|
|
});
|
|
player.bigPlayPauseButton?.();
|
|
if (this.options.full) {
|
|
player.settingsMenu?.();
|
|
player.skipButtons?.({ skip: this.options.skipSeconds });
|
|
}
|
|
}
|
|
syncPoster(poster) {
|
|
const videoElement = this.videoRef;
|
|
if (!videoElement) {
|
|
return;
|
|
}
|
|
if (poster) {
|
|
videoElement.setAttribute("poster", poster);
|
|
}
|
|
else {
|
|
videoElement.removeAttribute("poster");
|
|
}
|
|
}
|
|
syncPlayerDisplayOptions(previous, next) {
|
|
const player = this.playerRef;
|
|
if (!player) {
|
|
return;
|
|
}
|
|
if (previous.muted !== next.muted) {
|
|
player.muted(next.muted);
|
|
}
|
|
if (previous.controls !== next.controls) {
|
|
player.controls(next.controls);
|
|
}
|
|
if (!previous.full && next.full) {
|
|
player.settingsMenu?.();
|
|
player.skipButtons?.({ skip: next.skipSeconds });
|
|
}
|
|
if (previous.initialTime !== next.initialTime && next.initialTime > 0) {
|
|
if (this.hlsRef) {
|
|
this.hlsRef.startLoad(next.initialTime);
|
|
}
|
|
player.currentTime(next.initialTime);
|
|
}
|
|
if (!previous.autoplay && next.autoplay && player.paused()) {
|
|
this.tryPlay(player);
|
|
}
|
|
}
|
|
attachCompatibilityApi(player) {
|
|
player.subscribeToSegmentChange = callback => {
|
|
let lastIndex = -1;
|
|
if (this.hlsRef) {
|
|
this.hlsRef.on(Hls.Events.FRAG_CHANGED, (_event, data) => {
|
|
callback(data.frag);
|
|
});
|
|
return;
|
|
}
|
|
player.on("timeupdate", () => {
|
|
const seconds = Math.floor(player.currentTime() || 0);
|
|
const segmentIndex = Math.floor(seconds / 10);
|
|
if (segmentIndex > lastIndex) {
|
|
lastIndex = segmentIndex;
|
|
callback({ start: segmentIndex * 10 });
|
|
}
|
|
});
|
|
};
|
|
player.mediaduration = () => {
|
|
if (this.hlsRef) {
|
|
const level = this.hlsRef.levels[this.hlsRef.currentLevel];
|
|
const details = level?.details;
|
|
if (details?.totalduration) {
|
|
return details.totalduration;
|
|
}
|
|
}
|
|
return player.duration();
|
|
};
|
|
player.subscribeToDuration = callback => {
|
|
const fire = (duration) => {
|
|
if (duration && !Number.isNaN(duration) && duration > 0) {
|
|
callback(duration);
|
|
}
|
|
};
|
|
fire(player.duration());
|
|
if (this.hlsRef) {
|
|
this.hlsRef.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
const totalDuration = this.hlsRef?.levels[0]?.details?.totalduration;
|
|
if (totalDuration) {
|
|
fire(totalDuration);
|
|
}
|
|
});
|
|
}
|
|
player.on("loadedmetadata", () => fire(player.mediaduration()));
|
|
};
|
|
player.subscribeToPlayStart = callback => {
|
|
let started = false;
|
|
const onPlayStart = () => {
|
|
if (!started) {
|
|
started = true;
|
|
callback();
|
|
}
|
|
};
|
|
if (this.hlsRef) {
|
|
this.hlsRef.once(Hls.Events.FRAG_BUFFERED, onPlayStart);
|
|
}
|
|
player.one("playing", onPlayStart);
|
|
};
|
|
player.subscribeToPlayStarted = callback => {
|
|
let started = false;
|
|
const onPlayStarted = () => {
|
|
if (!started) {
|
|
started = true;
|
|
callback();
|
|
}
|
|
};
|
|
player.one("playing", onPlayStarted);
|
|
};
|
|
player.subscribeToManifestLoaded = callback => {
|
|
if (this.hlsRef) {
|
|
this.hlsRef.once(Hls.Events.MANIFEST_PARSED, () => callback());
|
|
return;
|
|
}
|
|
player.one("loadedmetadata", () => callback());
|
|
};
|
|
}
|
|
attachDurationAndPlayStartHooks() {
|
|
const player = this.playerRef;
|
|
if (!player) {
|
|
return;
|
|
}
|
|
player.one("playing", () => {
|
|
this.emit("playstart", {
|
|
engine: this.currentEngine,
|
|
});
|
|
});
|
|
player.one("loadedmetadata", () => {
|
|
const duration = player.duration();
|
|
if (typeof duration === "number" && !Number.isNaN(duration)) {
|
|
this.emit("duration", { duration });
|
|
}
|
|
});
|
|
}
|
|
async loadCurrentSource(previousEngine) {
|
|
if (!this.options || !this.playerRef || !this.videoRef) {
|
|
return this.getState();
|
|
}
|
|
const nextEngine = this.resolveEngine(this.options);
|
|
const source = this.options.source;
|
|
if (previousEngine !== nextEngine || this.currentEngine !== nextEngine) {
|
|
this.emit("enginechange", {
|
|
previous: this.currentEngine,
|
|
next: nextEngine,
|
|
source,
|
|
});
|
|
}
|
|
this.currentEngine = nextEngine;
|
|
this.currentSource = source;
|
|
if (nextEngine === "hls") {
|
|
await this.loadHlsSource();
|
|
}
|
|
else {
|
|
await this.loadVideoJsSource();
|
|
}
|
|
this.attachDurationAndPlayStartHooks();
|
|
return this.getState();
|
|
}
|
|
buildHlsConfig(overrides = {}) {
|
|
const options = this.options;
|
|
if (!options) {
|
|
return overrides;
|
|
}
|
|
const preferHqSettings = options.preferHQ
|
|
? { abrEwmaDefaultEstimate: 10690560 * 1.2 }
|
|
: {};
|
|
const playlistLoader = createAuthPlaylistLoader({ debug: options.debug });
|
|
return {
|
|
debug: options.debug,
|
|
enableWorker: true,
|
|
fragLoadingMaxRetry: 2,
|
|
manifestLoadingMaxRetry: 2,
|
|
fragLoadingRetryDelay: 2000,
|
|
manifestLoadingRetryDelay: 2000,
|
|
forceKeyFrameOnDiscontinuity: true,
|
|
backBufferLength: 90,
|
|
appendErrorMaxRetry: 3,
|
|
startPosition: options.initialTime > 0 ? options.initialTime : -1,
|
|
testBandwidth: false,
|
|
lowLatencyMode: false,
|
|
liveSyncDurationCount: 2,
|
|
maxBufferHole: 10,
|
|
nudgeOffset: 0.1,
|
|
nudgeMaxRetry: 5,
|
|
highBufferWatchdogPeriod: 2,
|
|
...preferHqSettings,
|
|
...overrides,
|
|
pLoader: playlistLoader,
|
|
};
|
|
}
|
|
async loadHlsSource() {
|
|
const options = this.options;
|
|
const player = this.playerRef;
|
|
const video = this.videoRef;
|
|
if (!options || !player || !video) {
|
|
return;
|
|
}
|
|
this.resetDeferredHlsLoading();
|
|
this.teardownHls();
|
|
player.pause();
|
|
this.hlsLoaded = false;
|
|
if (!Hls.isSupported()) {
|
|
await this.loadVideoJsSource();
|
|
return;
|
|
}
|
|
const setupHls = () => {
|
|
if (this.hlsLoaded) {
|
|
return;
|
|
}
|
|
this.hlsLoaded = true;
|
|
const hls = new Hls(this.buildHlsConfig());
|
|
this.hlsRef = hls;
|
|
player.hlsInstance = hls;
|
|
let recoveryAttempts = 0;
|
|
const MAX_RECOVERY_ATTEMPTS = 10;
|
|
let lastErrorTime = 0;
|
|
const ERROR_RESET_TIME = 10000;
|
|
let lastSegmentIndex = -1;
|
|
const isAtLiveEdge = () => {
|
|
if (!hls.liveSyncPosition || !video.duration) {
|
|
return false;
|
|
}
|
|
return hls.liveSyncPosition - video.currentTime < 10;
|
|
};
|
|
hls.on(Hls.Events.MANIFEST_PARSED, (_event, data) => {
|
|
const details = data.levels?.[0]?.details;
|
|
this.emit("manifestloaded", {
|
|
engine: "hls",
|
|
duration: details?.totalduration,
|
|
live: details?.live,
|
|
});
|
|
if (details?.live) {
|
|
video.parentElement?.classList.add("vjs-hls-live", "vjs-live");
|
|
player.duration(Infinity);
|
|
if (player.liveTracker) {
|
|
player.liveTracker.isLive_ = true;
|
|
player.liveTracker.startTracking();
|
|
player.liveTracker.trigger("durationchange");
|
|
}
|
|
}
|
|
if (options.initialTime > 0) {
|
|
hls.startLoad(options.initialTime);
|
|
}
|
|
});
|
|
hls.on(Hls.Events.FRAG_CHANGED, () => {
|
|
if (player.liveTracker) {
|
|
player.liveTracker.atLiveEdge = isAtLiveEdge;
|
|
player.liveTracker.trigger("liveedgechange");
|
|
}
|
|
});
|
|
hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
this.emit("error", {
|
|
scope: "hls",
|
|
error: data,
|
|
fatal: data.fatal,
|
|
});
|
|
if (!data.fatal) {
|
|
return;
|
|
}
|
|
const now = Date.now();
|
|
if (now - lastErrorTime > ERROR_RESET_TIME) {
|
|
recoveryAttempts = 0;
|
|
lastSegmentIndex = -1;
|
|
}
|
|
lastErrorTime = now;
|
|
if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) {
|
|
return;
|
|
}
|
|
recoveryAttempts += 1;
|
|
switch (data.type) {
|
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
setTimeout(() => hls.startLoad(), 1000);
|
|
break;
|
|
case Hls.ErrorTypes.MEDIA_ERROR: {
|
|
const currentLevel = hls.currentLevel;
|
|
const details = hls.levels[currentLevel]?.details;
|
|
if (details?.fragments?.length) {
|
|
if (lastSegmentIndex === -1) {
|
|
lastSegmentIndex = details.startSN;
|
|
}
|
|
lastSegmentIndex += 1;
|
|
if (lastSegmentIndex < details.endSN) {
|
|
const fragment = details.fragments[lastSegmentIndex - details.startSN];
|
|
if (fragment) {
|
|
const savedTime = fragment.start;
|
|
video.currentTime = savedTime;
|
|
setTimeout(() => {
|
|
if (Math.abs(video.currentTime - savedTime) > 0.1) {
|
|
video.currentTime = savedTime;
|
|
}
|
|
hls.recoverMediaError();
|
|
if (!video.paused && options.autoplay) {
|
|
void video.play().catch(() => undefined);
|
|
}
|
|
}, 100);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
recoveryAttempts = MAX_RECOVERY_ATTEMPTS;
|
|
break;
|
|
}
|
|
}
|
|
setTimeout(() => hls.recoverMediaError(), 1000);
|
|
break;
|
|
}
|
|
default:
|
|
setTimeout(() => {
|
|
this.teardownHls();
|
|
this.hlsLoaded = false;
|
|
setupHls();
|
|
}, 2000);
|
|
}
|
|
});
|
|
hls.loadSource(options.source.src);
|
|
hls.attachMedia(video);
|
|
if (options.autoplay && options.preload !== "none") {
|
|
this.tryPlay(player);
|
|
}
|
|
};
|
|
switch (options.preload) {
|
|
case "none":
|
|
this.originalPlayRef = player.play.bind(player);
|
|
player.play = (...args) => {
|
|
setupHls();
|
|
return this.originalPlayRef(...args);
|
|
};
|
|
break;
|
|
case "metadata": {
|
|
const hls = new Hls(this.buildHlsConfig({
|
|
autoStartLoad: false,
|
|
}));
|
|
this.hlsLoaded = true;
|
|
this.hlsRef = hls;
|
|
player.hlsInstance = hls;
|
|
hls.attachMedia(video);
|
|
hls.loadSource(options.source.src);
|
|
break;
|
|
}
|
|
case "visibility":
|
|
if (typeof IntersectionObserver !== "undefined" && this.containerRef) {
|
|
this.visibilityObserverRef = new IntersectionObserver(entries => {
|
|
if (entries[0]?.isIntersecting) {
|
|
setupHls();
|
|
this.visibilityObserverRef?.disconnect();
|
|
this.visibilityObserverRef = null;
|
|
}
|
|
}, { threshold: 0.25 });
|
|
this.visibilityObserverRef.observe(this.containerRef);
|
|
}
|
|
else {
|
|
setupHls();
|
|
}
|
|
break;
|
|
default:
|
|
setupHls();
|
|
}
|
|
}
|
|
async loadVideoJsSource() {
|
|
const options = this.options;
|
|
const player = this.playerRef;
|
|
if (!options || !player) {
|
|
return;
|
|
}
|
|
player.pause();
|
|
this.resetDeferredHlsLoading();
|
|
this.teardownHls();
|
|
try {
|
|
const token = await resolveVideoPlayerToken();
|
|
this.vhsAuthTokenRef = token;
|
|
this.ensureVhsAuthInterceptor(player);
|
|
}
|
|
catch (error) {
|
|
this.emit("error", {
|
|
scope: "runtime",
|
|
error,
|
|
});
|
|
}
|
|
player.src([
|
|
{
|
|
src: options.source.src,
|
|
type: options.source.type ?? DEFAULT_SOURCE_TYPE,
|
|
},
|
|
]);
|
|
if (options.initialTime > 0) {
|
|
player.one("loadedmetadata", () => {
|
|
player.currentTime(options.initialTime);
|
|
});
|
|
}
|
|
if (options.autoplay) {
|
|
this.tryPlay(player);
|
|
}
|
|
}
|
|
resetDeferredHlsLoading() {
|
|
if (this.visibilityObserverRef) {
|
|
this.visibilityObserverRef.disconnect();
|
|
this.visibilityObserverRef = null;
|
|
}
|
|
if (this.originalPlayRef && this.playerRef) {
|
|
this.playerRef.play = this.originalPlayRef;
|
|
this.originalPlayRef = null;
|
|
}
|
|
}
|
|
ensureVhsAuthInterceptor(player) {
|
|
if (this.vhsRequestCleanupRef) {
|
|
return;
|
|
}
|
|
const videojsAny = videojs;
|
|
const xhr = videojsAny?.Vhs?.xhr ?? videojsAny?.Hls?.xhr;
|
|
if (!xhr) {
|
|
return;
|
|
}
|
|
const originalBeforeRequest = xhr.beforeRequest;
|
|
xhr.beforeRequest = (requestOptions) => {
|
|
const processedOptions = originalBeforeRequest?.call(xhr, requestOptions) ?? requestOptions;
|
|
if (this.vhsAuthTokenRef) {
|
|
processedOptions.headers = {
|
|
...(processedOptions.headers ?? {}),
|
|
Authorization: `Bearer ${this.vhsAuthTokenRef}`,
|
|
};
|
|
}
|
|
return processedOptions;
|
|
};
|
|
this.vhsRequestCleanupRef = () => {
|
|
xhr.beforeRequest = originalBeforeRequest;
|
|
};
|
|
player.one("dispose", () => {
|
|
this.vhsRequestCleanupRef?.();
|
|
this.vhsRequestCleanupRef = null;
|
|
});
|
|
}
|
|
teardownHls() {
|
|
if (!this.hlsRef) {
|
|
return;
|
|
}
|
|
this.hlsRef.stopLoad();
|
|
this.hlsRef.detachMedia();
|
|
this.hlsRef.destroy();
|
|
this.hlsRef = null;
|
|
this.hlsLoaded = false;
|
|
if (this.playerRef) {
|
|
this.playerRef.hlsInstance = null;
|
|
const videoElement = this.playerRef
|
|
.el()
|
|
?.querySelector("video");
|
|
videoElement?.parentElement?.classList.remove("vjs-hls-live", "vjs-live");
|
|
if (this.playerRef.liveTracker) {
|
|
this.playerRef.liveTracker.isLive_ = false;
|
|
this.playerRef.liveTracker.trigger("durationchange");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//# sourceMappingURL=player-runtime.js.map
|