vnetwork-player
Version:
A React component custom player support video m3u8, mp4
1,216 lines (1,189 loc) • 66.8 kB
JavaScript
import React__default, { Component, createElement, useRef, useState, useEffect } from 'react';
import { useHotkeys } from '@tanstack/react-hotkeys';
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
_setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
var ErrorBoundary = /*#__PURE__*/function (_Component) {
_inheritsLoose(ErrorBoundary, _Component);
function ErrorBoundary() {
var _this;
_this = _Component.apply(this, arguments) || this;
_this.state = {
hasError: false,
message: ""
};
return _this;
}
ErrorBoundary.getDerivedStateFromError = function getDerivedStateFromError(_) {
// Update state so the next render will show the fallback UI.
return {
message: _.message,
hasError: true
};
};
var _proto = ErrorBoundary.prototype;
_proto.componentDidCatch = function componentDidCatch(error, errorInfo) {
console.error("Uncaught error:", error, errorInfo);
};
_proto.render = function render() {
if (this.state.hasError) {
return React__default.createElement("div", {
className: "video-container error-container"
}, React__default.createElement("img", {
className: "error-image",
src: "https://raw.githubusercontent.com/an678-mhg/tiktok-clone/main/public/error.png"
}), React__default.createElement("h1", {
className: "text-sm font-semibold error-message line-clamp-1"
}, React__default.createElement("span", {
style: {
color: "#fff"
}
}, "Error: "), " ", this.state.message));
}
return this.props.children;
};
return ErrorBoundary;
}(Component);
var VOLUME_KEY = "vnetwork-player-volume";
var MUTED_KEY = "vnetwork-player-muted";
var formatVideoTime = function formatVideoTime(seconds) {
try {
var date = new Date(0);
date.setSeconds(seconds);
var time = date.toISOString().slice(11, 19);
var result = time.startsWith("00:0") ? time.slice(4) : time.startsWith("00") ? time.slice(3) : time.length === 8 && time.startsWith("0") ? time.slice(1) : time;
return result;
} catch (error) {
return "0:00";
}
};
var playSpeedOptions = [{
label: "0.25x",
value: 0.25
}, {
label: "0.5x",
value: 0.5
}, {
label: "0.75x",
value: 0.75
}, {
label: "1x",
value: 1
}, {
label: "1.25x",
value: 1.25
}, {
label: "1.5x",
value: 1.5
}, {
label: "2x",
value: 2
}];
var removeSearchParams = function removeSearchParams(url) {
var _url$split;
return (_url$split = url.split("?")) == null ? void 0 : _url$split[0];
};
/**
* Extracts the lowercase file extension from a media URL ("mp4", "m3u8", ...).
* Safe against query strings containing dots (signed tokens/JWT), dots in
* directory names, uppercase extensions, and missing extensions.
*/
var getSourceType = function getSourceType(url) {
if (!url) return "";
var cleanUrl = removeSearchParams(url);
var fileName = cleanUrl.split("/").pop() || cleanUrl;
var dotIndex = fileName.lastIndexOf(".");
if (dotIndex === -1) return "";
return fileName.slice(dotIndex + 1).toLowerCase();
};
var fullscreenChangeEvents = ["fullscreenchange", "mozfullscreenchange", "webkitfullscreenchange", "MSFullscreenChange"];
var getFullscreenElement = function getFullscreenElement(doc) {
if (doc === void 0) {
doc = document;
}
var fullscreenDocument = doc;
return fullscreenDocument.fullscreenElement || fullscreenDocument.mozFullScreenElement || fullscreenDocument.webkitFullscreenElement || fullscreenDocument.msFullscreenElement || null;
};
var requestFullscreen = function requestFullscreen(element) {
var fullscreenElement = element;
var request = fullscreenElement.requestFullscreen || fullscreenElement.mozRequestFullScreen || fullscreenElement.webkitRequestFullscreen || fullscreenElement.msRequestFullscreen;
return request == null ? void 0 : request.call(fullscreenElement);
};
var exitFullscreen = function exitFullscreen(doc) {
if (doc === void 0) {
doc = document;
}
var fullscreenDocument = doc;
var exit = fullscreenDocument.exitFullscreen || fullscreenDocument.mozCancelFullScreen || fullscreenDocument.webkitExitFullscreen || fullscreenDocument.msExitFullscreen;
return exit == null ? void 0 : exit.call(fullscreenDocument);
};
var exitPictureInPicture = function exitPictureInPicture(doc) {
if (doc === void 0) {
doc = document;
}
var pipDocument = doc;
if (!pipDocument.pictureInPictureElement || !pipDocument.exitPictureInPicture) return;
pipDocument.exitPictureInPicture()["catch"](console.error);
};
var toggleFullscreen = function toggleFullscreen(element) {
exitPictureInPicture();
if (!getFullscreenElement()) {
return requestFullscreen(element);
}
return exitFullscreen();
};
var togglePictureInPicture = function togglePictureInPicture(videoElement) {
var pipDocument = document;
var pipVideo = videoElement;
if (!pipDocument.pictureInPictureEnabled && !pipVideo.webkitSetPresentationMode && !pipVideo.mozRequestPictureInPicture) {
console.warn("Picture-in-Picture không được hỗ trợ trong trình duyệt này.");
return;
}
if (pipDocument.pictureInPictureElement) {
exitPictureInPicture();
} else if (pipDocument.pictureInPictureEnabled && pipVideo.requestPictureInPicture) {
pipVideo.requestPictureInPicture()["catch"](console.error);
} else if (pipVideo.webkitSetPresentationMode) {
pipVideo.webkitSetPresentationMode(pipVideo.webkitPresentationMode === "picture-in-picture" ? "inline" : "picture-in-picture");
} else {
pipVideo.mozRequestPictureInPicture == null || pipVideo.mozRequestPictureInPicture()["catch"](console.error);
}
};
var addFullscreenChangeListener = function addFullscreenChangeListener(listener) {
fullscreenChangeEvents.forEach(function (eventName) {
document.addEventListener(eventName, listener);
});
return function () {
fullscreenChangeEvents.forEach(function (eventName) {
document.removeEventListener(eventName, listener);
});
};
};
var HLS_AUTO_LEVEL = -1;
// Bộ cấu hình tối ưu - Trải nghiệm nhạy như YouTube
var AUTO_QUALITY_STALL_WINDOW_MS = 10000; // 10 giây: Đủ để theo dõi các biến động mạng gần nhất.
var AUTO_QUALITY_STALL_LIMIT = 1; // 1 lần: Gắt như YouTube, cứ khựng 1 lần là lùi bước để cứu buffer.
var AUTO_QUALITY_LONG_STALL_MS = 1500; // 1.5 giây: Đang xem mà bị "đứng hình" quá 1.5s là tự động hạ cấp ngay lập tức.
var AUTO_QUALITY_SWITCH_COOLDOWN_MS = 5000; // 5 giây: Sau khi đổi chất lượng, đợi 5s xem tình hình thế nào rồi mới cho đổi tiếp.
var AUTO_QUALITY_SWITCH_GRACE_MS = 2000; // 2 giây: Bỏ qua các khựng kỹ thuật nhỏ trong 2s đầu vừa đổi chất lượng.
var AUTO_QUALITY_UPGRADE_BUFFER_SECONDS = 8; // 8 giây: Chỉ cần video tải trước được 8 giây (mạng tốt) là đủ điều kiện lên đời.
var AUTO_QUALITY_UPGRADE_HEALTHY_MS = 8000; // 8 giây: Mạng khỏe liên tục 8 giây là tự động bật chất lượng nét hơn.
var AUTO_QUALITY_CHECK_INTERVAL_MS = 1000; // 1 giây: Cứ mỗi giây "bộ não" player quét 1 lần để phản ứng ngay lập tức.
var DEFAULT_AUTO_QUALITY_CONFIG = {
stallWindowMs: AUTO_QUALITY_STALL_WINDOW_MS,
stallLimit: AUTO_QUALITY_STALL_LIMIT,
longStallMs: AUTO_QUALITY_LONG_STALL_MS,
switchCooldownMs: AUTO_QUALITY_SWITCH_COOLDOWN_MS,
switchGraceMs: AUTO_QUALITY_SWITCH_GRACE_MS,
upgradeBufferSeconds: AUTO_QUALITY_UPGRADE_BUFFER_SECONDS,
upgradeHealthyMs: AUTO_QUALITY_UPGRADE_HEALTHY_MS,
checkIntervalMs: AUTO_QUALITY_CHECK_INTERVAL_MS
};
var resolveAutoQualityConfig = function resolveAutoQualityConfig(config) {
return _extends({}, DEFAULT_AUTO_QUALITY_CONFIG, config);
};
var getBufferedAhead = function getBufferedAhead(videoElement) {
var buffered = videoElement.buffered,
currentTime = videoElement.currentTime;
for (var i = 0; i < buffered.length; i++) {
if (buffered.start(i) <= currentTime && currentTime <= buffered.end(i)) {
return buffered.end(i) - currentTime;
}
}
return 0;
};
var LIVE_EDGE_OFFSET_SECONDS = 0.5;
var LIVE_EDGE_CLICKABLE_THRESHOLD_SECONDS = 2;
var getSeekableLivePosition = function getSeekableLivePosition(videoElement) {
var seekable = videoElement.seekable;
var seekableLength = seekable.length;
if (!seekableLength) return null;
var lastRangeIndex = seekableLength - 1;
var liveEdge = seekable.end(lastRangeIndex);
var rangeStart = seekable.start(lastRangeIndex);
if (!Number.isFinite(liveEdge)) return null;
return Math.max(rangeStart, liveEdge - LIVE_EDGE_OFFSET_SECONDS);
};
var getLivePosition = function getLivePosition(videoElement, hls) {
var hlsLivePosition = hls == null ? void 0 : hls.liveSyncPosition;
if (typeof hlsLivePosition === "number" && Number.isFinite(hlsLivePosition)) {
return hlsLivePosition;
}
return getSeekableLivePosition(videoElement);
};
var normalizeSkipIntro = function normalizeSkipIntro(startIntro, endIntro) {
if (typeof startIntro !== "number" || typeof endIntro !== "number") {
return null;
}
return {
start: startIntro,
end: endIntro,
label: "Skip intro"
};
};
var normalizeSkipOutro = function normalizeSkipOutro(startOutro, endOutro) {
if (typeof startOutro !== "number" || typeof endOutro !== "number") {
return null;
}
return {
start: startOutro,
end: endOutro,
label: "Skip outro"
};
};
var isSkipSegmentActive = function isSkipSegmentActive(segment, currentTime) {
if (!segment) return false;
if (currentTime < segment.start) return false;
if (currentTime >= segment.end) return false;
return true;
};
var _excluded = ["children"];
function IconBase(_ref) {
var children = _ref.children,
props = _objectWithoutPropertiesLoose(_ref, _excluded);
return createElement("svg", Object.assign({
viewBox: "0 0 24 24",
fill: "none",
height: "1em",
width: "1em",
"aria-hidden": "true",
focusable: "false"
}, props), children);
}
function IconPlay(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
d: "M8 5.9c0-1.2 1.4-2 2.5-1.3l8.8 5.9c1 .7 1 2.1 0 2.8l-8.8 5.9C9.4 20 8 19.2 8 18z"
}));
}
function IconFullscreen(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "none",
stroke: "currentColor",
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2.6,
d: "M4.5 9.5v-5h5m5 0h5v5m0 5v5h-5m-5 0h-5v-5"
}));
}
function IconFullscreenExit(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
d: "M8.2 4.2c.7 0 1.2.5 1.2 1.2v3.2h3.2c.7 0 1.2.5 1.2 1.2s-.5 1.2-1.2 1.2H8.1c-.6 0-1.1-.5-1.1-1.1V5.4c0-.7.5-1.2 1.2-1.2m7.6 0c.7 0 1.2.5 1.2 1.2v4.5c0 .6-.5 1.1-1.1 1.1h-4.5c-.7 0-1.2-.5-1.2-1.2s.5-1.2 1.2-1.2h3.2V5.4c0-.7.5-1.2 1.2-1.2M7 14.1c0-.6.5-1.1 1.1-1.1h4.5c.7 0 1.2.5 1.2 1.2s-.5 1.2-1.2 1.2H9.4v3.2c0 .7-.5 1.2-1.2 1.2S7 19.3 7 18.6zm4.4-1.1h4.5c.6 0 1.1.5 1.1 1.1v4.5c0 .7-.5 1.2-1.2 1.2s-1.2-.5-1.2-1.2v-3.2h-3.2c-.7 0-1.2-.5-1.2-1.2s.5-1.2 1.2-1.2"
}));
}
function IconPause(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
d: "M6.2 5.2c0-.7.5-1.2 1.2-1.2h2.7c.7 0 1.2.5 1.2 1.2v13.6c0 .7-.5 1.2-1.2 1.2H7.4c-.7 0-1.2-.5-1.2-1.2zm6.5 0c0-.7.5-1.2 1.2-1.2h2.7c.7 0 1.2.5 1.2 1.2v13.6c0 .7-.5 1.2-1.2 1.2h-2.7c-.7 0-1.2-.5-1.2-1.2z"
}));
}
function IconSettingsSharp(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
fillRule: "evenodd",
d: "M9.8 2.8a1.3 1.3 0 0 1 1.3-1.1h1.8a1.3 1.3 0 0 1 1.3 1.1l.3 1.8c.7.3 1.4.7 2 1.1l1.7-.6a1.3 1.3 0 0 1 1.6.6l.9 1.6a1.3 1.3 0 0 1-.3 1.7L19 10.2a8 8 0 0 1 0 2.4l1.4 1.2c.5.4.6 1.1.3 1.7l-.9 1.6c-.3.6-1 .8-1.6.6l-1.7-.6c-.6.5-1.3.8-2 1.1l-.3 1.8a1.3 1.3 0 0 1-1.3 1.1h-1.8A1.3 1.3 0 0 1 9.8 20l-.3-1.8c-.7-.3-1.4-.7-2-1.1l-1.7.6a1.3 1.3 0 0 1-1.6-.6l-.9-1.6a1.3 1.3 0 0 1 .3-1.7L5 12.6a8 8 0 0 1 0-2.4L3.6 9a1.3 1.3 0 0 1-.3-1.7l.9-1.6c.3-.6 1-.8 1.6-.6l1.7.6c.6-.5 1.3-.8 2-1.1zM12 15.7a3.7 3.7 0 1 0 0-7.4 3.7 3.7 0 0 0 0 7.4"
}));
}
function IconVolumeHigh(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
d: "M4.2 8.5c-.7 0-1.2.5-1.2 1.2v4.6c0 .7.5 1.2 1.2 1.2h3l4.3 3.8c.9.8 2.3.2 2.3-1V5.7c0-1.2-1.4-1.8-2.3-1L7.2 8.5zM16.5 7.1a1.1 1.1 0 0 0-.2 1.5 5.3 5.3 0 0 1 0 6.8 1.1 1.1 0 0 0 .2 1.5c.5.4 1.2.3 1.6-.2a7.6 7.6 0 0 0 0-9.4 1.1 1.1 0 0 0-1.6-.2m3.2-2.4a1.1 1.1 0 0 0-.2 1.5 9.5 9.5 0 0 1 0 11.6 1.1 1.1 0 0 0 .2 1.5c.5.4 1.2.3 1.6-.2a11.8 11.8 0 0 0 0-14.2 1.1 1.1 0 0 0-1.6-.2"
}));
}
function IconVolumeMute(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
d: "M4.2 8.5c-.7 0-1.2.5-1.2 1.2v4.6c0 .7.5 1.2 1.2 1.2h3l4.3 3.8c.9.8 2.3.2 2.3-1V5.7c0-1.2-1.4-1.8-2.3-1L7.2 8.5zm13.2.2a1.2 1.2 0 0 0-1.7 1.7l1.6 1.6-1.6 1.6a1.2 1.2 0 0 0 1.7 1.7l1.6-1.6 1.6 1.6a1.2 1.2 0 0 0 1.7-1.7L20.7 12l1.6-1.6a1.2 1.2 0 0 0-1.7-1.7L19 10.3z"
}));
}
function IconPictureInPictureFill(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
fillRule: "evenodd",
d: "M4.8 4h14.4C20.7 4 22 5.3 22 6.8v10.4c0 1.5-1.3 2.8-2.8 2.8H4.8A2.8 2.8 0 0 1 2 17.2V6.8C2 5.3 3.3 4 4.8 4m1 3.2c-.3 0-.6.3-.6.6v8.4c0 .3.3.6.6.6h5.9v-4.6c0-1.3 1-2.3 2.3-2.3h5.8V7.8c0-.3-.3-.6-.6-.6zm8.1 9.7h5.2c.4 0 .7-.3.7-.7v-3.9h-5.2c-.4 0-.7.3-.7.7z"
}));
}
function IconChevronLeft(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "none",
stroke: "currentColor",
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 3,
d: "m15.8 5.2-6.8 6.8 6.8 6.8"
}));
}
function IconChevronRight(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "none",
stroke: "currentColor",
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 3,
d: "m8.2 5.2 6.8 6.8-6.8 6.8"
}));
}
function IconSpeedtest(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
d: "M12 3.5a9.5 9.5 0 0 0-8.7 13.3c.4.8 1.2 1.2 2 1.2h13.4c.8 0 1.6-.5 2-1.2A9.5 9.5 0 0 0 12 3.5m5.5 6.3-4.2 5a2.3 2.3 0 1 1-1.9-1.1l4.8-4.4c.8-.8 2 .4 1.3 1.2"
}));
}
function IconQualityHigh(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
fillRule: "evenodd",
d: "M4.7 4.5h14.6c1 0 1.7.8 1.7 1.7v11.6c0 1-.8 1.7-1.7 1.7H4.7c-1 0-1.7-.8-1.7-1.7V6.2c0-1 .8-1.7 1.7-1.7m2.5 4.2c-.7 0-1.2.5-1.2 1.2v4.2c0 .7.5 1.2 1.2 1.2s1.2-.5 1.2-1.2V13h2.4v1.1c0 .7.5 1.2 1.2 1.2s1.2-.5 1.2-1.2V9.9c0-.7-.5-1.2-1.2-1.2s-1.2.5-1.2 1.2V11H8.4V9.9c0-.7-.5-1.2-1.2-1.2m8.9 0c-.7 0-1.2.5-1.2 1.2v4.2c0 .7.5 1.2 1.2 1.2h1c1.8 0 3-1.3 3-3.3s-1.2-3.3-3-3.3zm1.2 2.2c.3 0 .5.4.5 1.1s-.2 1.1-.5 1.1h-.1v-2.2z"
}));
}
function IconSubtitles(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "currentColor",
fillRule: "evenodd",
d: "M4.8 5h14.4C20.7 5 22 6.3 22 7.8v8.4c0 1.5-1.3 2.8-2.8 2.8H4.8A2.8 2.8 0 0 1 2 16.2V7.8C2 6.3 3.3 5 4.8 5M7.6 11.2h2.8a1.1 1.1 0 1 0 0-2.2H7.6A2.6 2.6 0 0 0 5 11.6v.8A2.6 2.6 0 0 0 7.6 15h2.8a1.1 1.1 0 1 0 0-2.2H7.6a.4.4 0 0 1-.4-.4v-.8c0-.2.2-.4.4-.4m7.2 0h2.6a1.1 1.1 0 1 0 0-2.2h-2.6a2.6 2.6 0 0 0-2.6 2.6v.8a2.6 2.6 0 0 0 2.6 2.6h2.6a1.1 1.1 0 1 0 0-2.2h-2.6a.4.4 0 0 1-.4-.4v-.8c0-.2.2-.4.4-.4"
}));
}
function IconCheckLg(props) {
return createElement(IconBase, Object.assign({}, props), createElement("path", {
fill: "none",
stroke: "currentColor",
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 3,
d: "m5.2 12.2 4.3 4.2 9.3-9.3"
}));
}
var MainSettings = function MainSettings(_ref) {
var setSettingsType = _ref.setSettingsType,
currentQuality = _ref.currentQuality,
currentSpeed = _ref.currentSpeed,
currentSubtitle = _ref.currentSubtitle,
haveQuality = _ref.haveQuality,
haveSubtitle = _ref.haveSubtitle;
return React__default.createElement("div", {
className: "opacity-animation settings-content scale-in-bl w-full",
onClick: function onClick(e) {
return e.stopPropagation();
}
}, React__default.createElement("div", {
onClick: function onClick() {
return setSettingsType("playspeed");
},
className: "main-settings cursor-pointer "
}, React__default.createElement("div", {
className: "main-settings-content"
}, React__default.createElement(IconSpeedtest, {
fontSize: 20,
className: "mr-3"
}), React__default.createElement("p", {
className: "text-sm font-semibold"
}, "Play speed")), React__default.createElement("div", {
className: "main-settings-content"
}, React__default.createElement("p", {
className: "text-sm font-semibold line-clamp-1 mr-3"
}, currentSpeed), React__default.createElement(IconChevronRight, {
fontSize: 20
}))), haveQuality && React__default.createElement("div", {
onClick: function onClick() {
return setSettingsType("quality");
},
className: "main-settings-content justify-between cursor-pointer p-2 "
}, React__default.createElement("div", {
className: "main-settings-content"
}, React__default.createElement(IconQualityHigh, {
fontSize: 22,
className: "mr-3"
}), React__default.createElement("p", {
className: "text-sm font-semibold"
}, "Quality")), React__default.createElement("div", {
className: "main-settings-content"
}, React__default.createElement("p", {
className: "text-sm font-semibold line-clamp-1 mr-3"
}, currentQuality), React__default.createElement(IconChevronRight, {
fontSize: 20
}))), haveSubtitle && React__default.createElement("div", {
onClick: function onClick() {
return setSettingsType("subtitle");
},
className: "main-settings-content justify-between cursor-pointer p-2"
}, React__default.createElement("div", {
className: "main-settings-content"
}, React__default.createElement(IconSubtitles, {
fontSize: 22,
className: "mr-3"
}), React__default.createElement("p", {
className: "text-sm font-semibold"
}, "Subtitle")), React__default.createElement("div", {
className: "main-settings-content"
}, React__default.createElement("p", {
className: "text-sm font-semibold line-clamp-1 mr-3"
}, currentSubtitle), React__default.createElement(IconChevronRight, {
fontSize: 20
}))));
};
var PlaySpeedSettings = function PlaySpeedSettings(_ref) {
var setSettingsType = _ref.setSettingsType,
currentPlaySpeed = _ref.currentPlaySpeed,
handleChangePlaySpeed = _ref.handleChangePlaySpeed;
return React__default.createElement("div", {
className: "opacity-animation settings-content scale-in-bl w-full",
onClick: function onClick(e) {
return e.stopPropagation();
}
}, React__default.createElement("div", {
onClick: function onClick(e) {
e.stopPropagation();
setSettingsType("main");
},
className: "main-settings-content p-2 cursor-pointer"
}, React__default.createElement(IconChevronLeft, {
fontSize: 20,
className: "mr-3"
}), React__default.createElement("p", {
className: "text-sm font-semibold"
}, "Play speed")), React__default.createElement("div", null, playSpeedOptions == null ? void 0 : playSpeedOptions.map(function (item, index) {
return React__default.createElement("div", {
className: "p-2 text-sm font-semibold main-settings-content cursor-pointer",
key: item == null ? void 0 : item.value,
onClick: function onClick() {
return handleChangePlaySpeed(index, item == null ? void 0 : item.value);
}
}, React__default.createElement("div", {
className: "icon-20px mr-3"
}, currentPlaySpeed === index && React__default.createElement(IconCheckLg, {
fontSize: 20
})), React__default.createElement("p", null, item == null ? void 0 : item.label));
})));
};
var QualitySettings = function QualitySettings(_ref) {
var _source$currentSource;
var setSettingsType = _ref.setSettingsType,
source = _ref.source,
currentSource = _ref.currentSource,
handleChangeSource = _ref.handleChangeSource,
autoQuality = _ref.autoQuality,
handleSelectAutoQuality = _ref.handleSelectAutoQuality;
var showAutoOption = (source == null ? void 0 : source.length) > 1;
return React__default.createElement("div", {
className: "overflow-y-auto opacity-animation settings-content scale-in-bl w-full",
onClick: function onClick(e) {
return e.stopPropagation();
}
}, React__default.createElement("div", {
onClick: function onClick(e) {
e.stopPropagation();
setSettingsType("main");
},
className: "main-settings-content p-2 cursor-pointer"
}, React__default.createElement(IconChevronLeft, {
fontSize: 20,
className: "mr-3"
}), React__default.createElement("p", {
className: "text-sm font-semibold"
}, "Quality")), React__default.createElement("div", null, showAutoOption && React__default.createElement("div", {
onClick: handleSelectAutoQuality,
className: "p-2 text-sm font-semibold main-settings-content cursor-pointer"
}, React__default.createElement("div", {
className: "icon-20px mr-3"
}, autoQuality && React__default.createElement(IconCheckLg, {
fontSize: 20
})), React__default.createElement("p", null, autoQuality ? "Auto (" + (source == null || (_source$currentSource = source[currentSource]) == null ? void 0 : _source$currentSource.label) + ")" : "Auto")), source == null ? void 0 : source.map(function (item, index) {
return React__default.createElement("div", {
onClick: function onClick() {
return handleChangeSource(index);
},
className: "p-2 text-sm font-semibold main-settings-content cursor-pointer",
key: item == null ? void 0 : item.url
}, React__default.createElement("div", {
className: "icon-20px mr-3"
}, !autoQuality && currentSource === index && React__default.createElement(IconCheckLg, {
fontSize: 20
})), React__default.createElement("p", null, item == null ? void 0 : item.label));
})));
};
var SubtitleSettings = function SubtitleSettings(_ref) {
var currentSubtitle = _ref.currentSubtitle,
handleChangeSubtitle = _ref.handleChangeSubtitle,
setSettingsType = _ref.setSettingsType,
subtitle = _ref.subtitle,
handleTurnOffSubtitle = _ref.handleTurnOffSubtitle;
return React__default.createElement("div", {
className: "overflow-y-auto opacity-animation settings-content scale-in-bl w-full",
onClick: function onClick(e) {
return e.stopPropagation();
}
}, React__default.createElement("div", {
onClick: function onClick(e) {
e.stopPropagation();
setSettingsType("main");
},
className: "main-settings-content p-2 cursor-pointer"
}, React__default.createElement(IconChevronLeft, {
fontSize: 20,
className: "mr-3"
}), React__default.createElement("p", {
className: "text-sm font-semibold"
}, "Subtitle")), React__default.createElement("div", null, React__default.createElement("div", {
onClick: handleTurnOffSubtitle,
className: "p-2 text-sm font-semibold main-settings-content cursor-pointer"
}, React__default.createElement("div", {
className: "icon-20px mr-3"
}, currentSubtitle === null && React__default.createElement(IconCheckLg, {
fontSize: 20
})), React__default.createElement("p", null, "Off")), subtitle == null ? void 0 : subtitle.map(function (item, index) {
return React__default.createElement("div", {
onClick: function onClick() {
return handleChangeSubtitle(index);
},
className: "p-2 text-sm font-semibold main-settings-content cursor-pointer",
key: item == null ? void 0 : item.url
}, React__default.createElement("div", {
className: "icon-20px mr-3"
}, currentSubtitle === index && React__default.createElement(IconCheckLg, {
fontSize: 20
})), React__default.createElement("p", null, item == null ? void 0 : item.lang));
})));
};
var CircularProgress = function CircularProgress() {
return React__default.createElement("svg", {
viewBox: "25 25 50 50",
className: "loading"
}, React__default.createElement("circle", {
cx: "50",
cy: "50",
r: "20"
}));
};
var Slider = function Slider(_ref) {
var value = _ref.value,
onChange = _ref.onChange,
_ref$min = _ref.min,
min = _ref$min === void 0 ? 0 : _ref$min,
_ref$max = _ref.max,
max = _ref$max === void 0 ? 100 : _ref$max,
_ref$className = _ref.className,
className = _ref$className === void 0 ? "" : _ref$className,
_ref$color = _ref.color,
color = _ref$color === void 0 ? "#ef4444" : _ref$color,
trackColor = _ref.trackColor,
_ref$buffered = _ref.buffered,
buffered = _ref$buffered === void 0 ? [] : _ref$buffered,
_ref$bufferedColor = _ref.bufferedColor,
bufferedColor = _ref$bufferedColor === void 0 ? "rgba(255, 255, 255, 0.75)" : _ref$bufferedColor,
_ref$live = _ref.live,
live = _ref$live === void 0 ? false : _ref$live,
_ref$markers = _ref.markers,
markers = _ref$markers === void 0 ? [] : _ref$markers,
onDragEnd = _ref.onDragEnd,
onPreview = _ref.onPreview,
_ref$children = _ref.children,
children = _ref$children === void 0 ? React__default.createElement(React__default.Fragment, null) : _ref$children;
var sliderRef = useRef(null);
var _useState = useState(false),
isDragging = _useState[0],
setIsDragging = _useState[1];
var handleCalculateValue = function handleCalculateValue(clientX) {
if (!sliderRef.current || live) return;
var rect = sliderRef.current.getBoundingClientRect();
var percent = (clientX - rect.left) / rect.width;
var newValue = Math.min(Math.max(percent * (max - min) + min, min), max);
return newValue;
};
var handleSliderChange = function handleSliderChange(clientX) {
var newValue = handleCalculateValue(clientX);
onChange(newValue);
};
var handlePreview = function handlePreview(clientX) {
var _sliderRef$current;
var newValue = handleCalculateValue(clientX);
var rect = (_sliderRef$current = sliderRef.current) == null ? void 0 : _sliderRef$current.getBoundingClientRect();
var left = rect == null ? void 0 : rect.left;
var width = rect == null ? void 0 : rect.width;
var percent = (clientX - left) / width;
onPreview && onPreview(newValue, percent);
};
var handleMove = function handleMove(clientX) {
if (isDragging) handleSliderChange(clientX);
};
var handleMoveEnd = function handleMoveEnd() {
setIsDragging(false);
onDragEnd && onDragEnd();
document.body.style.userSelect = "auto";
};
var handleStart = function handleStart(clientX) {
if (!live) {
setIsDragging(true);
handleSliderChange(clientX);
}
document.body.style.userSelect = "none";
};
useEffect(function () {
if (!isDragging) return;
var handleMouseMove = function handleMouseMove(e) {
return handleMove(e.clientX);
};
var handleTouchMove = function handleTouchMove(e) {
return handleMove(e.touches[0].clientX);
};
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("touchmove", handleTouchMove);
document.addEventListener("mouseup", handleMoveEnd);
document.addEventListener("touchend", handleMoveEnd);
return function () {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("touchmove", handleTouchMove);
document.removeEventListener("mouseup", handleMoveEnd);
document.removeEventListener("touchend", handleMoveEnd);
onDragEnd && onDragEnd();
};
}, [isDragging]);
var range = max - min;
var canRenderTimeline = Number.isFinite(range) && range > 0;
var percentValue = canRenderTimeline ? (value - min) / range * 100 : 0;
var markerItems = !live && canRenderTimeline ? markers : [];
var bufferedItems = !live && canRenderTimeline ? buffered : [];
return React__default.createElement("div", {
ref: sliderRef,
className: "progress-vnet " + className,
onClick: function onClick(e) {
return handleSliderChange(e.clientX);
},
onMouseDown: function onMouseDown(e) {
return handleStart(e.clientX);
},
onTouchStart: function onTouchStart(e) {
return handleStart(e.touches[0].clientX);
},
onMouseMove: function onMouseMove(e) {
return handlePreview(e.clientX);
},
onMouseLeave: function onMouseLeave() {
onPreview && onPreview(null, null);
onDragEnd && onDragEnd();
}
}, React__default.createElement("div", {
className: "progress-gray",
style: trackColor ? {
backgroundColor: trackColor
} : undefined
}, bufferedItems.map(function (rangeItem, index) {
var start = Math.max(rangeItem.start, min);
var end = Math.min(rangeItem.end, max);
if (end <= start) return null;
var left = (start - min) / range * 100;
var width = (end - start) / range * 100;
return React__default.createElement("div", {
key: "buffered-" + index,
className: "progress-buffered",
style: {
left: left + "%",
width: width + "%",
backgroundColor: bufferedColor
}
});
}), markerItems.map(function (marker) {
var start = Math.max(marker.start, min);
var end = Math.min(marker.end, max);
if (end <= start) return null;
var left = (start - min) / range * 100;
var width = (end - start) / range * 100;
return React__default.createElement("div", {
key: marker.label + "-" + marker.start + "-" + marker.end,
className: "progress-marker",
style: {
left: left + "%",
width: width + "%",
backgroundColor: marker.color || color
},
onMouseEnter: function onMouseEnter() {
return onPreview && onPreview(null, null);
},
onMouseMove: function onMouseMove(e) {
e.stopPropagation();
onPreview && onPreview(null, null);
},
onMouseDown: function onMouseDown(e) {
return e.stopPropagation();
},
onTouchStart: function onTouchStart(e) {
return e.stopPropagation();
}
}, React__default.createElement("span", {
className: "progress-marker-tooltip"
}, marker.label));
}), React__default.createElement("div", {
className: "progress-main",
style: {
width: live ? "100%" : percentValue + "%",
backgroundColor: color
}
})), children, !live && React__default.createElement("div", {
className: "progress-dot",
style: {
backgroundColor: color,
left: "calc(" + percentValue + "% - 5px)"
}
}));
};
var _excluded$1 = ["color", "trackColor", "bufferedColor", "subtitle", "playerRef", "className", "poster", "videoTitle", "videoDescription", "source", "live", "autoPlay", "autoUnmuteDelay", "startIntro", "endIntro", "introColor", "startOutro", "endOutro", "outroColor", "autoQualityConfig", "Hls"];
var Player = function Player(_ref) {
var _source$map, _playerRef$current, _sourceMulti$currentS, _sourceMulti$currentS2, _playSpeedOptions$cur, _subtitle$currentSubt3, _playerRef$current12, _playerRef$current13;
var color = _ref.color,
trackColor = _ref.trackColor,
bufferedColor = _ref.bufferedColor,
subtitle = _ref.subtitle,
passedRef = _ref.playerRef,
className = _ref.className,
poster = _ref.poster,
videoTitle = _ref.videoTitle,
videoDescription = _ref.videoDescription,
src = _ref.source,
live = _ref.live,
autoPlay = _ref.autoPlay,
autoUnmuteDelay = _ref.autoUnmuteDelay,
startIntro = _ref.startIntro,
endIntro = _ref.endIntro,
introColor = _ref.introColor,
startOutro = _ref.startOutro,
endOutro = _ref.endOutro,
outroColor = _ref.outroColor,
autoQualityConfig = _ref.autoQualityConfig,
Hls = _ref.Hls,
props = _objectWithoutPropertiesLoose(_ref, _excluded$1);
var hlsRef = useRef(null);
if (!src) {
var _hlsRef$current;
if (hlsRef.current) (_hlsRef$current = hlsRef.current) == null || _hlsRef$current.destroy();
throw new Error("Missing src props");
}
var source = src;
var videoContainerRef = useRef(null);
var myRef = useRef(null);
var timeoutSeek = useRef(null);
var _useState = useState(0),
currentSource = _useState[0],
setCurrentSource = _useState[1];
var _useState2 = useState([]),
sourceMulti = _useState2[0],
setSourceMulti = _useState2[1];
var _useState3 = useState(true),
autoQuality = _useState3[0],
setAutoQuality = _useState3[1];
var autoQualityRef = useRef(autoQuality);
autoQualityRef.current = autoQuality;
// True when the source is a single m3u8 master playlist: hls.js owns the
// adaptive switching. False for user-provided Source[] profiles where the
// custom stall/buffer monitor drives auto switching.
var isMasterHlsRef = useRef(false);
var _useState4 = useState(3),
currentPlaySpeed = _useState4[0],
setCurrePlaySpeed = _useState4[1];
var _useState5 = useState(true),
showControl = _useState5[0],
setShowControl = _useState5[1];
var _useState6 = useState(false),
play = _useState6[0],
setPlay = _useState6[1];
var _useState7 = useState(0),
currentTime = _useState7[0],
setCurrentTime = _useState7[1];
var _useState8 = useState(false),
fullScreen = _useState8[0],
setFullScreen = _useState8[1];
var _useState9 = useState(function () {
return JSON.parse(localStorage.getItem(MUTED_KEY)) || false;
}),
muted = _useState9[0],
setMuted = _useState9[1];
var _useState10 = useState(true),
loading = _useState10[0],
setLoading = _useState10[1];
var _useState11 = useState(false),
showSettings = _useState11[0],
setShowSettings = _useState11[1];
var _useState12 = useState("main"),
settingsType = _useState12[0],
setSettingsType = _useState12[1];
var _useState13 = useState(0),
currentSubtitle = _useState13[0],
setCurrentSubtitle = _useState13[1];
var _useState14 = useState(function () {
return Number(localStorage.getItem(VOLUME_KEY)) || 100;
}),
volume = _useState14[0],
setVolume = _useState14[1];
var _useState15 = useState(false),
seeking = _useState15[0],
setSeeking = _useState15[1];
var _useState16 = useState({
time: null,
left: null
}),
previewTime = _useState16[0],
setPreviewTime = _useState16[1];
var _useState17 = useState(0),
liveEdgeDelay = _useState17[0],
setLiveEdgeDelay = _useState17[1];
var _useState18 = useState([]),
bufferedRanges = _useState18[0],
setBufferedRanges = _useState18[1];
// Stable identity for the source prop: effects keyed on this only re-run
// when the actual URL list changes (an inline array prop gets a new
// reference on every render and must not retrigger loads or listeners).
var sourceKey = typeof source === "string" ? source : source == null || (_source$map = source.map(function (item) {
return item == null ? void 0 : item.url;
})) == null ? void 0 : _source$map.join("|");
var defaultColor = color || "#ef4444";
var defaultIntroColor = introColor || "#f59e0b";
var defaultOutroColor = outroColor || "#8b5cf6";
var playerRef = passedRef || myRef;
var hasVideoMetadata = Boolean(videoTitle || videoDescription);
var canSyncLive = live && liveEdgeDelay > LIVE_EDGE_CLICKABLE_THRESHOLD_SECONDS;
var introSegment = normalizeSkipIntro(startIntro, endIntro);
var outroSegment = normalizeSkipOutro(startOutro, endOutro);
var activeSkipSegment = isSkipSegmentActive(introSegment, currentTime) ? introSegment : isSkipSegmentActive(outroSegment, currentTime) ? outroSegment : null;
var videoDuration = (_playerRef$current = playerRef.current) == null ? void 0 : _playerRef$current.duration;
var canShowSkipMarkers = !live && typeof videoDuration === "number" && Number.isFinite(videoDuration);
var skipMarkers = canShowSkipMarkers ? [introSegment, outroSegment].filter(function (segment) {
return Boolean(segment);
}).map(function (segment) {
return _extends({}, segment, {
color: segment.label === "Skip intro" ? defaultIntroColor : defaultOutroColor
});
}) : [];
var updateLiveEdgeDelay = function updateLiveEdgeDelay() {
if (!live || !playerRef.current) {
setLiveEdgeDelay(0);
return;
}
var livePosition = getLivePosition(playerRef.current, hlsRef.current);
if (livePosition === null) {
setLiveEdgeDelay(0);
return;
}
setLiveEdgeDelay(Math.max(0, livePosition - playerRef.current.currentTime));
};
var handlePlayPause = function handlePlayPause() {
setPlay(function (prev) {
return !prev;
});
};
var handleFullScreen = function handleFullScreen() {
if (!videoContainerRef.current) return;
toggleFullscreen(videoContainerRef.current);
};
var handleTimeUpdate = function handleTimeUpdate() {
var _playerRef$current2;
if (seeking) return;
setCurrentTime((playerRef == null || (_playerRef$current2 = playerRef.current) == null ? void 0 : _playerRef$current2.currentTime) || 0);
updateLiveEdgeDelay();
};
var handleToggleMuted = function handleToggleMuted() {
setMuted(function (prev) {
return !prev;
});
localStorage.setItem(MUTED_KEY, JSON.stringify(!muted));
};
var handleChangePlaySpeed = function handleChangePlaySpeed(index, value) {
setCurrePlaySpeed(index);
if (playerRef !== null && (playerRef == null ? void 0 : playerRef.current) !== null) {
playerRef.current.playbackRate = value;
}
setShowSettings(false);
setSettingsType("main");
};
var applySourceChange = function applySourceChange(index) {
var _playerRef$current3;
if (!(playerRef != null && playerRef.current)) return;
setCurrentSource(index);
if (hlsRef != null && hlsRef.current && isMasterHlsRef.current) {
// Master playlist: switch level in place, playback continues seamlessly.
hlsRef.current.currentLevel = index;
return;
}
var existTrack = playerRef == null || (_playerRef$current3 = playerRef.current) == null ? void 0 : _playerRef$current3.querySelector("track");
if (existTrack) existTrack.remove();
var tmpCurrentTime = playerRef.current.currentTime || currentTime;
if (hlsRef != null && hlsRef.current) {
var _sourceMulti$index;
var hls = hlsRef == null ? void 0 : hlsRef.current;
hls.startLevel = index;
hls.loadSource(sourceMulti == null || (_sourceMulti$index = sourceMulti[index]) == null ? void 0 : _sourceMulti$index.url);
hls.startLoad();
} else {
var _playerRef$current4, _sourceMulti$index2;
playerRef == null || (_playerRef$current4 = playerRef.current) == null || _playerRef$current4.setAttribute("src", sourceMulti == null || (_sourceMulti$index2 = sourceMulti[index]) == null ? void 0 : _sourceMulti$index2.url);
}
setCurrentTime(tmpCurrentTime);
if (playerRef !== null && (playerRef == null ? void 0 : playerRef.current) !== null) {
playerRef.current.currentTime = tmpCurrentTime;
playerRef.current.play();
}
};
var handleChangeSource = function handleChangeSource(index) {
if (!(playerRef != null && playerRef.current)) return;
if (currentSource === index && !autoQuality) return;
setAutoQuality(false);
applySourceChange(index);
setShowSettings(false);
setSettingsType("main");
};
var handleSelectAutoQuality = function handleSelectAutoQuality() {
setAutoQuality(true);
if (hlsRef != null && hlsRef.current && isMasterHlsRef.current) {
hlsRef.current.currentLevel = HLS_AUTO_LEVEL;
}
setShowSettings(false);
setSettingsType("main");
};
var handleLoadVideoMp4 = function handleLoadVideoMp4() {
var _hlsRef$current2;
if (hlsRef != null && hlsRef.current) hlsRef == null || (_hlsRef$current2 = hlsRef.current) == null || _hlsRef$current2.destroy();
isMasterHlsRef.current = false;
setSourceMulti(typeof source === "string" ? [{
label: "Default",
url: source
}] : source);
if (typeof source === "string") {
var _playerRef$current5;
playerRef == null || (_playerRef$current5 = playerRef.current) == null || _playerRef$current5.setAttribute("src", source);
} else {
var _playerRef$current6, _source;
setCurrentSource((source == null ? void 0 : source.length) - 1);
playerRef == null || (_playerRef$current6 = playerRef.current) == null || _playerRef$current6.setAttribute("src", source == null || (_source = source[(source == null ? void 0 : source.length) - 1]) == null ? void 0 : _source.url);
}
};
var handleLoadVideoM3u8 = function handleLoadVideoM3u8() {
if (!Hls) throw Error("To use video type m3u8 try install `npm i hls.js` and pass props Hls");
if (!(playerRef != null && playerRef.current)) return;
var HlsConstructor = Hls;
if (!HlsConstructor.isSupported()) throw Error("Not support hls");
if (hlsRef != null && hlsRef.current) {
var _hlsRef$current3;
hlsRef == null || (_hlsRef$current3 = hlsRef.current) == null || _hlsRef$current3.destroy();
}
hlsRef.current = new HlsConstructor();
var hls = hlsRef == null ? void 0 : hlsRef.current;
hls.on(HlsConstructor.Events.MANIFEST_PARSED, function (_, data) {
if (typeof source !== "string") {
isMasterHlsRef.current = false;
return setSourceMulti(source);
}
console.log((data == null ? void 0 : data.levels) || []);
var levels = (data == null ? void 0 : data.levels) || [];
isMasterHlsRef.current = levels.length > 1;
setSourceMulti(levels == null ? void 0 : levels.map(function (item) {
var _item$url;
return {
label: (item == null ? void 0 : item.height) + "p",
url: Array.isArray(item == null ? void 0 : item.url) ? item == null || (_item$url = item.url) == null ? void 0 : _item$url[0] : (item == null ? void 0 : item.url) || ""
};
}));
if (isMasterHlsRef.current && autoQualityRef.current) {
// Let hls.js ABR pick the level based on measured bandwidth. Clamp
// the displayed level until the first LEVEL_SWITCHED arrives.
setCurrentSource(function (prev) {
return Math.max(0, Math.min(prev, levels.length - 1));
});
hls.currentLevel = HLS_AUTO_LEVEL;
} else {
setCurrentSource((levels == null ? void 0 : levels.length) - 1);
hls.startLevel = (levels == null ? void 0 : levels.length) - 1;
}
});
hls.on(HlsConstructor.Events.LEVEL_SWITCHED, function (_, data) {
if (!isMasterHlsRef.current) return;
if (typeof (data == null ? void 0 : data.level) !== "number") return;
setCurrentSource(data.level);
});
hls.attachMedia(playerRef == null ? void 0 : playerRef.current);
if (typeof source === "string") {
hls.loadSource(source);
} else {
var _source$currentSource;
hls.loadSource(source == null || (_source$currentSource = source[currentSource]) == null ? void 0 : _source$currentSource.url);
}
hls.startLoad();
};
var handleChangeSubtitle = function handleChangeSubtitle(index) {
setCurrentSubtitle(index);
setShowSettings(false);
setSettingsType("main");
};
var handleVideoPicture = function handleVideoPicture() {
if (!playerRef.current) return;
togglePictureInPicture(playerRef.current);
};
var handleGoLive = function handleGoLive() {
var _hlsRef$current4;
if (!canSyncLive || !playerRef.current) return;
var videoElement = playerRef.current;
var livePosition = getLivePosition(videoElement, hlsRef.current);
(_hlsRef$current4 = hlsRef.current) == null || _hlsRef$current4.startLoad();
if (livePosition === null) {
videoElement.play()["catch"](console.error);
setPlay(true);
return;
}
videoElement.currentTime = livePosition;
setCurrentTime(livePosition);
setLiveEdgeDelay(0);
setSeeking(false);
setPlay(true);
videoElement.play()["catch"](console.error);
};
var handleSkipSegment = function handleSkipSegment() {
if (!activeSkipSegment || !playerRef.current) return;
playerRef.current.currentTime = activeSkipSegment.end;
setCurrentTime(activeSkipSegment.end);
setSeeking(false);
};
var handleVolumeChange = function handleVolumeChange(volume) {
if (playerRef !== null && (playerRef == null ? void 0 : playerRef.current) !== null) {
setSeeking(true);
setVolume(volume);
localStorage.setItem(VOLUME_KEY, JSON.stringify(volume));
playerRef.current.volume = volume / 100;
}
};
var handleTurnOffSubtitle = function handleTurnOffSubtitle() {
var _playerRef$current7;
setCurrentSubtitle(null);
var track = playerRef == null || (_playerRef$current7 = playerRef.current) == null ? void 0 : _playerRef$current7.querySelector("track");
if (track) {
track.remove();
}
setShowSettings(false);
setSettingsType("main");
};
var handleChangeTime = function handleChangeTime(time) {
if (playerRef !== null && (playerRef == null ? void 0 : playerRef.current) !== null) {
setSeeking(true);
setCurrentTime(time);
if (timeoutSeek.current) {
clearTimeout(timeoutSeek.current);
}
timeoutSeek.current = setTimeout(function () {
if (playerRef.current) {
playerRef.current.currentTime = time;
}
}, 100);
}
};
var handleDragEnd = function handleDragEnd() {
setPreviewTime({
time: null,
left: null
});
setSeeking(false);
};
useEffect(function () {
if (playerRef !== null && (playerRef == null ? void 0 : playerRef.current) !== null) {
playerRef.current.volume = (volume <= 0 ? 0 : volume) / 100;
}
}, [volume]);
useEffect(function () {
if (!live || !playerRef.current) {
setLiveEdgeDelay(0);
return;
}
var videoElement = playerRef.current;
var handleLiveProgress = function handleLiveProgress() {
return updateLiveEdgeDelay();
};
videoElement.addEventListener("progress", handleLiveProgress);
videoElement.addEventListener("loadedmetadata", handleLiveProgress);
var interval = window.setInterval(handleLiveProgress, 1000);
handleLiveProgress();
return function () {
videoElement.removeEventListener("progress", handleLiveProgress);
videoElement.removeEventListener("loadedmetadata", handleLiveProgress);
window.clearInterval(interval);
};
}, [live, sourceKey, currentSource]);
// Track how far the video has been downloaded so the seek bar can render
// the buffered ranges behind the playback progress.
useEffect(function () {
if (!playerRef.current) return;
var videoElement = playerRef.current;
var lastSignature = "";
var updateBuffered = function updateBuffered() {
var buffered = videoElement.buffered;
var ranges = [];
var signature = "";
for (var i = 0; i < buffered.length; i++) {
var start = buffered.start(i);
var end = buffered.end(i);
ranges.push({
start: start,
end: end
});
signature += start.toFixed(1) + ":" + end.toFixed(1) + ";";
}
// Skip the state update (and the re-render it causes) when the
// buffered ranges have not visibly moved since the last event.
if (signature === lastSignature) return;
lastSignature = signature;
setBufferedRanges(ranges);
};
var resetBuffered = function resetBuffered() {
lastSignature = "";
setBufferedRanges([]);
};
videoElement.addEventListener("progress", updateBuffered);
videoElement.addEventListener("timeupdate", updateBuffered);
videoElement.addEventListener("loadedmetadata", updateBuffered);
videoElement.addEventListener("emptied", resetBuffered);
return function () {
videoElement.removeEventListener("progress", updateBuffered);
videoElement.removeEventListener("timeupdate", updateBuffered);
videoElement.removeEventListener("loadedmetadata", updateBuffered);
videoElement.removeEventListener("emptied", resetBuffered);
};
}, [sourceKey, currentSource]);
// Custom auto-quality monitor. Only used when hls.js ABR is not in charge
// (user-provided Source[] profiles or progressive mp4): steps down one
// profile when playback stalls repeatedly or for too long, steps back up
// when the buffer stays healthy.
useEffect(function () {
if (!autoQuality) return;
if (isMasterHlsRef.current) return;
if ((sourceMulti == null ? void 0 : sourceMulti.length) <= 1) return;
if (!playerRef.current) return;
var cfg = resolveAutoQuality