UNPKG

vnetwork-player

Version:

A React component custom player support video m3u8, mp4

1,216 lines (1,189 loc) 66.8 kB
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