chore: Монорепо с общими пакетами
This commit is contained in:
707
packages/video-player/dist/core/player-runtime.js
vendored
Normal file
707
packages/video-player/dist/core/player-runtime.js
vendored
Normal file
@@ -0,0 +1,707 @@
|
||||
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
|
||||
Reference in New Issue
Block a user