/*! * artplayer.js v5.4.1 * Github: https://github.com/zhw2590582/ArtPlayer * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ function getDefaultExportFromCjs(x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; } var optionValidator$1 = { exports: {} }; var optionValidator = optionValidator$1.exports; var hasRequiredOptionValidator; function requireOptionValidator() { if (hasRequiredOptionValidator) return optionValidator$1.exports; hasRequiredOptionValidator = 1; (function(module, exports$1) { !(function(r, t) { module.exports = t(); })(optionValidator, function() { function e(r) { return (e = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(r2) { return typeof r2; } : function(r2) { return r2 && "function" == typeof Symbol && r2.constructor === Symbol && r2 !== Symbol.prototype ? "symbol" : typeof r2; })(r); } var n2 = Object.prototype.toString, c = function(r) { if (void 0 === r) return "undefined"; if (null === r) return "null"; var t = e(r); if ("boolean" === t) return "boolean"; if ("string" === t) return "string"; if ("number" === t) return "number"; if ("symbol" === t) return "symbol"; if ("function" === t) return (function(r2) { return "GeneratorFunction" === o2(r2); })(r) ? "generatorfunction" : "function"; if ((function(r2) { return Array.isArray ? Array.isArray(r2) : r2 instanceof Array; })(r)) return "array"; if ((function(r2) { if (r2.constructor && "function" == typeof r2.constructor.isBuffer) return r2.constructor.isBuffer(r2); return false; })(r)) return "buffer"; if ((function(r2) { try { if ("number" == typeof r2.length && "function" == typeof r2.callee) return true; } catch (r3) { if (-1 !== r3.message.indexOf("callee")) return true; } return false; })(r)) return "arguments"; if ((function(r2) { return r2 instanceof Date || "function" == typeof r2.toDateString && "function" == typeof r2.getDate && "function" == typeof r2.setDate; })(r)) return "date"; if ((function(r2) { return r2 instanceof Error || "string" == typeof r2.message && r2.constructor && "number" == typeof r2.constructor.stackTraceLimit; })(r)) return "error"; if ((function(r2) { return r2 instanceof RegExp || "string" == typeof r2.flags && "boolean" == typeof r2.ignoreCase && "boolean" == typeof r2.multiline && "boolean" == typeof r2.global; })(r)) return "regexp"; switch (o2(r)) { case "Symbol": return "symbol"; case "Promise": return "promise"; case "WeakMap": return "weakmap"; case "WeakSet": return "weakset"; case "Map": return "map"; case "Set": return "set"; case "Int8Array": return "int8array"; case "Uint8Array": return "uint8array"; case "Uint8ClampedArray": return "uint8clampedarray"; case "Int16Array": return "int16array"; case "Uint16Array": return "uint16array"; case "Int32Array": return "int32array"; case "Uint32Array": return "uint32array"; case "Float32Array": return "float32array"; case "Float64Array": return "float64array"; } if ((function(r2) { return "function" == typeof r2.throw && "function" == typeof r2.return && "function" == typeof r2.next; })(r)) return "generator"; switch (t = n2.call(r)) { case "[object Object]": return "object"; case "[object Map Iterator]": return "mapiterator"; case "[object Set Iterator]": return "setiterator"; case "[object String Iterator]": return "stringiterator"; case "[object Array Iterator]": return "arrayiterator"; } return t.slice(8, -1).toLowerCase().replace(/\s/g, ""); }; function o2(r) { return r.constructor ? r.constructor.name : null; } function f2(r, t) { var e2 = 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : ["option"]; return s2(r, t, e2), y(r, t, e2), (function(a2, i, u) { var r2 = c(i), t2 = c(a2); if ("object" === r2) { if ("object" !== t2) throw new Error("[Type Error]: '".concat(u.join("."), "' require 'object' type, but got '").concat(t2, "'")); Object.keys(i).forEach(function(r3) { var t3 = a2[r3], e3 = i[r3], n3 = u.slice(); n3.push(r3), s2(t3, e3, n3), y(t3, e3, n3), f2(t3, e3, n3); }); } if ("array" === r2) { if ("array" !== t2) throw new Error("[Type Error]: '".concat(u.join("."), "' require 'array' type, but got '").concat(t2, "'")); a2.forEach(function(r3, t3) { var e3 = a2[t3], n3 = i[t3] || i[0], o3 = u.slice(); o3.push(t3), s2(e3, n3, o3), y(e3, n3, o3), f2(e3, n3, o3); }); } })(r, t, e2), r; } function s2(r, t, e2) { if ("string" === c(t)) { var n3 = c(r); if ("?" === t[0] && (t = t.slice(1) + "|undefined"), !(-1 < t.indexOf("|") ? t.split("|").map(function(r2) { return r2.toLowerCase().trim(); }).filter(Boolean).some(function(r2) { return n3 === r2; }) : t.toLowerCase().trim() === n3)) throw new Error("[Type Error]: '".concat(e2.join("."), "' require '").concat(t, "' type, but got '").concat(n3, "'")); } } function y(r, t, e2) { if ("function" === c(t)) { var n3 = t(r, c(r), e2); if (true !== n3) { var o3 = c(n3); throw "string" === o3 ? new Error(n3) : "error" === o3 ? n3 : new Error("[Validator Error]: The scheme for '".concat(e2.join("."), "' validator require return true, but got '").concat(n3, "'")); } } } return f2.kindOf = c, f2; }); })(optionValidator$1); return optionValidator$1.exports; } var optionValidatorExports = requireOptionValidator(); const validator = /* @__PURE__ */ getDefaultExportFromCjs(optionValidatorExports); const version$1 = "5.4.1"; const config$1 = { properties: [ "audioTracks", "autoplay", "buffered", "controller", "controls", "crossOrigin", "currentSrc", "currentTime", "defaultMuted", "defaultPlaybackRate", "duration", "ended", "error", "loop", "mediaGroup", "muted", "networkState", "paused", "playbackRate", "played", "preload", "readyState", "seekable", "seeking", "src", "startDate", "textTracks", "videoTracks", "volume" ], methods: ["addTextTrack", "canPlayType", "load", "play", "pause"], events: [ "abort", "canplay", "canplaythrough", "durationchange", "emptied", "ended", "error", "loadeddata", "loadedmetadata", "loadstart", "pause", "play", "playing", "progress", "ratechange", "seeked", "seeking", "stalled", "suspend", "timeupdate", "volumechange", "waiting" ], prototypes: [ "width", "height", "videoWidth", "videoHeight", "poster", "webkitDecodedFrameCount", "webkitDroppedFrameCount", "playsInline", "webkitSupportsFullscreen", "webkitDisplayingFullscreen", "onenterpictureinpicture", "onleavepictureinpicture", "disablePictureInPicture", "cancelVideoFrameCallback", "requestVideoFrameCallback", "getVideoPlaybackQuality", "requestPictureInPicture", "webkitEnterFullScreen", "webkitEnterFullscreen", "webkitExitFullScreen", "webkitExitFullscreen" ] }; const userAgent = globalThis?.CUSTOM_USER_AGENT ?? (typeof navigator !== "undefined" ? navigator.userAgent : ""); const isSafari = /^(?:(?!chrome|android).)*safari/i.test(userAgent); const isIOS = /iPad|iPhone|iPod/i.test(userAgent) && !window.MSStream; const isIOS13 = isIOS || userAgent.includes("Macintosh") && navigator.maxTouchPoints >= 1; const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent) || isIOS13; const isBrowser = typeof window !== "undefined" && typeof document !== "undefined"; function query(selector, parent = document) { return parent.querySelector(selector); } function queryAll(selector, parent = document) { return Array.from(parent.querySelectorAll(selector)); } function addClass(target, className) { return target.classList.add(className); } function removeClass(target, className) { return target.classList.remove(className); } function hasClass(target, className) { return target.classList.contains(className); } function append(parent, child) { if (child instanceof Element) { parent.appendChild(child); } else { parent.insertAdjacentHTML("beforeend", String(child)); } return parent.lastElementChild || parent.lastChild; } function remove(child) { return child.parentNode.removeChild(child); } function setStyle(element, key, value) { element.style[key] = value; return element; } function setStyles(element, styles) { for (const key in styles) { setStyle(element, key, styles[key]); } return element; } function getStyle(element, key, numberType = true) { const value = window.getComputedStyle(element, null).getPropertyValue(key); return numberType ? Number.parseFloat(value) : value; } function siblings(target) { return Array.from(target.parentElement.children).filter((item) => item !== target); } function inverseClass(target, className) { siblings(target).forEach((item) => removeClass(item, className)); addClass(target, className); } function tooltip(target, msg, pos = "top") { if (isMobile) return; target.setAttribute("aria-label", msg); addClass(target, "hint--rounded"); addClass(target, `hint--${pos}`); } function isInViewport(el, offset = 0) { const rect = el.getBoundingClientRect(); const windowHeight = window.innerHeight || document.documentElement.clientHeight; const windowWidth = window.innerWidth || document.documentElement.clientWidth; const vertInView = rect.top - offset <= windowHeight && rect.top + rect.height + offset >= 0; const horInView = rect.left - offset <= windowWidth + offset && rect.left + rect.width + offset >= 0; return vertInView && horInView; } function includeFromEvent(event, target) { return getComposedPath(event).includes(target); } function replaceElement(newChild, oldChild) { oldChild.parentNode.replaceChild(newChild, oldChild); return newChild; } function createElement(tag) { return document.createElement(tag); } function getIcon(key = "", html = "") { const icon = createElement("i"); addClass(icon, "art-icon"); addClass(icon, `art-icon-${key}`); append(icon, html); return icon; } function setStyleText(id2, style2) { let $style = document.getElementById(id2); if (!$style) { $style = document.createElement("style"); $style.id = id2; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { document.head.appendChild($style); }); } else { (document.head || document.documentElement).appendChild($style); } } $style.textContent = style2; } function supportsFlex() { const div = document.createElement("div"); div.style.display = "flex"; return div.style.display === "flex"; } function getRect(el) { return el.getBoundingClientRect(); } function loadImg(url, scale) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = function() { if (!scale || scale === 1) { resolve(img); } else { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); canvas.width = img.width * scale; canvas.height = img.height * scale; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob((blob) => { const blobUrl = URL.createObjectURL(blob); const scaledImg = new Image(); scaledImg.onload = function() { resolve(scaledImg); }; scaledImg.onerror = function() { URL.revokeObjectURL(blobUrl); reject(new Error(`Image load failed: ${url}`)); }; scaledImg.src = blobUrl; }); } }; img.onerror = function() { reject(new Error(`Image load failed: ${url}`)); }; img.src = url; }); } function getComposedPath(event) { if (event.composedPath) return event.composedPath(); const path = []; let node = event.target; while (node) { path.push(node); node = node.parentNode; } if (!path.includes(window) && window !== void 0) { path.push(window); } return path; } function getSafeAreaInsets() { const div = document.createElement("div"); div.style.cssText = "position:fixed;top:env(safe-area-inset-top,0px);right:env(safe-area-inset-right,0px);bottom:env(safe-area-inset-bottom,0px);left:env(safe-area-inset-left,0px);pointer-events:none;visibility:hidden;"; document.body.appendChild(div); const style2 = getComputedStyle(div); const insets = { top: Number.parseFloat(style2.top) || 0, right: Number.parseFloat(style2.right) || 0, bottom: Number.parseFloat(style2.bottom) || 0, left: Number.parseFloat(style2.left) || 0 }; div.remove(); return insets; } class ArtPlayerError extends Error { constructor(message, context) { super(message); if (typeof Error.captureStackTrace === "function") { Error.captureStackTrace(this, context || this.constructor); } this.name = "ArtPlayerError"; } } function errorHandle(condition, msg) { if (!condition) { throw new ArtPlayerError(msg); } return condition; } function silencePromise(value) { if (value && typeof value.catch === "function") { return value.catch(() => { }); } return value; } function getExt(url) { if (url.includes("?")) { return getExt(url.split("?")[0]); } if (url.includes("#")) { return getExt(url.split("#")[0]); } return url.trim().toLowerCase().split(".").pop(); } function download(url, name) { const elink = document.createElement("a"); elink.style.display = "none"; elink.href = url; elink.download = name; document.body.appendChild(elink); elink.click(); document.body.removeChild(elink); } function clamp(num, a2, b2) { return Math.max(Math.min(num, Math.max(a2, b2)), Math.min(a2, b2)); } function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } function secondToTime(second) { if (!second) return "00:00"; const add0 = (num) => num < 10 ? `0${num}` : String(num); const hour = Math.floor(second / 3600); const min = Math.floor((second - hour * 3600) / 60); const sec = Math.floor(second - hour * 3600 - min * 60); return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(":"); } function escape(str) { return str.replace( /[&<>'"]/g, (tag) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ })[tag] || tag ); } function unescape(str) { const map = { "&": "&", "<": "<", ">": ">", "'": "'", """: '"' }; const reg = new RegExp(`(${Object.keys(map).join("|")})`, "g"); return str.replace(reg, (tag) => map[tag] || tag); } const def = Object.defineProperty; const { hasOwnProperty } = Object.prototype; function has(obj, name) { return hasOwnProperty.call(obj, name); } function get(obj, name) { return Object.getOwnPropertyDescriptor(obj, name); } function mergeDeep(...objects) { const isObject = (item) => item && typeof item === "object" && !Array.isArray(item); return objects.reduce((prev, obj) => { Object.keys(obj).forEach((key) => { const pVal = prev[key]; const oVal = obj[key]; if (Array.isArray(pVal) && Array.isArray(oVal)) { prev[key] = pVal.concat(...oVal); } else if (isObject(pVal) && isObject(oVal)) { prev[key] = mergeDeep(pVal, oVal); } else { prev[key] = oVal; } }); return prev; }, {}); } function fixSrt(srt) { return srt.replace(/(\d\d:\d\d:\d\d)[,.](\d+)/g, (_, $1, $2) => { let ms = $2.slice(0, 3); if ($2.length === 1) { ms = `${$2}00`; } if ($2.length === 2) { ms = `${$2}0`; } return `${$1},${ms}`; }); } function srtToVtt(srtText) { return "WEBVTT \r\n\r\n".concat( fixSrt(srtText).replace(/\{\\([ibu])\}/g, "").replace(/\{\\([ibu])1\}/g, "<$1>").replace(/\{([ibu])\}/g, "<$1>").replace(/\{\/([ibu])\}/g, "").replace(/(\d\d:\d\d:\d\d),(\d\d\d)/g, "$1.$2").replace(/\{[\s\S]*?\}/g, "").concat("\r\n\r\n") ); } function vttToBlob(vttText) { return URL.createObjectURL( new Blob([vttText], { type: "text/vtt" }) ); } function assToVtt(ass) { const reAss = new RegExp( "Dialogue:\\s\\d,(\\d+:\\d\\d:\\d\\d.\\d\\d),(\\d+:\\d\\d:\\d\\d.\\d\\d),([^,]*),([^,]*),(?:[^,]*,){4}([\\s\\S]*)$", "i" ); function fixTime(time2 = "") { return time2.split(/[:.]/).map((item, index, arr) => { if (index === arr.length - 1) { if (item.length === 1) { return `.${item}00`; } if (item.length === 2) { return `.${item}0`; } } else if (item.length === 1) { return (index === 0 ? "0" : ":0") + item; } return index === 0 ? item : index === arr.length - 1 ? `.${item}` : `:${item}`; }).join(""); } return `WEBVTT ${ass.split(/\r?\n/).map((line) => { const m = line.match(reAss); if (!m) return null; return { start: fixTime(m[1].trim()), end: fixTime(m[2].trim()), text: m[5].replace(/\{[\s\S]*?\}/g, "").replace(/(\\N)/g, "\n").trim().split(/\r?\n/).map((item) => item.trim()).join("\n") }; }).filter((line) => line).map((line, index) => { if (line) { return `${index + 1} ${line.start} --> ${line.end} ${line.text}`; } return ""; }).filter((line) => line.trim()).join("\n\n")}`; } function sleep(ms = 0) { return new Promise((resolve) => setTimeout(resolve, ms)); } function debounce(func, duration) { let timeout; return function(...args) { const effect = () => { timeout = null; return func.apply(this, args); }; clearTimeout(timeout); timeout = setTimeout(effect, duration); }; } function throttle(func, duration) { let shouldWait = false; return function(...args) { if (!shouldWait) { func.apply(this, args); shouldWait = true; setTimeout(() => { shouldWait = false; }, duration); } }; } const utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, ArtPlayerError, addClass, append, assToVtt, capitalize, clamp, createElement, debounce, def, download, errorHandle, escape, get, getComposedPath, getExt, getIcon, getRect, getSafeAreaInsets, getStyle, has, hasClass, includeFromEvent, inverseClass, isBrowser, isIOS, isIOS13, isInViewport, isMobile, isSafari, loadImg, mergeDeep, query, queryAll, remove, removeClass, replaceElement, secondToTime, setStyle, setStyleText, setStyles, siblings, silencePromise, sleep, srtToVtt, supportsFlex, throttle, tooltip, unescape, userAgent, vttToBlob }, Symbol.toStringTag, { value: "Module" })); const a = "array"; const b = "boolean"; const s = "string"; const n = "number"; const o = "object"; const f = "function"; function validElement(value, type, paths) { return errorHandle( type === s || type === n || value instanceof Element, `${paths.join(".")} require '${s}' or 'Element' type` ); } const ComponentOption = { html: validElement, disable: `?${b}`, name: `?${s}`, index: `?${n}`, style: `?${o}`, click: `?${f}`, mounted: `?${f}`, tooltip: `?${s}|${n}`, width: `?${n}`, selector: `?${a}`, onSelect: `?${f}`, switch: `?${b}`, onSwitch: `?${f}`, range: `?${a}`, onRange: `?${f}`, onChange: `?${f}` }; const scheme = { id: s, container: validElement, url: s, poster: s, type: s, theme: s, lang: s, volume: n, isLive: b, muted: b, autoplay: b, autoSize: b, autoMini: b, loop: b, flip: b, playbackRate: b, aspectRatio: b, screenshot: b, setting: b, hotkey: b, pip: b, mutex: b, backdrop: b, fullscreen: b, fullscreenWeb: b, subtitleOffset: b, miniProgressBar: b, useSSR: b, playsInline: b, lock: b, gesture: b, fastForward: b, autoPlayback: b, autoOrientation: b, airplay: b, proxy: `?${f}`, plugins: [f], layers: [ComponentOption], contextmenu: [ComponentOption], settings: [ComponentOption], controls: [ { ...ComponentOption, position: (value, _, paths) => { const position = ["top", "left", "right"]; return errorHandle( position.includes(value), `${paths.join(".")} only accept ${position.toString()} as parameters` ); } } ], quality: [ { default: `?${b}`, html: s, url: s } ], highlight: [ { time: n, text: s } ], thumbnails: { url: s, number: n, column: n, width: n, height: n, scale: n }, subtitle: { url: s, name: s, type: s, style: o, escape: b, encoding: s, onVttLoad: f }, moreVideoAttr: o, i18n: o, icons: o, cssVar: o, customType: o }; class Component { constructor(art) { this.id = 0; this.art = art; this.cache = /* @__PURE__ */ new Map(); this.add = this.add.bind(this); this.remove = this.remove.bind(this); this.update = this.update.bind(this); } get show() { return hasClass(this.art.template.$player, `art-${this.name}-show`); } set show(value) { const { $player } = this.art.template; const className = `art-${this.name}-show`; if (value) { addClass($player, className); } else { removeClass($player, className); } this.art.emit(this.name, value); } toggle() { this.show = !this.show; } add(getOption) { const option = typeof getOption === "function" ? getOption(this.art) : getOption; option.html = option.html || ""; validator(option, ComponentOption); if (!this.$parent || !this.name || option.disable) return; const name = option.name || `${this.name}${this.id}`; errorHandle(!this.cache.has(name), `Can't add an existing [${name}] to the [${this.name}]`); this.id += 1; const $ref = createElement("div"); addClass($ref, `art-${this.name}`); addClass($ref, `art-${this.name}-${name}`); const childs = Array.from(this.$parent.children); $ref.dataset.index = option.index || this.id; const nextChild = childs.find((item) => Number(item.dataset.index) >= Number($ref.dataset.index)); if (nextChild) { nextChild.insertAdjacentElement("beforebegin", $ref); } else { append(this.$parent, $ref); } if (option.html) { append($ref, option.html); } if (option.style) { setStyles($ref, option.style); } if (option.tooltip) { tooltip($ref, option.tooltip); } const events = []; if (option.click) { const destroyEvent = this.art.events.proxy($ref, "click", (event) => { event.preventDefault(); option.click.call(this.art, this, event); }); events.push(destroyEvent); } if (option.selector && ["left", "right"].includes(option.position)) { this.selector(option, $ref, events); } this[name] = $ref; this.cache.set(name, { $ref, events, option }); if (option.mounted) { option.mounted.call(this.art, $ref); } return $ref; } remove(name) { errorHandle(this.cache.has(name), `Can't find [${name}] from the [${this.name}]`); const item = this.cache.get(name); if (item.option.beforeUnmount) { item.option.beforeUnmount.call(this.art, item.$ref); } for (const event of item.events) { this.art.events.remove(event); } this.cache.delete(name); delete this[name]; remove(item.$ref); } update(option) { if (this.cache.has(option.name)) { const item = this.cache.get(option.name); option = Object.assign(item.option, option); this.remove(option.name); } return this.add(option); } } function aspectRatio$2(option) { return (art) => { const { i18n, constructor: { ASPECT_RATIO } } = art; const html = ASPECT_RATIO.map( (item) => `${item === "default" ? i18n.get("Default") : item}` ).join(""); return { ...option, html: `${i18n.get("Aspect Ratio")}: ${html}`, click: (contextmenu, event) => { const { value } = event.target.dataset; if (value) { art.aspectRatio = value; contextmenu.show = false; } }, mounted: ($panel) => { const $default = query('[data-value="default"]', $panel); if ($default) { inverseClass($default, "art-current"); } art.on("aspectRatio", (value) => { const $current = queryAll("span", $panel).find((item) => item.dataset.value === value); if ($current) { inverseClass($current, "art-current"); } }); } }; }; } function close$1(option) { return (art) => ({ ...option, html: art.i18n.get("Close"), click: (contextmenu) => { contextmenu.show = false; } }); } function flip$2(option) { return (art) => { const { i18n, constructor: { FLIP } } = art; const html = FLIP.map((item) => `${i18n.get(capitalize(item))}`).join(""); return { ...option, html: `${i18n.get("Video Flip")}: ${html}`, click: (contextmenu, event) => { const { value } = event.target.dataset; if (value) { art.flip = value.toLowerCase(); contextmenu.show = false; } }, mounted: ($panel) => { const $default = query('[data-value="normal"]', $panel); if ($default) { inverseClass($default, "art-current"); } art.on("flip", (value) => { const $current = queryAll("span", $panel).find((item) => item.dataset.value === value); if ($current) { inverseClass($current, "art-current"); } }); } }; }; } function info(option) { return (art) => ({ ...option, html: art.i18n.get("Video Info"), click: (contextmenu) => { art.info.show = true; contextmenu.show = false; } }); } function playbackRate$2(option) { return (art) => { const { i18n, constructor: { PLAYBACK_RATE } } = art; const html = PLAYBACK_RATE.map( (item) => `${item === 1 ? i18n.get("Normal") : item.toFixed(1)}` ).join(""); return { ...option, html: `${i18n.get("Play Speed")}: ${html}`, click: (contextmenu, event) => { const { value } = event.target.dataset; if (value) { art.playbackRate = Number(value); contextmenu.show = false; } }, mounted: ($panel) => { const $default = query('[data-value="1"]', $panel); if ($default) inverseClass($default, "art-current"); art.on("video:ratechange", () => { const $current = queryAll("span", $panel).find( (item) => Number(item.dataset.value) === art.playbackRate ); if ($current) { inverseClass($current, "art-current"); } }); } }; }; } function version(option) { const url = isBrowser ? location.href : ""; return { ...option, html: `ArtPlayer ${version$1}` }; } class Contextmenu extends Component { constructor(art) { super(art); this.name = "contextmenu"; this.$parent = art.template.$contextmenu; if (!isMobile) { this.init(); } } init() { const { option, proxy, template: { $player, $contextmenu } } = this.art; if (option.playbackRate) { this.add( playbackRate$2({ name: "playbackRate", index: 10 }) ); } if (option.aspectRatio) { this.add( aspectRatio$2({ name: "aspectRatio", index: 20 }) ); } if (option.flip) { this.add( flip$2({ name: "flip", index: 30 }) ); } this.add( info({ name: "info", index: 40 }) ); this.add( version({ name: "version", index: 50 }) ); this.add( close$1({ name: "close", index: 60 }) ); for (let index = 0; index < option.contextmenu.length; index++) { this.add(option.contextmenu[index]); } proxy($player, "contextmenu", (event) => { if (!this.art.constructor.CONTEXTMENU) return; event.preventDefault(); this.show = true; const mouseX = event.clientX; const mouseY = event.clientY; const { height: cHeight, width: cWidth, left: cLeft, top: cTop } = getRect($player); const { height: mHeight, width: mWidth } = getRect($contextmenu); let menuLeft = mouseX - cLeft; let menuTop = mouseY - cTop; if (mouseX + mWidth > cLeft + cWidth) { menuLeft = cWidth - mWidth; } if (mouseY + mHeight > cTop + cHeight) { menuTop = cHeight - mHeight; } setStyles($contextmenu, { top: `${menuTop}px`, left: `${menuLeft}px` }); }); proxy($player, "click", (event) => { if (!includeFromEvent(event, $contextmenu)) { this.show = false; } }); this.art.on("blur", () => { this.show = false; }); } } function airplay$1(option) { return (art) => ({ ...option, tooltip: art.i18n.get("AirPlay"), mounted: ($control) => { const { proxy, icons } = art; append($control, icons.airplay); proxy($control, "click", () => art.airplay()); } }); } function fullscreen(option) { return (art) => ({ ...option, tooltip: art.i18n.get("Fullscreen"), mounted: ($control) => { const { proxy, icons, i18n } = art; const $fullscreenOn = append($control, icons.fullscreenOn); const $fullscreenOff = append($control, icons.fullscreenOff); setStyle($fullscreenOff, "display", "none"); proxy($control, "click", () => { art.fullscreen = !art.fullscreen; }); art.on("fullscreen", (state2) => { if (state2) { tooltip($control, i18n.get("Exit Fullscreen")); setStyle($fullscreenOn, "display", "none"); setStyle($fullscreenOff, "display", "inline-flex"); } else { tooltip($control, i18n.get("Fullscreen")); setStyle($fullscreenOn, "display", "inline-flex"); setStyle($fullscreenOff, "display", "none"); } }); } }); } function fullscreenWeb(option) { return (art) => ({ ...option, tooltip: art.i18n.get("Web Fullscreen"), mounted: ($control) => { const { proxy, icons, i18n } = art; const $fullscreenWebOn = append($control, icons.fullscreenWebOn); const $fullscreenWebOff = append($control, icons.fullscreenWebOff); setStyle($fullscreenWebOff, "display", "none"); proxy($control, "click", () => { art.fullscreenWeb = !art.fullscreenWeb; }); art.on("fullscreenWeb", (value) => { if (value) { tooltip($control, i18n.get("Exit Web Fullscreen")); setStyle($fullscreenWebOn, "display", "none"); setStyle($fullscreenWebOff, "display", "inline-flex"); } else { tooltip($control, i18n.get("Web Fullscreen")); setStyle($fullscreenWebOn, "display", "inline-flex"); setStyle($fullscreenWebOff, "display", "none"); } }); } }); } function pip$1(option) { return (art) => ({ ...option, tooltip: art.i18n.get("PIP Mode"), mounted: ($control) => { const { proxy, icons, i18n } = art; append($control, icons.pip); proxy($control, "click", () => { art.pip = !art.pip; }); art.on("pip", (value) => { tooltip($control, i18n.get(value ? "Exit PIP Mode" : "PIP Mode")); }); } }); } function playAndPause(option) { return (art) => ({ ...option, mounted: ($control) => { const { proxy, icons, i18n } = art; const $play = append($control, icons.play); const $pause = append($control, icons.pause); tooltip($play, i18n.get("Play")); tooltip($pause, i18n.get("Pause")); proxy($play, "click", () => { silencePromise(art.play()); }); proxy($pause, "click", () => { art.pause(); }); function showPlay() { setStyle($play, "display", "flex"); setStyle($pause, "display", "none"); } function showPause() { setStyle($play, "display", "none"); setStyle($pause, "display", "flex"); } if (art.playing) { showPause(); } else { showPlay(); } art.on("video:playing", () => { showPause(); }); art.on("video:pause", () => { showPlay(); }); } }); } function getPosFromEvent(art, event) { const { $progress } = art.template; const { left } = getRect($progress); const eventLeft = isMobile ? event.touches[0].clientX : event.clientX; const width = clamp(eventLeft - left, 0, $progress.clientWidth); const second = width / $progress.clientWidth * art.duration; const time2 = secondToTime(second); const percentage = clamp(width / $progress.clientWidth, 0, 1); return { second, time: time2, width, percentage }; } function setCurrentTime(art, event) { if (art.isRotate) { const percentage = (event.touches[0].clientY - art.top) / art.height; const second = percentage * art.duration; art.emit("setBar", "played", percentage, event); art.seek = second; } else { const { second, percentage } = getPosFromEvent(art, event); art.emit("setBar", "played", percentage, event); art.seek = second; } } function progress(options) { return (art) => { const { icons, option, proxy } = art; const { $player, $progress } = art.template; return { ...options, html: `
00:00
`, mounted: ($control) => { let tipTimer = null; let isDragging = false; const $hover = query(".art-progress-hover", $control); const $loaded = query(".art-progress-loaded", $control); const $played = query(".art-progress-played", $control); const $highlight = query(".art-progress-highlight", $control); const $indicator = query(".art-progress-indicator", $control); const $tip = query(".art-progress-tip", $control); if (icons.indicator) { append($indicator, icons.indicator); } else { setStyle($indicator, "backgroundColor", "var(--art-theme)"); } function showHighlight(event) { const { width } = getPosFromEvent(art, event); const { text } = event.target.dataset; $tip.textContent = text; const tipWidth = $tip.clientWidth; if (width <= tipWidth / 2) { setStyle($tip, "left", 0); } else if (width > $control.clientWidth - tipWidth / 2) { setStyle($tip, "left", `${$control.clientWidth - tipWidth}px`); } else { setStyle($tip, "left", `${width - tipWidth / 2}px`); } } function showTime(event, touch) { const { width, time: time2 } = touch || getPosFromEvent(art, event); $tip.textContent = time2 || "00:00"; const tipWidth = $tip.clientWidth; if (width <= tipWidth / 2) { setStyle($tip, "left", 0); } else if (width > $control.clientWidth - tipWidth / 2) { setStyle($tip, "left", `${$control.clientWidth - tipWidth}px`); } else { setStyle($tip, "left", `${width - tipWidth / 2}px`); } } function updateHighlight() { $highlight.textContent = ""; for (let index = 0; index < option.highlight.length; index++) { const item = option.highlight[index]; const left = clamp(item.time, 0, art.duration) / art.duration * 100; const html = ``; append($highlight, html); } } function setBar(type, percentage, event) { const isMobileDragging = type === "played" && event && isMobile; if (type === "loaded") { setStyle($loaded, "width", `${percentage * 100}%`); } if (type === "hover") { setStyle($hover, "width", `${percentage * 100}%`); if (includeFromEvent(event, $highlight)) { showHighlight(event); } else { showTime(event); } if (percentage === 0) { removeClass($player, "art-progress-hover"); } else { addClass($player, "art-progress-hover"); } } if (type === "played") { setStyle($played, "width", `${percentage * 100}%`); setStyle($indicator, "left", `${percentage * 100}%`); } if (isMobileDragging) { addClass($player, "art-progress-hover"); const width = $control.clientWidth * percentage; const time2 = secondToTime(percentage * art.duration); showTime(event, { width, time: time2 }); clearTimeout(tipTimer); tipTimer = setTimeout(() => { removeClass($player, "art-progress-hover"); }, 500); } } art.on("setBar", setBar); art.on("video:loadedmetadata", updateHighlight); if (art.constructor.USE_RAF) { art.on("raf", () => { art.emit("setBar", "played", art.played); art.emit("setBar", "loaded", art.loaded); }); } else { art.on("video:timeupdate", () => { art.emit("setBar", "played", art.played); }); art.on("video:progress", () => { art.emit("setBar", "loaded", art.loaded); }); art.on("video:ended", () => { art.emit("setBar", "played", 1); }); } art.emit("setBar", "loaded", art.loaded || 0); if (!isMobile) { proxy($progress, "click", (event) => { if (event.target !== $indicator) { setCurrentTime(art, event); } }); proxy($progress, "mousemove", (event) => { const { percentage } = getPosFromEvent(art, event); art.emit("setBar", "hover", percentage, event); }); proxy($progress, "mouseleave", (event) => { art.emit("setBar", "hover", 0, event); }); proxy($progress, "mousedown", (event) => { isDragging = event.button === 0; }); art.on("document:mousemove", (event) => { if (isDragging) { const { second, percentage } = getPosFromEvent(art, event); art.emit("setBar", "played", percentage, event); art.seek = second; } }); art.on("document:mouseup", () => { if (isDragging) { isDragging = false; } }); } } }; }; } function screenshot$1(option) { return (art) => ({ ...option, tooltip: art.i18n.get("Screenshot"), mounted: ($control) => { const { proxy, icons } = art; append($control, icons.screenshot); proxy($control, "click", () => { art.screenshot(); }); } }); } function setting$1(option) { return (art) => ({ ...option, tooltip: art.i18n.get("Show Setting"), mounted: ($control) => { const { proxy, icons, i18n } = art; append($control, icons.setting); proxy($control, "click", () => { art.setting.toggle(); art.setting.resize(); }); art.on("setting", (value) => { tooltip($control, i18n.get(value ? "Hide Setting" : "Show Setting")); }); } }); } function time(option) { return (art) => ({ ...option, style: isMobile ? { fontSize: "12px", padding: "0 5px" } : { cursor: "auto", padding: "0 10px" }, mounted: ($control) => { function getTime() { const newTime = `${secondToTime(art.currentTime)} / ${secondToTime(art.duration)}`; if (newTime !== $control.textContent) { $control.textContent = newTime; } } getTime(); const events = ["video:loadedmetadata", "video:timeupdate", "video:progress"]; for (let index = 0; index < events.length; index++) { art.on(events[index], getTime); } } }); } function volume$1(option) { return (art) => ({ ...option, mounted: ($control) => { const { proxy, icons } = art; const $volume = append($control, icons.volume); const $close = append($control, icons.volumeClose); const $panel = append($control, '
'); const $inner = append($panel, '
'); const $value = append($inner, `
`); const $slider = append($inner, `
`); const $handle = append($slider, `
`); const $loaded = append($handle, `
`); const $indicator = append($slider, `
`); function getVolumeFromEvent(event) { const { top, height } = getRect($slider); return 1 - (event.clientY - top) / height; } function update() { if (art.muted || art.volume === 0) { setStyle($volume, "display", "none"); setStyle($close, "display", "flex"); setStyle($indicator, "top", "100%"); setStyle($loaded, "top", "100%"); $value.textContent = 0; } else { const percentage = art.volume * 100; setStyle($volume, "display", "flex"); setStyle($close, "display", "none"); setStyle($indicator, "top", `${100 - percentage}%`); setStyle($loaded, "top", `${100 - percentage}%`); $value.textContent = Math.floor(percentage); } } update(); art.on("video:volumechange", update); proxy($volume, "click", () => { art.muted = true; }); proxy($close, "click", () => { art.muted = false; }); if (isMobile) { setStyle($panel, "display", "none"); } else { let isDragging = false; proxy($slider, "mousedown", (event) => { isDragging = event.button === 0; art.volume = getVolumeFromEvent(event); }); art.on("document:mousemove", (event) => { if (isDragging) { art.muted = false; art.volume = getVolumeFromEvent(event); } }); art.on("document:mouseup", () => { if (isDragging) { isDragging = false; } }); } } }); } class Control extends Component { constructor(art) { super(art); this.isHover = false; this.name = "control"; this.timer = Date.now(); const { constructor } = art; const { $player, $bottom } = this.art.template; art.on("mousemove", () => { if (!isMobile) { this.show = true; } }); art.on("click", () => { if (isMobile) { this.toggle(); } else { this.show = true; } }); art.on("document:mousemove", (event) => { this.isHover = includeFromEvent(event, $bottom); }); art.on("video:timeupdate", () => { if (!art.setting.show && !this.isHover && !art.isInput && art.playing && this.show && Date.now() - this.timer >= constructor.CONTROL_HIDE_TIME) { this.show = false; } }); art.on("control", (state2) => { if (state2) { removeClass($player, "art-hide-cursor"); addClass($player, "art-hover"); this.timer = Date.now(); } else { addClass($player, "art-hide-cursor"); removeClass($player, "art-hover"); } }); this.init(); } init() { const { option } = this.art; if (!option.isLive) { this.add( progress({ name: "progress", position: "top", index: 10 }) ); } this.add({ name: "thumbnails", position: "top", index: 20 }); this.add( playAndPause({ name: "playAndPause", position: "left", index: 10 }) ); this.add( volume$1({ name: "volume", position: "left", index: 20 }) ); if (!option.isLive) { this.add( time({ name: "time", position: "left", index: 30 }) ); } if (option.quality.length) { sleep().then(() => { this.art.quality = option.quality; }); } if (option.screenshot && !isMobile) { this.add( screenshot$1({ name: "screenshot", position: "right", index: 20 }) ); } if (option.setting) { this.add( setting$1({ name: "setting", position: "right", index: 30 }) ); } if (option.pip) { this.add( pip$1({ name: "pip", position: "right", index: 40 }) ); } if (option.airplay && window.WebKitPlaybackTargetAvailabilityEvent) { this.add( airplay$1({ name: "airplay", position: "right", index: 50 }) ); } if (option.fullscreenWeb) { this.add( fullscreenWeb({ name: "fullscreenWeb", position: "right", index: 60 }) ); } if (option.fullscreen) { this.add( fullscreen({ name: "fullscreen", position: "right", index: 70 }) ); } for (let index = 0; index < option.controls.length; index++) { this.add(option.controls[index]); } } add(getOption) { const option = typeof getOption === "function" ? getOption(this.art) : getOption; const { $progress, $controlsLeft, $controlsRight } = this.art.template; switch (option.position) { case "top": this.$parent = $progress; break; case "left": this.$parent = $controlsLeft; break; case "right": this.$parent = $controlsRight; break; default: errorHandle(false, `Control option.position must one of 'top', 'left', 'right'`); break; } super.add(option); } check(target) { if (!target) { return; } target.$control_value.innerHTML = target.html; for (let index = 0; index < target.$control_option.length; index++) { const item = target.$control_option[index]; item.default = item === target; if (item.default) { inverseClass(item.$control_item, "art-current"); } } } selector(option, $ref, events) { const { proxy } = this.art.events; addClass($ref, "art-control-selector"); const $value = createElement("div"); addClass($value, "art-selector-value"); append($value, option.html); $ref.textContent = ""; append($ref, $value); const $list = createElement("div"); addClass($list, "art-selector-list"); append($ref, $list); for (let index = 0; index < option.selector.length; index++) { const item = option.selector[index]; const $item = createElement("div"); addClass($item, "art-selector-item"); if (item.default) addClass($item, "art-current"); $item.dataset.index = index; $item.dataset.value = item.value; $item.innerHTML = item.html; append($list, $item); def(item, "$control_option", { get: () => option.selector }); def(item, "$control_item", { get: () => $item }); def(item, "$control_value", { get: () => $value }); } const event = proxy($list, "click", async (event2) => { const path = getComposedPath(event2); const item = option.selector.find( (item2) => item2.$control_item === path.find(($item) => item2.$control_item === $item) ); this.check(item); if (option.onSelect) { $value.innerHTML = await option.onSelect.call(this.art, item, item.$control_item, event2); } }); events.push(event); } } function clickInit(art, events) { const { constructor, template: { $player, $video } } = art; function onDocumentClick(event) { if (includeFromEvent(event, $player)) { art.isInput = event.target.tagName === "INPUT"; art.isFocus = true; art.emit("focus", event); } else { art.isInput = false; art.isFocus = false; art.emit("blur", event); } } art.on("document:click", onDocumentClick); art.on("document:contextmenu", onDocumentClick); let clickTimes = []; events.proxy($video, "click", (event) => { const now = Date.now(); clickTimes.push(now); const { MOBILE_CLICK_PLAY, DBCLICK_TIME, MOBILE_DBCLICK_PLAY, DBCLICK_FULLSCREEN } = constructor; const clicks = clickTimes.filter((t) => now - t <= DBCLICK_TIME); switch (clicks.length) { case 1: art.emit("click", event); if (isMobile) { if (!art.isLock && MOBILE_CLICK_PLAY) { silencePromise(art.toggle()); } } else { silencePromise(art.toggle()); } clickTimes = clicks; break; case 2: art.emit("dblclick", event); if (isMobile) { if (!art.isLock && MOBILE_DBCLICK_PLAY) { silencePromise(art.toggle()); } } else { if (DBCLICK_FULLSCREEN) { art.fullscreen = !art.fullscreen; } } clickTimes = []; break; default: clickTimes = []; } }); } function GetSlideAngle(dx, dy) { return Math.atan2(dy, dx) * 180 / Math.PI; } function GetSlideDirection(startX, startY, endX, endY) { const dy = startY - endY; const dx = endX - startX; let result = 0; if (Math.abs(dx) < 2 && Math.abs(dy) < 2) { return result; } const angle = GetSlideAngle(dx, dy); if (angle >= -45 && angle < 45) { result = 4; } else if (angle >= 45 && angle < 135) { result = 1; } else if (angle >= -135 && angle < -45) { result = 2; } else if (angle >= 135 && angle <= 180 || angle >= -180 && angle < -135) { result = 3; } return result; } function gestureInit(art, events) { if (isMobile && !art.option.isLive) { const { $video, $progress } = art.template; let touchTarget = null; let isDragging = false; let startX = 0; let startY = 0; let startTime = 0; const onTouchStart = (event) => { if (event.touches.length === 1 && !art.isLock) { if (touchTarget === $progress) { setCurrentTime(art, event); } isDragging = true; const { pageX, pageY } = event.touches[0]; startX = pageX; startY = pageY; startTime = art.currentTime; } }; const onTouchMove = (event) => { if (event.touches.length === 1 && isDragging && art.duration) { const { pageX, pageY } = event.touches[0]; const direction = GetSlideDirection(startX, startY, pageX, pageY); const isHorizontal = [3, 4].includes(direction); const isVertical = [1, 2].includes(direction); const isLegal = isHorizontal && !art.isRotate || isVertical && art.isRotate; if (isLegal) { const ratioX = clamp((pageX - startX) / art.width, -1, 1); const ratioY = clamp((pageY - startY) / art.height, -1, 1); const ratio = art.isRotate ? ratioY : ratioX; const TOUCH_MOVE_RATIO = touchTarget === $video ? art.constructor.TOUCH_MOVE_RATIO : 1; const currentTime = clamp(startTime + art.duration * ratio * TOUCH_MOVE_RATIO, 0, art.duration); art.seek = currentTime; art.emit("setBar", "played", clamp(currentTime / art.duration, 0, 1), event); art.notice.show = `${secondToTime(currentTime)} / ${secondToTime(art.duration)}`; } } }; const onTouchEnd = () => { if (isDragging) { startX = 0; startY = 0; startTime = 0; isDragging = false; touchTarget = null; } }; if (art.option.gesture) { events.proxy($video, "touchstart", (event) => { touchTarget = $video; onTouchStart(event); }); events.proxy($video, "touchmove", onTouchMove); } events.proxy($progress, "touchstart", (event) => { touchTarget = $progress; onTouchStart(event); }); events.proxy($progress, "touchmove", onTouchMove); art.on("document:touchend", onTouchEnd); } } function globalInit(art, events) { const documentEvents = [ "click", "mouseup", "keydown", "touchend", "touchmove", "mousemove", "pointerup", "contextmenu", "pointermove", "visibilitychange", "webkitfullscreenchange" ]; const windowEvents = [ "resize", "scroll", "orientationchange" ]; const destroyEvents = []; function bindGlobalEvents(source = {}) { for (let index = 0; index < destroyEvents.length; index++) { events.remove(destroyEvents[index]); } destroyEvents.length = 0; const { $player } = art.template; documentEvents.forEach((name) => { const doc = source.document || $player.ownerDocument || document; const destroy = events.proxy(doc, name, (event) => { art.emit(`document:${name}`, event); }); destroyEvents.push(destroy); }); windowEvents.forEach((name) => { const win = source.window || $player.ownerDocument?.defaultView || window; const destroy = events.proxy(win, name, (event) => { art.emit(`window:${name}`, event); }); destroyEvents.push(destroy); }); } bindGlobalEvents(); events.bindGlobalEvents = bindGlobalEvents; } function hoverInit(art, events) { const { $player } = art.template; events.hover( $player, (event) => { addClass($player, "art-hover"); art.emit("hover", true, event); }, (event) => { removeClass($player, "art-hover"); art.emit("hover", false, event); } ); } function moveInit(art, events) { const { $player } = art.template; events.proxy($player, "mousemove", (event) => { art.emit("mousemove", event); }); } function resizeInit(art, events) { const { option, constructor } = art; art.on("resize", () => { const { aspectRatio: aspectRatio2, notice } = art; if (art.state === "standard" && option.autoSize) { art.autoSize(); } art.aspectRatio = aspectRatio2; notice.show = ""; }); const resizeFn = debounce(() => art.emit("resize"), constructor.RESIZE_TIME); art.on("window:orientationchange", () => resizeFn()); art.on("window:resize", () => resizeFn()); if (screen && screen.orientation && screen.orientation.onchange) { events.proxy(screen.orientation, "change", () => resizeFn()); } } function updateInit(art) { if (art.constructor.USE_RAF) { let timer = null; (function update() { if (art.playing) { art.emit("raf"); } if (!art.isDestroy) { timer = requestAnimationFrame(update); } })(); art.on("destroy", () => { cancelAnimationFrame(timer); }); } } function viewInit(art) { const { option, constructor, template: { $container } } = art; const scrollFn = throttle(() => { art.emit("view", isInViewport($container, constructor.SCROLL_GAP)); }, constructor.SCROLL_TIME); art.on("window:scroll", () => scrollFn()); art.on("view", (state2) => { if (option.autoMini) { art.mini = !state2; } }); } class Events { constructor(art) { this.destroyEvents = /* @__PURE__ */ new Set(); this.proxy = this.proxy.bind(this); this.hover = this.hover.bind(this); clickInit(art, this); hoverInit(art, this); moveInit(art, this); resizeInit(art, this); gestureInit(art, this); viewInit(art); globalInit(art, this); updateInit(art); } proxy(target, name, callback, option = {}) { if (Array.isArray(name)) { return name.map((item) => this.proxy(target, item, callback, option)); } target.addEventListener(name, callback, option); const destroy = () => target.removeEventListener(name, callback, option); this.destroyEvents.add(destroy); return destroy; } hover(target, mouseenter, mouseleave) { if (mouseenter) { this.proxy(target, "mouseenter", mouseenter); } if (mouseleave) { this.proxy(target, "mouseleave", mouseleave); } } remove(destroyEvent) { if (this.destroyEvents.has(destroyEvent)) { try { destroyEvent(); } catch (error2) { console.warn("Failed to remove event listener:", error2); } finally { this.destroyEvents.delete(destroyEvent); } } } destroy() { for (const destroyEvent of this.destroyEvents) { try { destroyEvent(); } catch (error2) { console.warn("Failed to destroy event listener:", error2); } } this.destroyEvents.clear(); } } class Hotkey { constructor(art) { this.art = art; this.keys = {}; if (!isMobile) { this.init(); } } init() { const { constructor } = this.art; if (this.art.option.hotkey) { this.add("Escape", () => { if (this.art.fullscreenWeb) { this.art.fullscreenWeb = false; } }); this.add("Space", () => { silencePromise(this.art.toggle()); }); this.add("ArrowLeft", () => { this.art.backward = constructor.SEEK_STEP; }); this.add("ArrowUp", () => { this.art.volume += constructor.VOLUME_STEP; }); this.add("ArrowRight", () => { this.art.forward = constructor.SEEK_STEP; }); this.add("ArrowDown", () => { this.art.volume -= constructor.VOLUME_STEP; }); } this.art.on("document:keydown", (event) => { if (this.art.isFocus) { const tag = document.activeElement.tagName.toUpperCase(); const editable = document.activeElement.getAttribute("contenteditable"); if (tag !== "INPUT" && tag !== "TEXTAREA" && editable !== "" && editable !== "true" && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { const events = this.keys[event.code]; if (events) { event.preventDefault(); for (let index = 0; index < events.length; index++) { events[index].call(this.art, event); } this.art.emit("hotkey", event); } } } this.art.emit("keydown", event); }); } add(key, event) { if (this.keys[key]) { if (!this.keys[key].includes(event)) { this.keys[key].push(event); } } else { this.keys[key] = [event]; } return this; } remove(key, event) { if (this.keys[key]) { const index = this.keys[key].indexOf(event); if (index !== -1) { this.keys[key].splice(index, 1); } if (this.keys[key].length === 0) { delete this.keys[key]; } } return this; } } const zhCn = { "Video Info": "统计信息", "Close": "关闭", "Video Load Failed": "加载失败", "Volume": "音量", "Play": "播放", "Pause": "暂停", "Rate": "速度", "Mute": "静音", "Video Flip": "画面翻转", "Horizontal": "水平", "Vertical": "垂直", "Reconnect": "重新连接", "Show Setting": "显示设置", "Hide Setting": "隐藏设置", "Screenshot": "截图", "Play Speed": "播放速度", "Aspect Ratio": "画面比例", "Default": "默认", "Normal": "正常", "Open": "打开", "Switch Video": "切换", "Switch Subtitle": "切换字幕", "Fullscreen": "全屏", "Exit Fullscreen": "退出全屏", "Web Fullscreen": "网页全屏", "Exit Web Fullscreen": "退出网页全屏", "Mini Player": "迷你播放器", "PIP Mode": "开启画中画", "Exit PIP Mode": "退出画中画", "PIP Not Supported": "不支持画中画", "Fullscreen Not Supported": "不支持全屏", "Subtitle Offset": "字幕偏移", "Last Seen": "上次看到", "Jump Play": "跳转播放", "AirPlay": "隔空播放", "AirPlay Not Available": "隔空播放不可用" }; if (typeof window !== "undefined") { window["artplayer-i18n-zh-cn"] = zhCn; } class I18n { constructor(art) { this.art = art; this.languages = { "zh-cn": zhCn }; this.language = {}; this.update(art.option.i18n); } init() { const lang = this.art.option.lang.toLowerCase(); this.language = this.languages[lang] || {}; } get(key) { return this.language[key] || key; } update(value) { this.languages = mergeDeep(this.languages, value); this.init(); } } const airplay = '\n \n \n \n \n\n'; const arrowLeft = '\n \n'; const arrowRight = '\n \n'; const aspectRatio$1 = ''; const check = '\n\n'; const close = '\n\n\n\n\n'; const config = ''; const error = '\n\n\n\n\n'; const flip$1 = '\n\n\n\n\n\n'; const fullscreenOff = '\n\n\n\n\n'; const fullscreenOn = '\n\n\n\n\n'; const fullscreenWebOff = '\n\n\n\n\n'; const fullscreenWebOn = '\n\n\n\n\n'; const loading = '\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n'; const lock$1 = '\n\n\n\n\n'; const pause = '\n \n'; const pip = '\n\n'; const play = '\n \n'; const playbackRate$1 = ''; const screenshot = '\n \n\n'; const setting = '\n \n \n'; const state = '\n\n\n'; const switchOff = '\n\n\n \n\n'; const switchOn = '\n\n\n \n'; const unlock = '\n\n\n'; const volumeClose = '\n \n \n'; const volume = '\n \n \n'; class Icons { constructor(art) { const icons = { loading, state, play, pause, check, volume, volumeClose, screenshot, setting, pip, arrowLeft, arrowRight, playbackRate: playbackRate$1, aspectRatio: aspectRatio$1, config, lock: lock$1, flip: flip$1, unlock, fullscreenOff, fullscreenOn, fullscreenWebOff, fullscreenWebOn, switchOn, switchOff, error, close, airplay, ...art.option.icons }; for (const key in icons) { def(this, key, { get: () => getIcon(key, icons[key]) }); } } } class Info extends Component { constructor(art) { super(art); this.name = "info"; if (!isMobile) { this.init(); } } init() { const { proxy, constructor, template: { $infoPanel, $infoClose, $video } } = this.art; proxy($infoClose, "click", () => { this.show = false; }); let timer = null; const $types = queryAll("[data-video]", $infoPanel) || []; this.art.on("destroy", () => clearTimeout(timer)); function loop() { for (let index = 0; index < $types.length; index++) { const item = $types[index]; const value = $video[item.dataset.video]; const textContent = typeof value === "number" ? value.toFixed(2) : value; if (item.textContent !== textContent) { item.textContent = textContent; } } timer = setTimeout(loop, constructor.INFO_LOOP_TIME); } loop(); } } class Layer extends Component { constructor(art) { super(art); const { option, template: { $layer } } = art; this.name = "layer"; this.$parent = $layer; for (let index = 0; index < option.layers.length; index++) { this.add(option.layers[index]); } } } class Loading extends Component { constructor(art) { super(art); this.name = "loading"; append(art.template.$loading, art.icons.loading); } } class Mask extends Component { constructor(art) { super(art); this.name = "mask"; const { template, icons, events } = art; const $state = append(template.$state, icons.state); const $error = append(template.$state, icons.error); setStyle($error, "display", "none"); art.on("destroy", () => { setStyle($state, "display", "none"); setStyle($error, "display", null); }); events.proxy(template.$state, "click", () => silencePromise(art.play())); } } class Notice { constructor(art) { this.art = art; this.timer = null; art.on("destroy", () => this.destroy()); } destroy() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } } set show(msg) { const { constructor, template: { $player, $noticeInner } } = this.art; if (msg) { $noticeInner.textContent = msg instanceof Error ? msg.message.trim() : msg; addClass($player, "art-notice-show"); clearTimeout(this.timer); this.timer = setTimeout(() => { $noticeInner.textContent = ""; removeClass($player, "art-notice-show"); }, constructor.NOTICE_TIME); } else { removeClass($player, "art-notice-show"); } } get show() { const { template: { $player } } = this.art; return $player.classList.contains("art-notice-show"); } } function airplayMix(art) { const { i18n, notice, proxy, template: { $video } } = art; let available = true; if (window.WebKitPlaybackTargetAvailabilityEvent && $video.webkitShowPlaybackTargetPicker) { proxy($video, "webkitplaybacktargetavailabilitychanged", (event) => { switch (event.availability) { case "available": available = true; break; case "not-available": available = false; break; } }); } else { available = false; } def(art, "airplay", { value() { if (available) { $video.webkitShowPlaybackTargetPicker(); art.emit("airplay"); } else { notice.show = i18n.get("AirPlay Not Available"); } } }); } function aspectRatioMix(art) { const { i18n, notice, template: { $video, $player } } = art; def(art, "aspectRatio", { get() { return $player.dataset.aspectRatio || "default"; }, set(ratio) { if (!ratio) ratio = "default"; if (ratio === "default") { setStyle($video, "width", null); setStyle($video, "height", null); setStyle($video, "margin", null); delete $player.dataset.aspectRatio; } else { const ratioArray = ratio.split(":").map(Number); const { clientWidth, clientHeight } = $player; const playerRatio = clientWidth / clientHeight; const setupRatio = ratioArray[0] / ratioArray[1]; if (playerRatio > setupRatio) { setStyle($video, "width", `${setupRatio * clientHeight}px`); setStyle($video, "height", "100%"); setStyle($video, "margin", "0 auto"); } else { setStyle($video, "width", "100%"); setStyle($video, "height", `${clientWidth / setupRatio}px`); setStyle($video, "margin", "auto 0"); } $player.dataset.aspectRatio = ratio; } notice.show = `${i18n.get("Aspect Ratio")}: ${ratio === "default" ? i18n.get("Default") : ratio}`; art.emit("aspectRatio", ratio); } }); } function attrMix(art) { const { template: { $video } } = art; def(art, "attr", { value(key, value) { if (value === void 0) return $video[key]; $video[key] = value; } }); } function autoHeightMix(art) { const { template: { $container, $video } } = art; def(art, "autoHeight", { value() { const { clientWidth } = $container; const { videoHeight, videoWidth } = $video; const height = videoHeight * (clientWidth / videoWidth); setStyle($container, "height", `${height}px`); art.emit("autoHeight", height); } }); } function autoSizeMix(art) { const { $container, $player, $video } = art.template; def(art, "autoSize", { value() { const { videoWidth, videoHeight } = $video; const { width, height } = getRect($container); const videoRatio = videoWidth / videoHeight; const containerRatio = width / height; if (containerRatio > videoRatio) { const percentage = height * videoRatio / width * 100; setStyle($player, "width", `${percentage}%`); setStyle($player, "height", "100%"); } else { const percentage = width / videoRatio / height * 100; setStyle($player, "width", "100%"); setStyle($player, "height", `${percentage}%`); } art.emit("autoSize", { width: art.width, height: art.height }); } }); } function cssVarMix(art) { const { $player } = art.template; def(art, "cssVar", { value(key, value) { if (value) { return $player.style.setProperty(key, value); } else { return getComputedStyle($player).getPropertyValue(key); } } }); } function currentTimeMix(art) { const { $video } = art.template; def(art, "currentTime", { get: () => $video.currentTime || 0, set: (time2) => { time2 = Number.parseFloat(time2); if (Number.isNaN(time2)) return; $video.currentTime = clamp(time2, 0, art.duration); } }); } function durationMix(art) { def(art, "duration", { get: () => { const { duration } = art.template.$video; if (duration === Infinity) return 0; return duration || 0; } }); } function eventInit(art) { const { i18n, notice, option, constructor, proxy, template: { $player, $video, $poster } } = art; let reconnectTime = 0; for (let index = 0; index < config$1.events.length; index++) { proxy($video, config$1.events[index], (event) => { art.emit(`video:${event.type}`, event); }); } art.on("video:canplay", () => { reconnectTime = 0; art.loading.show = false; }); art.once("video:canplay", () => { art.loading.show = false; art.controls.show = true; art.mask.show = true; art.isReady = true; art.emit("ready"); }); art.on("video:ended", () => { if (option.loop) { art.seek = 0; silencePromise(art.play()); art.controls.show = false; art.mask.show = false; } else { art.controls.show = true; art.mask.show = true; } }); art.on("video:error", async (error2) => { if (reconnectTime < constructor.RECONNECT_TIME_MAX) { await sleep(constructor.RECONNECT_SLEEP_TIME); reconnectTime += 1; art.url = option.url; notice.show = `${i18n.get("Reconnect")}: ${reconnectTime}`; art.emit("error", error2, reconnectTime); } else { art.mask.show = true; art.loading.show = false; art.controls.show = true; addClass($player, "art-error"); await sleep(constructor.RECONNECT_SLEEP_TIME); notice.show = i18n.get("Video Load Failed"); } }); art.on("video:loadedmetadata", () => { art.emit("resize"); if (isMobile) { art.loading.show = false; art.controls.show = true; art.mask.show = true; } }); art.on("video:loadstart", () => { art.loading.show = true; art.mask.show = false; art.controls.show = true; }); art.on("video:pause", () => { art.controls.show = true; art.mask.show = true; }); art.on("video:play", () => { art.mask.show = false; setStyle($poster, "display", "none"); }); art.on("video:playing", () => { art.mask.show = false; }); art.on("video:progress", () => { if (art.playing) { art.loading.show = false; } }); art.on("video:seeked", () => { art.loading.show = false; art.mask.show = true; }); art.on("video:seeking", () => { art.loading.show = true; art.mask.show = false; }); art.on("video:timeupdate", () => { art.mask.show = false; }); art.on("video:waiting", () => { art.loading.show = true; art.mask.show = false; }); } function flipMix(art) { const { template: { $player }, i18n, notice } = art; def(art, "flip", { get() { return $player.dataset.flip || "normal"; }, set(flip2) { if (!flip2) flip2 = "normal"; if (flip2 === "normal") { delete $player.dataset.flip; } else { $player.dataset.flip = flip2; } notice.show = `${i18n.get("Video Flip")}: ${i18n.get(capitalize(flip2))}`; art.emit("flip", flip2); } }); } const methodMap = [ [ "requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror" ], // New WebKit [ "webkitRequestFullscreen", "webkitExitFullscreen", "webkitFullscreenElement", "webkitFullscreenEnabled", "webkitfullscreenchange", "webkitfullscreenerror" ], // Old WebKit [ "webkitRequestFullScreen", "webkitCancelFullScreen", "webkitCurrentFullScreenElement", "webkitCancelFullScreen", "webkitfullscreenchange", "webkitfullscreenerror" ], [ "mozRequestFullScreen", "mozCancelFullScreen", "mozFullScreenElement", "mozFullScreenEnabled", "mozfullscreenchange", "mozfullscreenerror" ], [ "msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError" ] ]; const nativeAPI = (() => { if (typeof document === "undefined") { return false; } const unprefixedMethods = methodMap[0]; const returnValue = {}; for (const methodList of methodMap) { const exitFullscreenMethod = methodList[1]; if (exitFullscreenMethod in document) { for (const [index, method] of methodList.entries()) { returnValue[unprefixedMethods[index]] = method; } return returnValue; } } return false; })(); const eventNameMap = { change: nativeAPI.fullscreenchange, error: nativeAPI.fullscreenerror }; const screenfull = { request(element = document.documentElement, options) { return new Promise((resolve, reject) => { const onFullScreenEntered = () => { screenfull.off("change", onFullScreenEntered); resolve(); }; screenfull.on("change", onFullScreenEntered); const returnPromise = element[nativeAPI.requestFullscreen](options); if (returnPromise instanceof Promise) { returnPromise.then(onFullScreenEntered).catch(reject); } }); }, exit() { return new Promise((resolve, reject) => { if (!screenfull.isFullscreen) { resolve(); return; } const onFullScreenExit = () => { screenfull.off("change", onFullScreenExit); resolve(); }; screenfull.on("change", onFullScreenExit); const returnPromise = document[nativeAPI.exitFullscreen](); if (returnPromise instanceof Promise) { returnPromise.then(onFullScreenExit).catch(reject); } }); }, toggle(element, options) { return screenfull.isFullscreen ? screenfull.exit() : screenfull.request(element, options); }, onchange(callback) { screenfull.on("change", callback); }, onerror(callback) { screenfull.on("error", callback); }, on(event, callback) { const eventName = eventNameMap[event]; if (eventName) { document.addEventListener(eventName, callback, false); } }, off(event, callback) { const eventName = eventNameMap[event]; if (eventName) { document.removeEventListener(eventName, callback, false); } }, raw: nativeAPI }; Object.defineProperties(screenfull, { isFullscreen: { get: () => Boolean(document[nativeAPI.fullscreenElement]) }, element: { enumerable: true, get: () => document[nativeAPI.fullscreenElement] }, isEnabled: { enumerable: true, get: () => Boolean(document[nativeAPI.fullscreenEnabled]) } }); function fullscreenMix(art) { const { i18n, notice, template: { $video, $player } } = art; const nativeScreenfull = (art2) => { screenfull.on("change", () => { art2.emit("fullscreen", screenfull.isFullscreen); if (screenfull.isFullscreen) { art2.state = "fullscreen"; addClass($player, "art-fullscreen"); } else { removeClass($player, "art-fullscreen"); } art2.emit("resize"); }); screenfull.on("error", (event) => { art2.emit("fullscreenError", event); }); def(art2, "fullscreen", { get() { return screenfull.isFullscreen; }, async set(value) { if (value) { await screenfull.request($player); } else { await screenfull.exit(); } } }); }; const webkitScreenfull = (art2) => { art2.on("document:webkitfullscreenchange", () => { art2.emit("fullscreen", art2.fullscreen); art2.emit("resize"); }); def(art2, "fullscreen", { get() { return document.fullscreenElement === $video; }, set(value) { if (value) { art2.state = "fullscreen"; $video.webkitEnterFullscreen(); } else { $video.webkitExitFullscreen(); } } }); }; art.once("video:loadedmetadata", () => { if (screenfull.isEnabled) { nativeScreenfull(art); } else if ($video.webkitSupportsFullscreen) { webkitScreenfull(art); } else { def(art, "fullscreen", { get() { return false; }, set() { notice.show = i18n.get("Fullscreen Not Supported"); } }); } def(art, "fullscreen", get(art, "fullscreen")); }); } function fullscreenWebMix(art) { const { constructor, template: { $container, $player } } = art; let cssText = ""; def(art, "fullscreenWeb", { get() { return hasClass($player, "art-fullscreen-web"); }, set(value) { if (value) { cssText = $player.style.cssText; if (constructor.FULLSCREEN_WEB_IN_BODY) { append(document.body, $player); } art.state = "fullscreenWeb"; setStyle($player, "width", "100%"); setStyle($player, "height", "100%"); addClass($player, "art-fullscreen-web"); art.emit("fullscreenWeb", true); } else { if (constructor.FULLSCREEN_WEB_IN_BODY) { append($container, $player); } if (cssText) { $player.style.cssText = cssText; cssText = ""; } removeClass($player, "art-fullscreen-web"); art.emit("fullscreenWeb", false); } art.emit("resize"); } }); } function loadedMix(art) { const { $video } = art.template; def(art, "loaded", { get: () => art.loadedTime / $video.duration }); def(art, "loadedTime", { get: () => { return $video.buffered.length ? $video.buffered.end($video.buffered.length - 1) : 0; } }); } function miniMix(art) { const { icons, proxy, storage, template: { $player, $video } } = art; let isDragging = false; let lastPageX = 0; let lastPageY = 0; function hideMini() { const { $mini } = art.template; if ($mini) { removeClass($player, "art-mini"); setStyle($mini, "display", "none"); $player.prepend($video); art.emit("mini", false); } } function initState($play, $pause) { if (art.playing) { setStyle($play, "display", `none`); setStyle($pause, "display", `flex`); } else { setStyle($play, "display", `flex`); setStyle($pause, "display", `none`); } } function createMini() { const { $mini } = art.template; if ($mini) { append($mini, $video); return setStyle($mini, "display", "flex"); } else { const $mini2 = createElement("div"); addClass($mini2, "art-mini-popup"); append(document.body, $mini2); art.template.$mini = $mini2; append($mini2, $video); const $close = append($mini2, `
`); append($close, icons.close); proxy($close, "click", hideMini); const $state = append($mini2, `
`); const $play = append($state, icons.play); const $pause = append($state, icons.pause); proxy($play, "click", () => silencePromise(art.play())); proxy($pause, "click", () => art.pause()); initState($play, $pause); art.on("video:playing", () => initState($play, $pause)); art.on("video:pause", () => initState($play, $pause)); art.on("video:timeupdate", () => initState($play, $pause)); proxy($mini2, "mousedown", (event) => { isDragging = event.button === 0; lastPageX = event.pageX; lastPageY = event.pageY; }); art.on("document:mousemove", (event) => { if (isDragging) { addClass($mini2, "art-mini-dragging"); const x = event.pageX - lastPageX; const y = event.pageY - lastPageY; setStyle($mini2, "transform", `translate(${x}px, ${y}px)`); } }); art.on("document:mouseup", () => { if (isDragging) { isDragging = false; removeClass($mini2, "art-mini-dragging"); const rect = getRect($mini2); storage.set("left", rect.left); storage.set("top", rect.top); setStyle($mini2, "left", `${rect.left}px`); setStyle($mini2, "top", `${rect.top}px`); setStyle($mini2, "transform", null); } }); return $mini2; } } function initMini() { const { $mini } = art.template; const rect = getRect($mini); const top = window.innerHeight - rect.height - 50; const left = window.innerWidth - rect.width - 50; storage.set("top", top); storage.set("left", left); setStyle($mini, "top", `${top}px`); setStyle($mini, "left", `${left}px`); } def(art, "mini", { get() { return hasClass($player, "art-mini"); }, set(value) { if (value) { art.state = "mini"; addClass($player, "art-mini"); const $mini = createMini(); const top = storage.get("top"); const left = storage.get("left"); if (typeof top === "number" && typeof left === "number") { setStyle($mini, "top", `${top}px`); setStyle($mini, "left", `${left}px`); if (!isInViewport($mini)) { initMini(); } } else { initMini(); } art.emit("mini", true); } else { hideMini(); } } }); } function optionInit(art) { const { option, storage, template: { $video, $poster } } = art; for (const key in option.moreVideoAttr) { art.attr(key, option.moreVideoAttr[key]); } if (option.muted) { art.muted = option.muted; } if (option.volume) { $video.volume = clamp(option.volume, 0, 1); } const volumeStorage = storage.get("volume"); if (typeof volumeStorage === "number") { $video.volume = clamp(volumeStorage, 0, 1); } if (option.poster) { setStyle($poster, "backgroundImage", `url(${option.poster})`); } if (option.autoplay) { $video.autoplay = option.autoplay; } if (option.playsInline) { $video.playsInline = true; $video["webkit-playsinline"] = true; } if (option.theme) { option.cssVar["--art-theme"] = option.theme; } for (const key in option.cssVar) { art.cssVar(key, option.cssVar[key]); } art.url = option.url; } function pauseMix(art) { const { template: { $video }, i18n, notice } = art; def(art, "pause", { value() { const result = $video.pause(); notice.show = i18n.get("Pause"); art.emit("pause"); return result; } }); } function nativePip(art) { const { template: { $video }, proxy, notice } = art; $video.disablePictureInPicture = false; def(art, "pip", { get() { return document.pictureInPictureElement; }, set(value) { if (value) { art.state = "pip"; $video.requestPictureInPicture().catch((err) => { notice.show = err; throw err; }); } else { document.exitPictureInPicture().catch((err) => { notice.show = err; throw err; }); } } }); proxy($video, "enterpictureinpicture", () => { art.emit("pip", true); }); proxy($video, "leavepictureinpicture", () => { art.emit("pip", false); }); } function webkitPip(art) { const { $video } = art.template; $video.webkitSetPresentationMode("inline"); def(art, "pip", { get() { return $video.webkitPresentationMode === "picture-in-picture"; }, set(value) { if (value) { art.state = "pip"; $video.webkitSetPresentationMode("picture-in-picture"); art.emit("pip", true); } else { $video.webkitSetPresentationMode("inline"); art.emit("pip", false); } } }); } function pipMix(art) { const { i18n, notice, template: { $video } } = art; if (document.pictureInPictureEnabled) { nativePip(art); } else if ($video.webkitSupportsPresentationMode) { webkitPip(art); } else { def(art, "pip", { get() { return false; }, set() { notice.show = i18n.get("PIP Not Supported"); } }); } } function playbackRateMix(art) { const { template: { $video }, i18n, notice } = art; def(art, "playbackRate", { get() { return $video.playbackRate; }, set(rate) { if (rate) { if (rate === $video.playbackRate) return; $video.playbackRate = rate; notice.show = `${i18n.get("Rate")}: ${rate === 1 ? i18n.get("Normal") : `${rate}x`}`; } else { art.playbackRate = 1; } } }); } function playedMix(art) { def(art, "played", { get: () => art.currentTime / art.duration }); } function playingMix(art) { const { $video } = art.template; def(art, "playing", { get: () => { if (typeof $video.playing === "boolean") return $video.playing; return !!($video.currentTime > 0 && !$video.paused && !$video.ended && $video.readyState > 2); } }); } function playMix(art) { const { i18n, notice, option, constructor: { instances: instances2 }, template: { $video } } = art; def(art, "play", { async value() { const result = await $video.play(); notice.show = i18n.get("Play"); art.emit("play"); if (option.mutex) { for (let index = 0; index < instances2.length; index++) { const instance = instances2[index]; if (instance !== art) { instance.pause(); } } } return result; } }); } function posterMix(art) { const { template: { $poster } } = art; def(art, "poster", { get: () => { try { return $poster.style.backgroundImage.match(/"(.*)"/)[1]; } catch { return ""; } }, set(url) { setStyle($poster, "backgroundImage", `url(${url})`); } }); } function qualityMix(art) { def(art, "quality", { set(quality) { const { controls, notice, i18n } = art; const qualityDefault = quality.find((item) => item.default) || quality[0]; controls.update({ name: "quality", position: "right", index: 10, style: { marginRight: "10px" }, html: qualityDefault?.html || "", selector: quality, async onSelect(item) { await art.switchQuality(item.url); notice.show = `${i18n.get("Switch Video")}: ${item.html}`; return item.html; } }); } }); } function rectMix(art) { def(art, "rect", { get: () => { return getRect(art.template.$player); } }); const keys = ["bottom", "height", "left", "right", "top", "width"]; for (let index = 0; index < keys.length; index++) { const key = keys[index]; def(art, key, { get: () => { return art.rect[key]; } }); } def(art, "x", { get: () => { return art.left + window.pageXOffset; } }); def(art, "y", { get: () => { return art.top + window.pageYOffset; } }); } function screenshotMix(art) { const { notice, template: { $video } } = art; const $canvas = createElement("canvas"); def(art, "getDataURL", { value: () => new Promise((resolve, reject) => { try { $canvas.width = $video.videoWidth; $canvas.height = $video.videoHeight; $canvas.getContext("2d").drawImage($video, 0, 0); resolve($canvas.toDataURL("image/png")); } catch (err) { notice.show = err; reject(err); } }) }); def(art, "getBlobUrl", { value: () => new Promise((resolve, reject) => { try { $canvas.width = $video.videoWidth; $canvas.height = $video.videoHeight; $canvas.getContext("2d").drawImage($video, 0, 0); $canvas.toBlob((blob) => { resolve(URL.createObjectURL(blob)); }); } catch (err) { notice.show = err; reject(err); } }) }); def(art, "screenshot", { value: async (name) => { const dataUri = await art.getDataURL(); const fileName = name || `artplayer_${secondToTime($video.currentTime)}`; download(dataUri, `${fileName}.png`); art.emit("screenshot", dataUri); return dataUri; } }); } function seekMix(art) { const { notice } = art; def(art, "seek", { set(time2) { art.currentTime = time2; if (art.duration) { notice.show = `${secondToTime(art.currentTime)} / ${secondToTime(art.duration)}`; } art.emit("seek", art.currentTime, time2); } }); def(art, "forward", { set(time2) { art.seek = art.currentTime + time2; } }); def(art, "backward", { set(time2) { art.seek = art.currentTime - time2; } }); } function stateMix(art) { const states = ["mini", "pip", "fullscreen", "fullscreenWeb"]; def(art, "state", { get: () => states.find((name) => art[name]) || "standard", set(name) { for (let index = 0; index < states.length; index++) { const prop = states[index]; if (prop !== name && art[prop]) { art[prop] = false; } } } }); } function subtitleOffsetMix(art) { const { notice, i18n, template } = art; def(art, "subtitleOffset", { get() { return template.$track?.offset || 0; }, set(value) { const { cues } = art.subtitle; if (!template.$track || cues.length === 0) return; const offset = clamp(value, -10, 10); template.$track.offset = offset; for (let index = 0; index < cues.length; index++) { const cue = cues[index]; cue.originalStartTime = cue.originalStartTime ?? cue.startTime; cue.originalEndTime = cue.originalEndTime ?? cue.endTime; cue.startTime = clamp(cue.originalStartTime + offset, 0, art.duration); cue.endTime = clamp(cue.originalEndTime + offset, 0, art.duration); } art.subtitle.update(); notice.show = `${i18n.get("Subtitle Offset")}: ${value}s`; art.emit("subtitleOffset", value); } }); } function switchMix(art) { function switchUrl(url, currentTime) { return new Promise((resolve, reject) => { if (url === art.url) { resolve(); return; } const { playing, aspectRatio: aspectRatio2, playbackRate: playbackRate2 } = art; art.pause(); art.url = url; art.notice.show = ""; const handlers = {}; handlers.error = (error2) => { art.off("video:canplay", handlers.canplay); art.off("video:loadedmetadata", handlers.metadata); reject(error2); }; handlers.metadata = () => { art.currentTime = currentTime; }; handlers.canplay = async () => { art.off("video:error", handlers.error); art.playbackRate = playbackRate2; art.aspectRatio = aspectRatio2; if (playing) { await silencePromise(art.play()); } art.notice.show = ""; resolve(); }; art.once("video:error", handlers.error); art.once("video:loadedmetadata", handlers.metadata); art.once("video:canplay", handlers.canplay); }); } def(art, "switchQuality", { value: (url) => { return switchUrl(url, art.currentTime); } }); def(art, "switchUrl", { value: (url) => { return switchUrl(url, 0); } }); def(art, "switch", { set: art.switchUrl }); } function themeMix(art) { def(art, "theme", { get() { return art.cssVar("--art-theme"); }, set(theme) { art.cssVar("--art-theme", theme); } }); } function thumbnailsMix(art) { const { option, template: { $progress, $video } } = art; let timer = null; let loding = false; let image = null; function reset() { clearTimeout(timer); timer = null; loding = false; image = null; } function showThumbnails(posWidth) { const $thumbnails = art.controls?.thumbnails; if (!$thumbnails) return; const { number, column, width, height, scale } = option.thumbnails; const width2 = width * scale || image.naturalWidth / column; const height2 = height * scale || width2 / ($video.videoWidth / $video.videoHeight); const perWidth = $progress.clientWidth / number; const perIndex = Math.floor(posWidth / perWidth); const yIndex = Math.ceil(perIndex / column) - 1; const xIndex = perIndex % column || column - 1; setStyle($thumbnails, "backgroundImage", `url(${image.src})`); setStyle($thumbnails, "height", `${height2}px`); setStyle($thumbnails, "width", `${width2}px`); setStyle($thumbnails, "backgroundPosition", `-${xIndex * width2}px -${yIndex * height2}px`); if (posWidth <= width2 / 2) { setStyle($thumbnails, "left", 0); } else if (posWidth > $progress.clientWidth - width2 / 2) { setStyle($thumbnails, "left", `${$progress.clientWidth - width2}px`); } else { setStyle($thumbnails, "left", `${posWidth - width2 / 2}px`); } } art.on("setBar", async (type, percentage, event) => { const $thumbnails = art.controls?.thumbnails; const { url, scale } = option.thumbnails; if (!$thumbnails || !url) return; const isMobileDragging = type === "played" && event && isMobile; if (type === "hover" || isMobileDragging) { if (!image && !loding) { loding = true; image = await loadImg(url, scale); loding = false; } if (!image) return; const width = $progress.clientWidth * percentage; if (width > 0 && width < $progress.clientWidth) { showThumbnails(width); } } }); def(art, "thumbnails", { get() { return art.option.thumbnails; }, set(thumbnails) { if (thumbnails.url && !art.option.isLive) { art.option.thumbnails = thumbnails; reset(); } } }); } function toggleMix(art) { def(art, "toggle", { value() { if (art.playing) { return art.pause(); } else { return art.play(); } } }); } function typeMix(art) { def(art, "type", { get() { return art.option.type; }, set(type) { art.option.type = type; } }); } function urlMix(art) { const { option, template: { $video } } = art; def(art, "url", { get() { return $video.src; }, async set(newUrl) { if (newUrl) { const oldUrl = art.url; const typeName = option.type || getExt(newUrl); const typeCallback = option.customType[typeName]; if (typeName && typeCallback) { await sleep(); art.loading.show = true; typeCallback.call(art, $video, newUrl, art); } else { URL.revokeObjectURL(oldUrl); $video.src = newUrl; } if (oldUrl !== art.url) { art.option.url = newUrl; if (art.isReady && oldUrl) { art.once("video:canplay", () => { art.emit("restart", newUrl); }); } } } else { await sleep(); art.loading.show = true; } } }); } function volumeMix(art) { const { template: { $video }, i18n, notice, storage } = art; def(art, "volume", { get: () => $video.volume || 0, set: (percentage) => { $video.volume = clamp(percentage, 0, 1); notice.show = `${i18n.get("Volume")}: ${Number.parseInt($video.volume * 100, 10)}`; if ($video.volume !== 0) { storage.set("volume", $video.volume); } } }); def(art, "muted", { get: () => $video.muted, set: (muted) => { $video.muted = muted; art.emit("muted", muted); } }); } class Player { constructor(art) { urlMix(art); attrMix(art); playMix(art); pauseMix(art); toggleMix(art); seekMix(art); volumeMix(art); currentTimeMix(art); durationMix(art); switchMix(art); playbackRateMix(art); aspectRatioMix(art); screenshotMix(art); fullscreenMix(art); fullscreenWebMix(art); pipMix(art); loadedMix(art); playedMix(art); playingMix(art); autoSizeMix(art); rectMix(art); flipMix(art); miniMix(art); posterMix(art); autoHeightMix(art); cssVarMix(art); themeMix(art); typeMix(art); stateMix(art); subtitleOffsetMix(art); airplayMix(art); qualityMix(art); thumbnailsMix(art); eventInit(art); optionInit(art); } } function autoOrientation(art) { const { notice, constructor, template: { $player, $video } } = art; const WEB_CLASS = "art-auto-orientation"; const FS_CLASS = "art-auto-orientation-fullscreen"; let fsLocked = false; function applyWebRotate() { const insets = getSafeAreaInsets(); const viewWidth = document.documentElement.clientWidth; const viewHeight = document.documentElement.clientHeight; const safeWidth = viewHeight - insets.top - insets.bottom; const safeHeight = viewWidth - insets.left - insets.right; setStyle($player, "width", `${safeWidth}px`); setStyle($player, "height", `${safeHeight}px`); setStyle($player, "transform-origin", "0 0"); setStyle($player, "transform", `rotate(90deg) translate(${insets.top}px, -${viewWidth - insets.right}px)`); addClass($player, WEB_CLASS); art.isRotate = true; art.emit("resize"); } function clearWebRotate() { setStyle($player, "width", ""); setStyle($player, "height", ""); setStyle($player, "transform-origin", ""); setStyle($player, "transform", ""); removeClass($player, WEB_CLASS); art.isRotate = false; art.emit("resize"); } function needRotate() { const { videoWidth, videoHeight } = $video; const vw = document.documentElement.clientWidth; const vh = document.documentElement.clientHeight; return videoWidth > videoHeight && vw < vh || videoWidth < videoHeight && vw > vh; } art.on("fullscreenWeb", (state2) => { if (state2) { if (needRotate()) { const delay = Number(constructor.AUTO_ORIENTATION_TIME ?? 0); setTimeout(() => { if (art.fullscreenWeb && !hasClass($player, WEB_CLASS)) { applyWebRotate(); } }, delay); } } else { if (hasClass($player, WEB_CLASS)) clearWebRotate(); } }); art.on("fullscreen", async (state2) => { const canLock = !!screen?.orientation?.lock; if (state2) { if (canLock && needRotate()) { try { const last = screen.orientation.type; const opposite = last.startsWith("portrait") ? "landscape" : "portrait"; await screen.orientation.lock(opposite); fsLocked = true; addClass($player, FS_CLASS); } catch (err) { fsLocked = false; notice.show = err; } } } else { if (hasClass($player, FS_CLASS)) { removeClass($player, FS_CLASS); } if (canLock && fsLocked) { try { screen.orientation.unlock(); } catch { } fsLocked = false; } } }); return { name: "autoOrientation", get state() { return hasClass($player, WEB_CLASS); } }; } function autoPlayback(art) { const { i18n, icons, storage, constructor, proxy, template: { $poster } } = art; const $autoPlayback = art.layers.add({ name: "auto-playback", html: `
` }); const $last = query(".art-auto-playback-last", $autoPlayback); const $jump = query(".art-auto-playback-jump", $autoPlayback); const $close = query(".art-auto-playback-close", $autoPlayback); append($close, icons.close); let timer = null; art.on("video:timeupdate", () => { if (art.playing) { const times = storage.get("times") || {}; const keys = Object.keys(times); if (keys.length > constructor.AUTO_PLAYBACK_MAX) { delete times[keys[0]]; } times[art.option.id || art.option.url] = art.currentTime; storage.set("times", times); } }); function init() { const times = storage.get("times") || {}; const currentTime = times[art.option.id || art.option.url]; clearTimeout(timer); setStyle($autoPlayback, "display", "none"); if (currentTime && currentTime >= constructor.AUTO_PLAYBACK_MIN) { setStyle($autoPlayback, "display", "flex"); $last.textContent = `${i18n.get("Last Seen")} ${secondToTime(currentTime)}`; $jump.textContent = i18n.get("Jump Play"); proxy($close, "click", () => { setStyle($autoPlayback, "display", "none"); }); proxy($jump, "click", () => { art.seek = currentTime; silencePromise(art.play()); setStyle($poster, "display", "none"); setStyle($autoPlayback, "display", "none"); }); art.once("video:timeupdate", () => { timer = setTimeout(() => { setStyle($autoPlayback, "display", "none"); }, constructor.AUTO_PLAYBACK_TIMEOUT); }); } } art.on("ready", init); art.on("restart", init); return { name: "auto-playback", get times() { return storage.get("times") || {}; }, clear() { return storage.del("times"); }, delete(id2) { const times = storage.get("times") || {}; delete times[id2]; storage.set("times", times); return times; } }; } function fastForward(art) { const { constructor, proxy, template: { $player, $video } } = art; let timer = null; let isPress = false; let lastPlaybackRate = 1; const onStart = (event) => { if (event.touches.length === 1 && art.playing && !art.isLock) { timer = setTimeout(() => { isPress = true; lastPlaybackRate = art.playbackRate; art.playbackRate = constructor.FAST_FORWARD_VALUE; addClass($player, "art-fast-forward"); }, constructor.FAST_FORWARD_TIME); } }; const onStop = () => { clearTimeout(timer); if (isPress) { isPress = false; art.playbackRate = lastPlaybackRate; removeClass($player, "art-fast-forward"); } }; proxy($video, "touchstart", onStart); art.on("document:touchmove", onStop); art.on("document:touchend", onStop); return { name: "fastForward", get state() { return hasClass($player, "art-fast-forward"); } }; } function lock(art) { const { layers, icons, template: { $player } } = art; function getState() { return hasClass($player, "art-lock"); } function setLock() { addClass($player, "art-lock"); art.isLock = true; art.emit("lock", true); } function setUnlock() { removeClass($player, "art-lock"); art.isLock = false; art.emit("lock", false); } layers.add({ name: "lock", mounted($el) { const $lock = append($el, icons.lock); const $unlock = append($el, icons.unlock); setStyle($lock, "display", "none"); art.on("lock", (state2) => { if (state2) { setStyle($lock, "display", "inline-flex"); setStyle($unlock, "display", "none"); } else { setStyle($lock, "display", "none"); setStyle($unlock, "display", "inline-flex"); } }); }, click() { if (getState()) { setUnlock(); } else { setLock(); } } }); return { name: "lock", get state() { return getState(); }, set state(value) { if (value) { setLock(); } else { setUnlock(); } } }; } function miniProgressBar(art) { art.on("control", (state2) => { if (state2) { removeClass(art.template.$player, "art-mini-progress-bar"); } else { addClass(art.template.$player, "art-mini-progress-bar"); } }); return { name: "mini-progress-bar" }; } class Plugins { constructor(art) { this.art = art; this.id = 0; const { option } = art; if (option.miniProgressBar && !option.isLive) { this.add(miniProgressBar); } if (option.lock && isMobile) { this.add(lock); } if (option.autoPlayback && !option.isLive) { this.add(autoPlayback); } if (option.autoOrientation && isMobile) { this.add(autoOrientation); } if (option.fastForward && isMobile && !option.isLive) { this.add(fastForward); } for (let index = 0; index < option.plugins.length; index++) { this.add(option.plugins[index]); } } add(plugin) { this.id += 1; const result = plugin.call(this.art, this.art); if (result instanceof Promise) { return result.then((res) => this.next(plugin, res)); } else { return this.next(plugin, result); } } next(plugin, result) { const pluginName = result && result.name || plugin.name || `plugin${this.id}`; errorHandle(!has(this, pluginName), `Cannot add a plugin that already has the same name: ${pluginName}`); def(this, pluginName, { value: result }); return this; } } function aspectRatio(art) { const { i18n, icons, constructor: { SETTING_ITEM_WIDTH, ASPECT_RATIO } } = art; function getI18n(value) { return value === "default" ? i18n.get("Default") : value; } function update() { const target = art.setting.find(`aspect-ratio-${art.aspectRatio}`); art.setting.check(target); } return { width: SETTING_ITEM_WIDTH, name: "aspect-ratio", html: i18n.get("Aspect Ratio"), icon: icons.aspectRatio, tooltip: getI18n(art.aspectRatio), selector: ASPECT_RATIO.map((item) => { return { value: item, name: `aspect-ratio-${item}`, default: item === art.aspectRatio, html: getI18n(item) }; }), onSelect(item) { art.aspectRatio = item.value; return item.html; }, mounted: () => { update(); art.on("aspectRatio", () => update()); } }; } function flip(art) { const { i18n, icons, constructor: { SETTING_ITEM_WIDTH, FLIP } } = art; function getI18n(value) { return i18n.get(capitalize(value)); } function update() { const target = art.setting.find(`flip-${art.flip}`); art.setting.check(target); } return { width: SETTING_ITEM_WIDTH, name: "flip", html: i18n.get("Video Flip"), tooltip: getI18n(art.flip), icon: icons.flip, selector: FLIP.map((item) => { return { value: item, name: `flip-${item}`, default: item === art.flip, html: getI18n(item) }; }), onSelect(item) { art.flip = item.value; return item.html; }, mounted: () => { update(); art.on("flip", () => update()); } }; } function playbackRate(art) { const { i18n, icons, constructor: { SETTING_ITEM_WIDTH, PLAYBACK_RATE } } = art; function getI18n(value) { return value === 1 ? i18n.get("Normal") : value.toFixed(1); } function update() { const target = art.setting.find(`playback-rate-${art.playbackRate}`); art.setting.check(target); } return { width: SETTING_ITEM_WIDTH, name: "playback-rate", html: i18n.get("Play Speed"), tooltip: getI18n(art.playbackRate), icon: icons.playbackRate, selector: PLAYBACK_RATE.map((item) => { return { value: item, name: `playback-rate-${item}`, default: item === art.playbackRate, html: getI18n(item) }; }), onSelect(item) { art.playbackRate = item.value; return item.html; }, mounted: () => { update(); art.on("video:ratechange", () => update()); } }; } function subtitleOffset(art) { const { i18n, icons, constructor } = art; return { width: constructor.SETTING_ITEM_WIDTH, name: "subtitle-offset", html: i18n.get("Subtitle Offset"), icon: icons.subtitle, tooltip: "0s", range: [0, -10, 10, 0.1], onChange(item) { art.subtitleOffset = item.range[0]; return `${item.range[0]}s`; }, mounted: (_, item) => { art.on("subtitleOffset", (value) => { item.$range.value = value; item.tooltip = `${value}s`; }); } }; } class Setting extends Component { constructor(art) { super(art); const { option, controls, template: { $setting } } = art; this.name = "setting"; this.$parent = $setting; this.id = 0; this.active = null; this.cache = /* @__PURE__ */ new Map(); this.option = [...this.builtin, ...option.settings]; if (option.setting) { this.format(); this.render(); art.on("blur", () => { if (this.show) { this.show = false; this.render(); } }); art.on("focus", (event) => { const isControl = includeFromEvent(event, controls.setting); const isSetting = includeFromEvent(event, this.$parent); if (this.show && !isControl && !isSetting) { this.show = false; this.render(); } }); art.on("resize", () => this.resize()); } } get builtin() { const result = []; const { option } = this.art; if (option.playbackRate) { result.push(playbackRate(this.art)); } if (option.aspectRatio) { result.push(aspectRatio(this.art)); } if (option.flip) { result.push(flip(this.art)); } if (option.subtitleOffset) { result.push(subtitleOffset(this.art)); } return result; } traverse(callback, option = this.option) { for (let index = 0; index < option.length; index++) { const item = option[index]; callback(item); if (item.selector?.length) { this.traverse(callback, item.selector); } } } check(target) { if (!target) { return; } target.$parent.tooltip = target.html; this.traverse((item) => { item.default = item === target; if (item.default && item.$item) { inverseClass(item.$item, "art-current"); } }, target.$option); this.render(target.$parents); } format(option = this.option, parent, parents, names = []) { for (let index = 0; index < option.length; index++) { const item = option[index]; if (item?.name) { errorHandle(!names.includes(item.name), `The [${item.name}] already exists in [setting]`); names.push(item.name); } else { item.name = `setting-${this.id++}`; } if (!item.$formatted) { def(item, "$parent", { get: () => parent }); def(item, "$parents", { get: () => parents }); def(item, "$option", { get: () => option }); const $events = []; def(item, "$events", { get: () => $events }); def(item, "$formatted", { get: () => true }); } this.format(item.selector || [], item, option, names); } this.option = option; } find(name = "") { let result = null; this.traverse((item) => { if (item.name === name) { result = item; } }); return result; } resize() { const { controls, constructor: { SETTING_WIDTH, SETTING_ITEM_HEIGHT }, template: { $player, $setting } } = this.art; if (controls.setting && this.show) { const settingWidth = this.active[0]?.$parent?.width || SETTING_WIDTH; const { left: controlLeft, width: controlWidth } = getRect(controls.setting); const { left: playerLeft, width: playerWidth } = getRect($player); const settingLeft = controlLeft - playerLeft + controlWidth / 2 - settingWidth / 2; const settingHeight = this.active === this.option ? this.active.length * SETTING_ITEM_HEIGHT : (this.active.length + 1) * SETTING_ITEM_HEIGHT; setStyle($setting, "height", `${settingHeight}px`); setStyle($setting, "width", `${settingWidth}px`); if (this.art.isRotate || isMobile) return; if (settingLeft + settingWidth > playerWidth) { setStyle($setting, "left", null); setStyle($setting, "right", null); } else { setStyle($setting, "left", `${settingLeft}px`); setStyle($setting, "right", "auto"); } } } inactivate(item) { for (let index = 0; index < item.$events.length; index++) { this.art.events.remove(item.$events[index]); } item.$events.length = 0; } remove(name) { const item = this.find(name); errorHandle(item, `Can't find [${name}] in the [setting]`); const index = item.$option.indexOf(item); item.$option.splice(index, 1); this.inactivate(item); if (item.$item) remove(item.$item); this.render(); } update(target) { const item = this.find(target.name); if (item) { this.inactivate(item); Object.assign(item, target); this.format(); this.createItem(item, true); this.render(); return item; } else { return this.add(target); } } add(item, option = this.option) { option.push(item); this.format(); this.createItem(item); this.render(); return item; } createHeader(item) { if (!this.cache.has(item.$option)) return; const $panel = this.cache.get(item.$option); const { proxy, icons: { arrowLeft: arrowLeft2 }, constructor: { SETTING_ITEM_HEIGHT } } = this.art; const $item = createElement("div"); setStyle($item, "height", `${SETTING_ITEM_HEIGHT}px`); addClass($item, "art-setting-item"); addClass($item, "art-setting-item-back"); const $left = append($item, '
'); const $icon = createElement("div"); addClass($icon, "art-setting-item-left-icon"); append($icon, arrowLeft2); append($left, $icon); append($left, item.$parent.html); const event = proxy($item, "click", () => this.render(item.$parents)); item.$parent.$events.push(event); append($panel, $item); } createItem(item, isUpdate = false) { if (!this.cache.has(item.$option)) return; const $panel = this.cache.get(item.$option); const oldItem = item.$item; let type = "selector"; if (has(item, "switch")) { type = "switch"; } if (has(item, "range")) { type = "range"; } if (has(item, "onClick")) { type = "button"; } const { icons, proxy, constructor } = this.art; const $item = createElement("div"); addClass($item, "art-setting-item"); setStyle($item, "height", `${constructor.SETTING_ITEM_HEIGHT}px`); $item.dataset.name = item.name || ""; $item.dataset.value = item.value || ""; const $left = append($item, '
'); const $right = append($item, '
'); const $icon = createElement("div"); addClass($icon, "art-setting-item-left-icon"); switch (type) { case "button": case "switch": case "range": append($icon, item.icon || icons.config); break; case "selector": if (item.selector?.length) { append($icon, item.icon || icons.config); } else { append($icon, icons.check); } break; } append($left, $icon); def(item, "$icon", { configurable: true, get: () => $icon }); def(item, "icon", { configurable: true, get() { return $icon.innerHTML; }, set(value) { $icon.innerHTML = ""; append($icon, value); } }); const $html = createElement("div"); addClass($html, "art-setting-item-left-text"); append($html, item.html || ""); append($left, $html); def(item, "$html", { configurable: true, get: () => $html }); def(item, "html", { configurable: true, get() { return $html.innerHTML; }, set(value) { $html.innerHTML = ""; append($html, value); } }); const $tooltip = createElement("div"); addClass($tooltip, "art-setting-item-right-tooltip"); append($tooltip, item.tooltip || ""); append($right, $tooltip); def(item, "$tooltip", { configurable: true, get: () => $tooltip }); def(item, "tooltip", { configurable: true, get() { return $tooltip.innerHTML; }, set(value) { $tooltip.innerHTML = ""; append($tooltip, value); } }); switch (type) { case "switch": { const $switch = createElement("div"); addClass($switch, "art-setting-item-right-icon"); const $switchOn = append($switch, icons.switchOn); const $switchOff = append($switch, icons.switchOff); setStyle(item.switch ? $switchOff : $switchOn, "display", "none"); append($right, $switch); def(item, "$switch", { configurable: true, get: () => $switch }); let $switchValue = item.switch; def(item, "switch", { configurable: true, get: () => $switchValue, set(value) { $switchValue = value; if (value) { setStyle($switchOff, "display", "none"); setStyle($switchOn, "display", null); } else { setStyle($switchOff, "display", null); setStyle($switchOn, "display", "none"); } } }); break; } case "range": { const $state = createElement("div"); addClass($state, "art-setting-item-right-icon"); const $range = append($state, ''); $range.value = item.range[0]; $range.min = item.range[1]; $range.max = item.range[2]; $range.step = item.range[3]; addClass($range, "art-setting-range"); append($right, $state); def(item, "$range", { configurable: true, get: () => $range }); let $rangeValue = [...item.range]; def(item, "range", { configurable: true, get: () => $rangeValue, set(value) { $rangeValue = [...value]; $range.value = value[0]; $range.min = value[1]; $range.max = value[2]; $range.step = value[3]; } }); } break; case "selector": if (item.selector?.length) { const $state = createElement("div"); addClass($state, "art-setting-item-right-icon"); append($state, icons.arrowRight); append($right, $state); } break; } switch (type) { case "switch": { if (item.onSwitch) { const event = proxy($item, "click", async (event2) => { item.switch = await item.onSwitch.call(this.art, item, $item, event2); }); item.$events.push(event); } break; } case "range": { if (item.$range) { if (item.onRange) { const event = proxy(item.$range, "change", async (event2) => { item.range[0] = item.$range.valueAsNumber; item.tooltip = await item.onRange.call(this.art, item, $item, event2); }); item.$events.push(event); } if (item.onChange) { const event = proxy(item.$range, "input", async (event2) => { item.range[0] = item.$range.valueAsNumber; item.tooltip = await item.onChange.call(this.art, item, $item, event2); }); item.$events.push(event); } } break; } case "selector": { const event = proxy($item, "click", async (event2) => { if (item.selector?.length) { this.render(item.selector); } else { this.check(item); if (item.$parent.onSelect) { item.$parent.tooltip = await item.$parent.onSelect.call(this.art, item, $item, event2); } } }); item.$events.push(event); if (item.default) { addClass($item, "art-current"); } } break; case "button": if (item.onClick) { const event = proxy($item, "click", async (event2) => { item.tooltip = await item.onClick.call(this.art, item, $item, event2); }); item.$events.push(event); } break; } def(item, "$item", { configurable: true, get: () => $item }); if (isUpdate) { replaceElement($item, oldItem); } else { append($panel, $item); } if (item.mounted) { setTimeout(() => item.mounted.call(this.art, item.$item, item), 0); } } render(option = this.option) { this.active = option; if (this.cache.has(option)) { const $panel = this.cache.get(option); inverseClass($panel, "art-current"); } else { const $panel = createElement("div"); this.cache.set(option, $panel); addClass($panel, "art-setting-panel"); append(this.$parent, $panel); inverseClass($panel, "art-current"); if (option[0]?.$parent) { this.createHeader(option[0]); } for (let index = 0; index < option.length; index++) { this.createItem(option[index]); } } this.resize(); } } class Storage { constructor() { this.name = "artplayer_settings"; this.settings = {}; } get(key) { try { const storage = JSON.parse(window.localStorage.getItem(this.name)) || {}; return key ? storage[key] : storage; } catch { return key ? this.settings[key] : this.settings; } } set(key, value) { try { const storage = Object.assign({}, this.get(), { [key]: value }); window.localStorage.setItem(this.name, JSON.stringify(storage)); } catch { this.settings[key] = value; } } del(key) { try { const storage = this.get(); delete storage[key]; window.localStorage.setItem(this.name, JSON.stringify(storage)); } catch { delete this.settings[key]; } } clear() { try { window.localStorage.removeItem(this.name); } catch { this.settings = {}; } } } const style = ".art-video-player {\n --art-theme: #f00;\n --art-font-color: #fff;\n --art-background-color: #000;\n --art-text-shadow-color: rgba(0, 0, 0, 0.5);\n --art-transition-duration: 0.2s;\n --art-padding: 10px;\n --art-border-radius: 3px;\n --art-progress-height: 6px;\n --art-progress-color: rgba(255, 255, 255, 0.25);\n --art-progress-top-gap: 10px;\n --art-hover-color: rgba(255, 255, 255, 0.25);\n --art-loaded-color: rgba(255, 255, 255, 0.25);\n --art-state-size: 80px;\n --art-state-opacity: 0.8;\n --art-bottom-height: 100px;\n --art-bottom-offset: 20px;\n --art-bottom-gap: 5px;\n --art-highlight-width: 8px;\n --art-highlight-color: rgba(255, 255, 255, 0.5);\n --art-control-height: 46px;\n --art-control-opacity: 0.75;\n --art-control-icon-size: 36px;\n --art-control-icon-scale: 1.1;\n --art-volume-height: 120px;\n --art-volume-handle-size: 14px;\n --art-lock-size: 36px;\n --art-indicator-scale: 0;\n --art-indicator-size: 16px;\n --art-fullscreen-web-index: 9999;\n --art-settings-icon-size: 24px;\n --art-settings-max-height: 300px;\n --art-selector-max-height: 300px;\n --art-contextmenus-min-width: 250px;\n --art-subtitle-font-size: 20px;\n --art-subtitle-gap: 5px;\n --art-subtitle-bottom: 15px;\n --art-subtitle-border: #000;\n --art-widget-background: rgba(0, 0, 0, 0.85);\n --art-tip-background: rgba(0, 0, 0, 0.7);\n --art-scrollbar-size: 4px;\n --art-scrollbar-background: rgba(255, 255, 255, 0.25);\n --art-scrollbar-background-hover: rgba(255, 255, 255, 0.5);\n --art-mini-progress-height: 2px;\n}\n.art-bg-cover {\n background-position: center center;\n background-repeat: no-repeat;\n background-size: cover;\n}\n.art-bottom-gradient {\n background-image: linear-gradient(to top, #000, rgba(0, 0, 0, 0.4), transparent);\n background-repeat: repeat-x;\n background-position: center bottom;\n}\n.art-backdrop-filter {\n -webkit-backdrop-filter: saturate(180%) blur(20px);\n backdrop-filter: saturate(180%) blur(20px);\n background-color: rgba(0, 0, 0, 0.75) !important;\n}\n.art-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.art-video-player {\n position: relative;\n margin: 0 auto;\n width: 100%;\n height: 100%;\n outline: 0;\n zoom: 1;\n padding: 0;\n text-align: left;\n direction: ltr;\n font-size: 14px;\n line-height: 1.3;\n user-select: none;\n box-sizing: border-box;\n color: var(--art-font-color);\n background-color: var(--art-background-color);\n text-shadow: 0 0 2px var(--art-text-shadow-color);\n font-family: PingFang SC, Helvetica Neue, Microsoft YaHei, Roboto, Arial, sans-serif;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n -ms-touch-action: manipulation;\n touch-action: manipulation;\n -ms-high-contrast-adjust: none;\n}\n.art-video-player *,\n.art-video-player *::before,\n.art-video-player *::after {\n box-sizing: border-box;\n}\n.art-video-player ::-webkit-scrollbar {\n width: var(--art-scrollbar-size);\n height: var(--art-scrollbar-size);\n}\n.art-video-player ::-webkit-scrollbar-thumb {\n background-color: var(--art-scrollbar-background);\n}\n.art-video-player ::-webkit-scrollbar-thumb:hover {\n background-color: var(--art-scrollbar-background-hover);\n}\n.art-video-player img {\n max-width: 100%;\n vertical-align: top;\n}\n.art-video-player svg {\n fill: var(--art-font-color);\n}\n.art-video-player a {\n color: var(--art-font-color);\n text-decoration: none;\n}\n.art-icon {\n line-height: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.art-video-player.art-backdrop .art-contextmenus,\n.art-video-player.art-backdrop .art-info,\n.art-video-player.art-backdrop .art-settings,\n.art-video-player.art-backdrop .art-layer-auto-playback,\n.art-video-player.art-backdrop .art-selector-list,\n.art-video-player.art-backdrop .art-volume-inner {\n -webkit-backdrop-filter: saturate(180%) blur(20px);\n backdrop-filter: saturate(180%) blur(20px);\n background-color: rgba(0, 0, 0, 0.75) !important;\n}\n.art-video {\n position: absolute;\n inset: 0;\n z-index: 10;\n width: 100%;\n height: 100%;\n}\n.art-poster {\n position: absolute;\n inset: 0;\n z-index: 11;\n width: 100%;\n height: 100%;\n background-position: center center;\n background-repeat: no-repeat;\n background-size: cover;\n pointer-events: none;\n}\n.art-video-player .art-subtitle {\n display: none;\n justify-content: center;\n align-items: center;\n flex-direction: column;\n position: absolute;\n z-index: 20;\n width: 100%;\n padding: 0 5%;\n text-align: center;\n pointer-events: none;\n gap: var(--art-subtitle-gap);\n bottom: var(--art-subtitle-bottom);\n font-size: var(--art-subtitle-font-size);\n transition: bottom var(--art-transition-duration) ease;\n text-shadow: var(--art-subtitle-border) 1px 0 1px, var(--art-subtitle-border) 0 1px 1px, var(--art-subtitle-border) -1px 0 1px, var(--art-subtitle-border) 0 -1px 1px, var(--art-subtitle-border) 1px 1px 1px, var(--art-subtitle-border) -1px -1px 1px, var(--art-subtitle-border) 1px -1px 1px, var(--art-subtitle-border) -1px 1px 1px;\n}\n.art-video-player.art-subtitle-show .art-subtitle {\n display: flex;\n}\n.art-video-player.art-control-show .art-subtitle {\n bottom: calc(var(--art-control-height) + var(--art-subtitle-bottom));\n}\n.art-danmuku {\n position: absolute;\n inset: 0;\n z-index: 30;\n width: 100%;\n height: 100%;\n pointer-events: none;\n overflow: hidden;\n}\n.art-video-player .art-layers {\n position: absolute;\n inset: 0;\n z-index: 40;\n width: 100%;\n height: 100%;\n display: none;\n pointer-events: none;\n}\n.art-video-player .art-layers .art-layer {\n pointer-events: auto;\n}\n.art-video-player.art-layer-show .art-layers {\n display: flex;\n}\n.art-video-player .art-mask {\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n inset: 0;\n z-index: 50;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.art-video-player .art-mask .art-state {\n display: flex;\n justify-content: center;\n align-items: center;\n opacity: 0;\n transform: scale(2);\n width: var(--art-state-size);\n height: var(--art-state-size);\n transition: all var(--art-transition-duration) ease;\n}\n.art-video-player.art-mask-show .art-state {\n pointer-events: auto;\n opacity: var(--art-state-opacity);\n transform: scale(1);\n}\n.art-video-player.art-loading-show .art-state {\n display: none;\n}\n.art-video-player .art-loading {\n display: none;\n justify-content: center;\n align-items: center;\n position: absolute;\n inset: 0;\n z-index: 70;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.art-video-player.art-loading-show .art-loading {\n display: flex;\n}\n.art-video-player.art-loading-show .art-mask {\n display: none;\n}\n.art-video-player .art-bottom {\n position: absolute;\n inset: 0;\n z-index: 60;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n opacity: 0;\n overflow: hidden;\n pointer-events: none;\n padding: 0 var(--art-padding);\n transition: all var(--art-transition-duration) ease;\n background-size: 100% var(--art-bottom-height);\n background-image: linear-gradient(to top, #000, rgba(0, 0, 0, 0.4), transparent);\n background-repeat: repeat-x;\n background-position: center bottom;\n}\n.art-video-player .art-bottom .art-controls,\n.art-video-player .art-bottom .art-progress {\n transform: translateY(var(--art-bottom-offset));\n transition: transform var(--art-transition-duration) ease;\n}\n.art-video-player.art-control-show .art-bottom,\n.art-video-player.art-hover .art-bottom {\n opacity: 1;\n}\n.art-video-player.art-control-show .art-bottom .art-controls,\n.art-video-player.art-hover .art-bottom .art-controls,\n.art-video-player.art-control-show .art-bottom .art-progress,\n.art-video-player.art-hover .art-bottom .art-progress {\n transform: translateY(0);\n}\n.art-bottom .art-progress {\n position: relative;\n z-index: 0;\n cursor: pointer;\n pointer-events: auto;\n padding-top: var(--art-progress-top-gap);\n padding-bottom: var(--art-bottom-gap);\n}\n.art-bottom .art-progress .art-control-progress {\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n height: var(--art-progress-height);\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner {\n display: flex;\n align-items: center;\n position: relative;\n height: 50%;\n width: 100%;\n transition: height var(--art-transition-duration) ease;\n background-color: var(--art-progress-color);\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-hover {\n position: absolute;\n inset: 0;\n z-index: 0;\n width: 100%;\n height: 100%;\n width: 0%;\n background-color: var(--art-hover-color);\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-loaded {\n position: absolute;\n inset: 0;\n z-index: 10;\n width: 100%;\n height: 100%;\n width: 0%;\n background-color: var(--art-loaded-color);\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-played {\n position: absolute;\n inset: 0;\n z-index: 20;\n width: 100%;\n height: 100%;\n width: 0%;\n background-color: var(--art-theme);\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-highlight {\n position: absolute;\n inset: 0;\n z-index: 30;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-highlight span {\n position: absolute;\n inset: 0;\n z-index: 0;\n width: 100%;\n height: 100%;\n right: auto;\n pointer-events: auto;\n width: var(--art-highlight-width) !important;\n transform: translateX(calc(var(--art-highlight-width) / -2));\n background-color: var(--art-highlight-color);\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-indicator {\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n z-index: 40;\n left: 0;\n border-radius: 50%;\n width: var(--art-indicator-size);\n height: var(--art-indicator-size);\n transform: scale(var(--art-indicator-scale));\n margin-left: calc(var(--art-indicator-size) / -2);\n transition: transform var(--art-transition-duration) ease;\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-indicator .art-icon {\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-indicator:hover {\n transform: scale(1.2) !important;\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-indicator:active {\n transform: scale(1) !important;\n}\n.art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-tip {\n transform-origin: bottom center;\n transform: scale(0.5);\n opacity: 0;\n position: absolute;\n z-index: 50;\n top: -25px;\n left: 0;\n padding: 3px 5px;\n line-height: 1;\n font-size: 12px;\n border-radius: var(--art-border-radius);\n white-space: nowrap;\n background-color: var(--art-tip-background);\n transition: transform var(--art-transition-duration) ease, opacity var(--art-transition-duration) ease;\n}\n.art-bottom .art-progress .art-control-thumbnails {\n transform-origin: bottom center;\n transform: scale(0.5);\n opacity: 0;\n position: absolute;\n bottom: calc(var(--art-bottom-gap) + 10px);\n left: 0;\n border-radius: var(--art-border-radius);\n pointer-events: none;\n background-color: var(--art-widget-background);\n transition: transform var(--art-transition-duration) ease, opacity var(--art-transition-duration) ease;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 2px -1px rgba(0, 0, 0, 0.2);\n}\n.art-bottom .art-progress:hover .art-control-progress .art-control-progress-inner {\n height: 100%;\n}\n.art-bottom:hover .art-progress .art-control-progress .art-control-progress-inner .art-progress-indicator {\n transform: scale(1);\n}\n.art-progress-hover .art-bottom .art-progress .art-control-progress .art-control-progress-inner .art-progress-tip,\n.art-progress-hover .art-bottom .art-progress .art-control-thumbnails {\n transform: scale(1);\n opacity: 1;\n}\n.art-video-player .art-controls {\n position: relative;\n z-index: 10;\n pointer-events: auto;\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: var(--art-control-height);\n}\n.art-video-player .art-controls .art-controls-left,\n.art-video-player .art-controls .art-controls-right {\n display: flex;\n height: 100%;\n}\n.art-video-player .art-controls .art-controls-center {\n display: none;\n justify-content: center;\n align-items: center;\n flex: 1;\n height: 100%;\n padding: 0 10px;\n}\n.art-video-player .art-controls .art-controls-right {\n justify-content: flex-end;\n}\n.art-video-player .art-controls .art-control {\n display: flex;\n justify-content: center;\n align-items: center;\n flex-shrink: 0;\n cursor: pointer;\n white-space: nowrap;\n opacity: var(--art-control-opacity);\n min-height: var(--art-control-height);\n min-width: var(--art-control-height);\n transition: opacity var(--art-transition-duration) ease;\n}\n.art-video-player .art-controls .art-control .art-icon {\n height: var(--art-control-icon-size);\n width: var(--art-control-icon-size);\n transform: scale(var(--art-control-icon-scale));\n transition: transform var(--art-transition-duration) ease;\n}\n.art-video-player .art-controls .art-control .art-icon:active {\n transform: scale(calc(var(--art-control-icon-scale) * 0.8));\n}\n.art-video-player .art-controls .art-control:hover {\n opacity: 1;\n}\n.art-control-volume {\n position: relative;\n}\n.art-control-volume .art-volume-panel {\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n left: 0;\n right: 0;\n padding: 0 5px;\n font-size: 12px;\n text-align: center;\n cursor: default;\n opacity: 0;\n transform: translateY(10px);\n pointer-events: none;\n bottom: var(--art-control-height);\n width: var(--art-control-height);\n height: var(--art-volume-height);\n transition: all var(--art-transition-duration) ease;\n}\n.art-control-volume .art-volume-panel .art-volume-inner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n height: 100%;\n width: 100%;\n padding: 10px 0 12px;\n border-radius: var(--art-border-radius);\n background-color: var(--art-widget-background);\n}\n.art-control-volume .art-volume-panel .art-volume-inner .art-volume-slider {\n flex: 1;\n width: 100%;\n display: flex;\n cursor: pointer;\n position: relative;\n justify-content: center;\n}\n.art-control-volume .art-volume-panel .art-volume-inner .art-volume-slider .art-volume-handle {\n position: relative;\n display: flex;\n justify-content: center;\n width: 2px;\n border-radius: var(--art-border-radius);\n overflow: hidden;\n background-color: rgba(255, 255, 255, 0.25);\n}\n.art-control-volume .art-volume-panel .art-volume-inner .art-volume-slider .art-volume-handle .art-volume-loaded {\n position: absolute;\n inset: 0;\n z-index: 0;\n width: 100%;\n height: 100%;\n background-color: var(--art-theme);\n}\n.art-control-volume .art-volume-panel .art-volume-inner .art-volume-slider .art-volume-indicator {\n position: absolute;\n width: var(--art-volume-handle-size);\n height: var(--art-volume-handle-size);\n margin-top: calc(var(--art-volume-handle-size) / -2);\n flex-shrink: 0;\n transform: scale(1);\n border-radius: 100%;\n background-color: var(--art-theme);\n transition: transform var(--art-transition-duration) ease;\n}\n.art-control-volume .art-volume-panel .art-volume-inner .art-volume-slider:active .art-volume-indicator {\n transform: scale(0.9);\n}\n.art-control-volume:hover .art-volume-panel {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n.art-video-player .art-notice {\n display: none;\n position: absolute;\n inset: 0;\n z-index: 80;\n width: 100%;\n height: 100%;\n height: auto;\n bottom: auto;\n padding: var(--art-padding);\n pointer-events: none;\n}\n.art-video-player .art-notice .art-notice-inner {\n display: inline-flex;\n padding: 5px;\n line-height: 1;\n border-radius: var(--art-border-radius);\n background-color: var(--art-tip-background);\n}\n.art-video-player.art-notice-show .art-notice {\n display: flex;\n}\n.art-video-player .art-contextmenus {\n display: none;\n flex-direction: column;\n position: absolute;\n z-index: 120;\n padding: 5px 0;\n border-radius: var(--art-border-radius);\n font-size: 12px;\n background-color: var(--art-widget-background);\n min-width: var(--art-contextmenus-min-width);\n}\n.art-video-player .art-contextmenus .art-contextmenu {\n cursor: pointer;\n display: flex;\n padding: 10px 15px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.art-video-player .art-contextmenus .art-contextmenu span {\n padding: 0 8px;\n}\n.art-video-player .art-contextmenus .art-contextmenu span:hover,\n.art-video-player .art-contextmenus .art-contextmenu span.art-current {\n color: var(--art-theme);\n}\n.art-video-player .art-contextmenus .art-contextmenu:hover {\n background-color: rgba(255, 255, 255, 0.1);\n}\n.art-video-player .art-contextmenus .art-contextmenu:last-child {\n border-bottom: none;\n}\n.art-video-player.art-contextmenu-show .art-contextmenus {\n display: flex;\n}\n.art-video-player .art-settings {\n display: none;\n flex-direction: column;\n position: absolute;\n z-index: 90;\n left: auto;\n overflow-y: auto;\n overflow-x: hidden;\n border-radius: var(--art-border-radius);\n max-height: var(--art-settings-max-height);\n right: var(--art-padding);\n bottom: var(--art-control-height);\n transition: all var(--art-transition-duration) ease;\n background-color: var(--art-widget-background);\n}\n.art-video-player .art-settings .art-setting-panel {\n display: none;\n flex-direction: column;\n}\n.art-video-player .art-settings .art-setting-panel.art-current {\n display: flex;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 5px;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--art-transition-duration) ease;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item:hover {\n background-color: rgba(255, 255, 255, 0.1);\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item.art-current {\n color: var(--art-theme);\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-icon-check {\n visibility: hidden;\n height: 15px;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item.art-current .art-icon-check {\n visibility: visible;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-setting-item-left {\n display: flex;\n justify-content: center;\n align-items: center;\n flex-shrink: 0;\n gap: 5px;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-setting-item-left .art-setting-item-left-icon {\n display: flex;\n justify-content: center;\n align-items: center;\n height: var(--art-settings-icon-size);\n width: var(--art-settings-icon-size);\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-setting-item-right {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 5px;\n font-size: 12px;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-setting-item-right .art-setting-item-right-tooltip {\n white-space: nowrap;\n color: rgba(255, 255, 255, 0.5);\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-setting-item-right .art-setting-item-right-icon {\n display: flex;\n justify-content: center;\n align-items: center;\n min-width: 32px;\n height: 24px;\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item .art-setting-item-right .art-setting-range {\n height: 3px;\n width: 80px;\n outline: none;\n appearance: none;\n background-color: rgba(255, 255, 255, 0.2);\n}\n.art-video-player .art-settings .art-setting-panel .art-setting-item-back {\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.art-video-player.art-setting-show .art-settings {\n display: flex;\n}\n.art-video-player .art-info {\n display: none;\n position: absolute;\n left: var(--art-padding);\n top: var(--art-padding);\n z-index: 100;\n padding: 10px;\n font-size: 12px;\n border-radius: var(--art-border-radius);\n background-color: var(--art-widget-background);\n}\n.art-video-player .art-info .art-info-panel {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.art-video-player .art-info .art-info-panel .art-info-item {\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.art-video-player .art-info .art-info-panel .art-info-item .art-info-title {\n width: 100px;\n text-align: right;\n}\n.art-video-player .art-info .art-info-panel .art-info-item .art-info-content {\n width: 250px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n user-select: all;\n}\n.art-video-player .art-info .art-info-close {\n position: absolute;\n top: 5px;\n right: 5px;\n cursor: pointer;\n}\n.art-video-player.art-info-show .art-info {\n display: flex;\n}\n.art-hide-cursor * {\n cursor: none !important;\n}\n.art-video-player[data-aspect-ratio] {\n overflow: hidden;\n}\n.art-video-player[data-aspect-ratio] .art-video {\n object-fit: fill;\n box-sizing: content-box;\n}\n.art-fullscreen {\n --art-progress-height: 8px;\n --art-indicator-size: 20px;\n --art-control-height: 60px;\n --art-control-icon-scale: 1.3;\n}\n.art-fullscreen-web {\n --art-progress-height: 8px;\n --art-indicator-size: 20px;\n --art-control-height: 60px;\n --art-control-icon-scale: 1.3;\n position: fixed;\n inset: 0;\n z-index: var(--art-fullscreen-web-index);\n width: 100%;\n height: 100%;\n}\n.art-mini-popup {\n position: fixed;\n z-index: 9999;\n width: 320px;\n height: 180px;\n background: #000;\n border-radius: var(--art-border-radius);\n cursor: move;\n user-select: none;\n overflow: hidden;\n transition: opacity 0.2s ease;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);\n}\n.art-mini-popup svg {\n fill: #fff;\n}\n.art-mini-popup .art-video {\n pointer-events: none;\n}\n.art-mini-popup .art-mini-close {\n position: absolute;\n z-index: 20;\n right: 10px;\n top: 10px;\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.art-mini-popup .art-mini-state {\n position: absolute;\n inset: 0;\n z-index: 30;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s ease;\n background-color: rgba(0, 0, 0, 0.25);\n}\n.art-mini-popup .art-mini-state .art-icon {\n opacity: 0.75;\n cursor: pointer;\n transform: scale(3);\n pointer-events: auto;\n transition: transform 0.2s ease;\n}\n.art-mini-popup .art-mini-state .art-icon:active {\n transform: scale(2.5);\n}\n.art-mini-popup.art-mini-dragging {\n opacity: 0.9;\n}\n.art-mini-popup:hover .art-mini-close,\n.art-mini-popup:hover .art-mini-state {\n opacity: 1;\n}\n.art-video-player[data-flip='horizontal'] .art-video {\n transform: scaleX(-1);\n}\n.art-video-player[data-flip='vertical'] .art-video {\n transform: scaleY(-1);\n}\n.art-video-player .art-layer-lock {\n display: none;\n justify-content: center;\n align-items: center;\n position: absolute;\n top: 50%;\n border-radius: 50%;\n transform: translateY(-50%);\n height: var(--art-lock-size);\n width: var(--art-lock-size);\n left: var(--art-padding);\n background-color: var(--art-tip-background);\n}\n.art-video-player .art-layer-auto-playback {\n display: none;\n gap: 10px;\n align-items: center;\n position: absolute;\n border-radius: var(--art-border-radius);\n padding: 10px;\n line-height: 1;\n left: var(--art-padding);\n bottom: calc(var(--art-control-height) + var(--art-bottom-gap) + 10px);\n background-color: var(--art-widget-background);\n}\n.art-video-player .art-layer-auto-playback .art-auto-playback-close {\n display: flex;\n justify-content: center;\n align-items: center;\n cursor: pointer;\n}\n.art-video-player .art-layer-auto-playback .art-auto-playback-close svg {\n width: 15px;\n height: 15px;\n fill: var(--art-theme);\n}\n.art-video-player .art-layer-auto-playback .art-auto-playback-jump {\n color: var(--art-theme);\n cursor: pointer;\n}\n.art-video-player.art-lock .art-subtitle {\n bottom: var(--art-subtitle-bottom) !important;\n}\n.art-video-player.art-mini-progress-bar .art-bottom,\n.art-video-player.art-lock .art-bottom {\n opacity: 1;\n padding: 0;\n background-image: none;\n}\n.art-video-player.art-mini-progress-bar .art-bottom .art-controls,\n.art-video-player.art-lock .art-bottom .art-controls,\n.art-video-player.art-mini-progress-bar .art-bottom .art-progress,\n.art-video-player.art-lock .art-bottom .art-progress {\n transform: translateY(calc(var(--art-control-height) + var(--art-bottom-gap) + var(--art-progress-height) / 4));\n}\n.art-video-player.art-mini-progress-bar .art-bottom .art-progress-indicator,\n.art-video-player.art-lock .art-bottom .art-progress-indicator {\n display: none !important;\n}\n.art-video-player.art-control-show .art-layer-lock {\n display: flex;\n}\n.art-control-selector {\n position: relative;\n display: flex;\n justify-content: center;\n}\n.art-control-selector .art-selector-list {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n position: absolute;\n border-radius: var(--art-border-radius);\n overflow-y: auto;\n overflow-x: hidden;\n opacity: 0;\n transform: translateY(10px);\n pointer-events: none;\n bottom: var(--art-control-height);\n max-height: var(--art-selector-max-height);\n background-color: var(--art-widget-background);\n transition: all var(--art-transition-duration) ease;\n}\n.art-control-selector .art-selector-list .art-selector-item {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 100%;\n padding: 10px 15px;\n flex-shrink: 0;\n line-height: 1;\n}\n.art-control-selector .art-selector-list .art-selector-item:hover {\n background-color: rgba(255, 255, 255, 0.1);\n}\n.art-control-selector .art-selector-list .art-selector-item:hover,\n.art-control-selector .art-selector-list .art-selector-item.art-current {\n color: var(--art-theme);\n}\n.art-control-selector:hover .art-selector-list {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n.art-video-player {\n /*! Hint.css - v2.7.0 - 2021-10-01\n * https://kushagra.dev/lab/hint/\n * Copyright (c) 2021 Kushagra Gour */\n /*-------------------------------------*\\\n HINT.css - A CSS tooltip library\n \\*-------------------------------------*/\n /**\n * HINT.css is a tooltip library made in pure CSS.\n *\n * Source: https://github.com/chinchang/hint.css\n * Demo: http://kushagragour.in/lab/hint/\n *\n */\n /**\n * source: hint-core.scss\n *\n * Defines the basic styling for the tooltip.\n * Each tooltip is made of 2 parts:\n * 1) body (:after)\n * 2) arrow (:before)\n *\n * Classes added:\n * 1) hint\n */\n /**\n * source: hint-position.scss\n *\n * Defines the positoning logic for the tooltips.\n *\n * Classes added:\n * 1) hint--top\n * 2) hint--bottom\n * 3) hint--left\n * 4) hint--right\n */\n /**\n * set default color for tooltip arrows\n */\n /**\n * top tooltip\n */\n /**\n * bottom tooltip\n */\n /**\n * right tooltip\n */\n /**\n * left tooltip\n */\n /**\n * top-left tooltip\n */\n /**\n * top-right tooltip\n */\n /**\n * bottom-left tooltip\n */\n /**\n * bottom-right tooltip\n */\n /**\n * source: hint-sizes.scss\n *\n * Defines width restricted tooltips that can span\n * across multiple lines.\n *\n * Classes added:\n * 1) hint--small\n * 2) hint--medium\n * 3) hint--large\n *\n */\n /**\n * source: hint-theme.scss\n *\n * Defines basic theme for tooltips.\n *\n */\n /**\n * source: hint-color-types.scss\n *\n * Contains tooltips of various types based on color differences.\n *\n * Classes added:\n * 1) hint--error\n * 2) hint--warning\n * 3) hint--info\n * 4) hint--success\n *\n */\n /**\n * Error\n */\n /**\n * Warning\n */\n /**\n * Info\n */\n /**\n * Success\n */\n /**\n * source: hint-always.scss\n *\n * Defines a persisted tooltip which shows always.\n *\n * Classes added:\n * 1) hint--always\n *\n */\n /**\n * source: hint-rounded.scss\n *\n * Defines rounded corner tooltips.\n *\n * Classes added:\n * 1) hint--rounded\n *\n */\n /**\n * source: hint-effects.scss\n *\n * Defines various transition effects for the tooltips.\n *\n * Classes added:\n * 1) hint--no-animate\n * 2) hint--bounce\n *\n */\n}\n.art-video-player [class*='hint--'] {\n position: relative;\n display: inline-block;\n font-style: normal;\n /**\n * tooltip arrow\n */\n /**\n * tooltip body\n */\n}\n.art-video-player [class*='hint--']:before,\n.art-video-player [class*='hint--']:after {\n position: absolute;\n -webkit-transform: translate3d(0, 0, 0);\n -moz-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n visibility: hidden;\n opacity: 0;\n z-index: 1000000;\n pointer-events: none;\n -webkit-transition: 0.3s ease;\n -moz-transition: 0.3s ease;\n transition: 0.3s ease;\n -webkit-transition-delay: 0ms;\n -moz-transition-delay: 0ms;\n transition-delay: 0ms;\n}\n.art-video-player [class*='hint--']:hover:before,\n.art-video-player [class*='hint--']:hover:after {\n visibility: visible;\n opacity: 1;\n}\n.art-video-player [class*='hint--']:hover:before,\n.art-video-player [class*='hint--']:hover:after {\n -webkit-transition-delay: 100ms;\n -moz-transition-delay: 100ms;\n transition-delay: 100ms;\n}\n.art-video-player [class*='hint--']:before {\n content: '';\n position: absolute;\n background: transparent;\n border: 6px solid transparent;\n z-index: 1000001;\n}\n.art-video-player [class*='hint--']:after {\n background: #000000;\n color: white;\n padding: 8px 10px;\n font-size: 12px;\n font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;\n line-height: 12px;\n white-space: nowrap;\n}\n.art-video-player [class*='hint--'][aria-label]:after {\n content: attr(aria-label);\n}\n.art-video-player [class*='hint--'][data-hint]:after {\n content: attr(data-hint);\n}\n.art-video-player [aria-label='']:before,\n.art-video-player [aria-label='']:after,\n.art-video-player [data-hint='']:before,\n.art-video-player [data-hint='']:after {\n display: none !important;\n}\n.art-video-player .hint--top-left:before {\n border-top-color: #000000;\n}\n.art-video-player .hint--top-right:before {\n border-top-color: #000000;\n}\n.art-video-player .hint--top:before {\n border-top-color: #000000;\n}\n.art-video-player .hint--bottom-left:before {\n border-bottom-color: #000000;\n}\n.art-video-player .hint--bottom-right:before {\n border-bottom-color: #000000;\n}\n.art-video-player .hint--bottom:before {\n border-bottom-color: #000000;\n}\n.art-video-player .hint--left:before {\n border-left-color: #000000;\n}\n.art-video-player .hint--right:before {\n border-right-color: #000000;\n}\n.art-video-player .hint--top:before {\n margin-bottom: -11px;\n}\n.art-video-player .hint--top:before,\n.art-video-player .hint--top:after {\n bottom: 100%;\n left: 50%;\n}\n.art-video-player .hint--top:before {\n left: calc(50% - 6px);\n}\n.art-video-player .hint--top:after {\n -webkit-transform: translateX(-50%);\n -moz-transform: translateX(-50%);\n transform: translateX(-50%);\n}\n.art-video-player .hint--top:hover:before {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--top:hover:after {\n -webkit-transform: translateX(-50%) translateY(-8px);\n -moz-transform: translateX(-50%) translateY(-8px);\n transform: translateX(-50%) translateY(-8px);\n}\n.art-video-player .hint--bottom:before {\n margin-top: -11px;\n}\n.art-video-player .hint--bottom:before,\n.art-video-player .hint--bottom:after {\n top: 100%;\n left: 50%;\n}\n.art-video-player .hint--bottom:before {\n left: calc(50% - 6px);\n}\n.art-video-player .hint--bottom:after {\n -webkit-transform: translateX(-50%);\n -moz-transform: translateX(-50%);\n transform: translateX(-50%);\n}\n.art-video-player .hint--bottom:hover:before {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--bottom:hover:after {\n -webkit-transform: translateX(-50%) translateY(8px);\n -moz-transform: translateX(-50%) translateY(8px);\n transform: translateX(-50%) translateY(8px);\n}\n.art-video-player .hint--right:before {\n margin-left: -11px;\n margin-bottom: -6px;\n}\n.art-video-player .hint--right:after {\n margin-bottom: -14px;\n}\n.art-video-player .hint--right:before,\n.art-video-player .hint--right:after {\n left: 100%;\n bottom: 50%;\n}\n.art-video-player .hint--right:hover:before {\n -webkit-transform: translateX(8px);\n -moz-transform: translateX(8px);\n transform: translateX(8px);\n}\n.art-video-player .hint--right:hover:after {\n -webkit-transform: translateX(8px);\n -moz-transform: translateX(8px);\n transform: translateX(8px);\n}\n.art-video-player .hint--left:before {\n margin-right: -11px;\n margin-bottom: -6px;\n}\n.art-video-player .hint--left:after {\n margin-bottom: -14px;\n}\n.art-video-player .hint--left:before,\n.art-video-player .hint--left:after {\n right: 100%;\n bottom: 50%;\n}\n.art-video-player .hint--left:hover:before {\n -webkit-transform: translateX(-8px);\n -moz-transform: translateX(-8px);\n transform: translateX(-8px);\n}\n.art-video-player .hint--left:hover:after {\n -webkit-transform: translateX(-8px);\n -moz-transform: translateX(-8px);\n transform: translateX(-8px);\n}\n.art-video-player .hint--top-left:before {\n margin-bottom: -11px;\n}\n.art-video-player .hint--top-left:before,\n.art-video-player .hint--top-left:after {\n bottom: 100%;\n left: 50%;\n}\n.art-video-player .hint--top-left:before {\n left: calc(50% - 6px);\n}\n.art-video-player .hint--top-left:after {\n -webkit-transform: translateX(-100%);\n -moz-transform: translateX(-100%);\n transform: translateX(-100%);\n}\n.art-video-player .hint--top-left:after {\n margin-left: 12px;\n}\n.art-video-player .hint--top-left:hover:before {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--top-left:hover:after {\n -webkit-transform: translateX(-100%) translateY(-8px);\n -moz-transform: translateX(-100%) translateY(-8px);\n transform: translateX(-100%) translateY(-8px);\n}\n.art-video-player .hint--top-right:before {\n margin-bottom: -11px;\n}\n.art-video-player .hint--top-right:before,\n.art-video-player .hint--top-right:after {\n bottom: 100%;\n left: 50%;\n}\n.art-video-player .hint--top-right:before {\n left: calc(50% - 6px);\n}\n.art-video-player .hint--top-right:after {\n -webkit-transform: translateX(0);\n -moz-transform: translateX(0);\n transform: translateX(0);\n}\n.art-video-player .hint--top-right:after {\n margin-left: -12px;\n}\n.art-video-player .hint--top-right:hover:before {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--top-right:hover:after {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--bottom-left:before {\n margin-top: -11px;\n}\n.art-video-player .hint--bottom-left:before,\n.art-video-player .hint--bottom-left:after {\n top: 100%;\n left: 50%;\n}\n.art-video-player .hint--bottom-left:before {\n left: calc(50% - 6px);\n}\n.art-video-player .hint--bottom-left:after {\n -webkit-transform: translateX(-100%);\n -moz-transform: translateX(-100%);\n transform: translateX(-100%);\n}\n.art-video-player .hint--bottom-left:after {\n margin-left: 12px;\n}\n.art-video-player .hint--bottom-left:hover:before {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--bottom-left:hover:after {\n -webkit-transform: translateX(-100%) translateY(8px);\n -moz-transform: translateX(-100%) translateY(8px);\n transform: translateX(-100%) translateY(8px);\n}\n.art-video-player .hint--bottom-right:before {\n margin-top: -11px;\n}\n.art-video-player .hint--bottom-right:before,\n.art-video-player .hint--bottom-right:after {\n top: 100%;\n left: 50%;\n}\n.art-video-player .hint--bottom-right:before {\n left: calc(50% - 6px);\n}\n.art-video-player .hint--bottom-right:after {\n -webkit-transform: translateX(0);\n -moz-transform: translateX(0);\n transform: translateX(0);\n}\n.art-video-player .hint--bottom-right:after {\n margin-left: -12px;\n}\n.art-video-player .hint--bottom-right:hover:before {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--bottom-right:hover:after {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--small:after,\n.art-video-player .hint--medium:after,\n.art-video-player .hint--large:after {\n white-space: normal;\n line-height: 1.4em;\n word-wrap: break-word;\n}\n.art-video-player .hint--small:after {\n width: 80px;\n}\n.art-video-player .hint--medium:after {\n width: 150px;\n}\n.art-video-player .hint--large:after {\n width: 300px;\n}\n.art-video-player [class*='hint--'] {\n /**\n * tooltip body\n */\n}\n.art-video-player [class*='hint--']:after {\n text-shadow: 0 -1px 0px black;\n box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3);\n}\n.art-video-player .hint--error:after {\n background-color: #b34e4d;\n text-shadow: 0 -1px 0px #592726;\n}\n.art-video-player .hint--error.hint--top-left:before {\n border-top-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--top-right:before {\n border-top-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--top:before {\n border-top-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--bottom-left:before {\n border-bottom-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--bottom-right:before {\n border-bottom-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--bottom:before {\n border-bottom-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--left:before {\n border-left-color: #b34e4d;\n}\n.art-video-player .hint--error.hint--right:before {\n border-right-color: #b34e4d;\n}\n.art-video-player .hint--warning:after {\n background-color: #c09854;\n text-shadow: 0 -1px 0px #6c5328;\n}\n.art-video-player .hint--warning.hint--top-left:before {\n border-top-color: #c09854;\n}\n.art-video-player .hint--warning.hint--top-right:before {\n border-top-color: #c09854;\n}\n.art-video-player .hint--warning.hint--top:before {\n border-top-color: #c09854;\n}\n.art-video-player .hint--warning.hint--bottom-left:before {\n border-bottom-color: #c09854;\n}\n.art-video-player .hint--warning.hint--bottom-right:before {\n border-bottom-color: #c09854;\n}\n.art-video-player .hint--warning.hint--bottom:before {\n border-bottom-color: #c09854;\n}\n.art-video-player .hint--warning.hint--left:before {\n border-left-color: #c09854;\n}\n.art-video-player .hint--warning.hint--right:before {\n border-right-color: #c09854;\n}\n.art-video-player .hint--info:after {\n background-color: #3986ac;\n text-shadow: 0 -1px 0px #1a3c4d;\n}\n.art-video-player .hint--info.hint--top-left:before {\n border-top-color: #3986ac;\n}\n.art-video-player .hint--info.hint--top-right:before {\n border-top-color: #3986ac;\n}\n.art-video-player .hint--info.hint--top:before {\n border-top-color: #3986ac;\n}\n.art-video-player .hint--info.hint--bottom-left:before {\n border-bottom-color: #3986ac;\n}\n.art-video-player .hint--info.hint--bottom-right:before {\n border-bottom-color: #3986ac;\n}\n.art-video-player .hint--info.hint--bottom:before {\n border-bottom-color: #3986ac;\n}\n.art-video-player .hint--info.hint--left:before {\n border-left-color: #3986ac;\n}\n.art-video-player .hint--info.hint--right:before {\n border-right-color: #3986ac;\n}\n.art-video-player .hint--success:after {\n background-color: #458746;\n text-shadow: 0 -1px 0px #1a321a;\n}\n.art-video-player .hint--success.hint--top-left:before {\n border-top-color: #458746;\n}\n.art-video-player .hint--success.hint--top-right:before {\n border-top-color: #458746;\n}\n.art-video-player .hint--success.hint--top:before {\n border-top-color: #458746;\n}\n.art-video-player .hint--success.hint--bottom-left:before {\n border-bottom-color: #458746;\n}\n.art-video-player .hint--success.hint--bottom-right:before {\n border-bottom-color: #458746;\n}\n.art-video-player .hint--success.hint--bottom:before {\n border-bottom-color: #458746;\n}\n.art-video-player .hint--success.hint--left:before {\n border-left-color: #458746;\n}\n.art-video-player .hint--success.hint--right:before {\n border-right-color: #458746;\n}\n.art-video-player .hint--always:after,\n.art-video-player .hint--always:before {\n opacity: 1;\n visibility: visible;\n}\n.art-video-player .hint--always.hint--top:before {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--always.hint--top:after {\n -webkit-transform: translateX(-50%) translateY(-8px);\n -moz-transform: translateX(-50%) translateY(-8px);\n transform: translateX(-50%) translateY(-8px);\n}\n.art-video-player .hint--always.hint--top-left:before {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--always.hint--top-left:after {\n -webkit-transform: translateX(-100%) translateY(-8px);\n -moz-transform: translateX(-100%) translateY(-8px);\n transform: translateX(-100%) translateY(-8px);\n}\n.art-video-player .hint--always.hint--top-right:before {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--always.hint--top-right:after {\n -webkit-transform: translateY(-8px);\n -moz-transform: translateY(-8px);\n transform: translateY(-8px);\n}\n.art-video-player .hint--always.hint--bottom:before {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--always.hint--bottom:after {\n -webkit-transform: translateX(-50%) translateY(8px);\n -moz-transform: translateX(-50%) translateY(8px);\n transform: translateX(-50%) translateY(8px);\n}\n.art-video-player .hint--always.hint--bottom-left:before {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--always.hint--bottom-left:after {\n -webkit-transform: translateX(-100%) translateY(8px);\n -moz-transform: translateX(-100%) translateY(8px);\n transform: translateX(-100%) translateY(8px);\n}\n.art-video-player .hint--always.hint--bottom-right:before {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--always.hint--bottom-right:after {\n -webkit-transform: translateY(8px);\n -moz-transform: translateY(8px);\n transform: translateY(8px);\n}\n.art-video-player .hint--always.hint--left:before {\n -webkit-transform: translateX(-8px);\n -moz-transform: translateX(-8px);\n transform: translateX(-8px);\n}\n.art-video-player .hint--always.hint--left:after {\n -webkit-transform: translateX(-8px);\n -moz-transform: translateX(-8px);\n transform: translateX(-8px);\n}\n.art-video-player .hint--always.hint--right:before {\n -webkit-transform: translateX(8px);\n -moz-transform: translateX(8px);\n transform: translateX(8px);\n}\n.art-video-player .hint--always.hint--right:after {\n -webkit-transform: translateX(8px);\n -moz-transform: translateX(8px);\n transform: translateX(8px);\n}\n.art-video-player .hint--rounded:after {\n border-radius: 4px;\n}\n.art-video-player .hint--no-animate:before,\n.art-video-player .hint--no-animate:after {\n -webkit-transition-duration: 0ms;\n -moz-transition-duration: 0ms;\n transition-duration: 0ms;\n}\n.art-video-player .hint--bounce:before,\n.art-video-player .hint--bounce:after {\n -webkit-transition: opacity 0.3s ease, visibility 0.3s ease, -webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);\n -moz-transition: opacity 0.3s ease, visibility 0.3s ease, -moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);\n transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);\n}\n.art-video-player .hint--no-shadow:before,\n.art-video-player .hint--no-shadow:after {\n text-shadow: initial;\n box-shadow: initial;\n}\n.art-video-player .hint--no-arrow:before {\n display: none;\n}\n.art-video-player.art-mobile {\n --art-bottom-gap: 10px;\n --art-control-height: 38px;\n --art-control-icon-scale: 1;\n --art-state-size: 60px;\n --art-settings-max-height: 180px;\n --art-selector-max-height: 180px;\n --art-indicator-scale: 1;\n --art-control-opacity: 1;\n}\n.art-video-player.art-mobile .art-controls-left {\n margin-left: calc(var(--art-padding) / -1);\n}\n.art-video-player.art-mobile .art-controls-right {\n margin-right: calc(var(--art-padding) / -1);\n}\n"; class Subtitle extends Component { constructor(art) { super(art); this.name = "subtitle"; this.option = null; this.destroyEvent = () => null; this.init(art.option.subtitle); let lastState = false; art.on("video:timeupdate", () => { if (!this.url) return; const state2 = this.art.template.$video.webkitDisplayingFullscreen; if (typeof state2 !== "boolean") return; if (state2 !== lastState) { lastState = state2; this.createTrack(state2 ? "subtitles" : "metadata", this.url); } }); } get url() { return this.art.template.$track.src; } set url(url) { this.switch(url); } get textTrack() { return this.art.template.$video?.textTracks?.[0]; } get activeCues() { if (!this.textTrack) return []; return Array.from(this.textTrack.activeCues); } get cues() { if (!this.textTrack) return []; return Array.from(this.textTrack.cues); } style(key, value) { const { $subtitle } = this.art.template; if (typeof key === "object") { return setStyles($subtitle, key); } return setStyle($subtitle, key, value); } update() { const { option: { subtitle }, template: { $subtitle } } = this.art; $subtitle.innerHTML = ""; if (!this.activeCues.length) return; this.art.emit("subtitleBeforeUpdate", this.activeCues); $subtitle.innerHTML = this.activeCues.map( (cue, index) => cue.text.split(/\r?\n/).filter((line) => line.trim()).map( (line) => `
${subtitle.escape ? escape(line) : line}
` ).join("") ).join(""); this.art.emit("subtitleAfterUpdate", this.activeCues); } async switch(url, newOption = {}) { const { i18n, notice, option } = this.art; const subtitleOption = { ...option.subtitle, ...newOption, url }; const subUrl = await this.init(subtitleOption); if (newOption.name) { notice.show = `${i18n.get("Switch Subtitle")}: ${newOption.name}`; } return subUrl; } createTrack(kind, url) { const { template, proxy, option } = this.art; const { $video, $track } = template; const $newTrack = createElement("track"); $newTrack.default = true; $newTrack.kind = kind; $newTrack.src = url; $newTrack.label = option.subtitle.name || "Artplayer"; $newTrack.track.mode = "hidden"; $newTrack.onload = () => { this.art.emit("subtitleLoad", this.cues, this.option); }; this.art.events.remove(this.destroyEvent); $track.onload = null; remove($track); append($video, $newTrack); template.$track = $newTrack; this.destroyEvent = proxy(this.textTrack, "cuechange", () => this.update()); } async init(subtitleOption) { const { notice, template: { $subtitle } } = this.art; if (!this.textTrack) return null; validator(subtitleOption, scheme.subtitle); if (!subtitleOption.url) return; this.option = subtitleOption; this.style(subtitleOption.style); return fetch(subtitleOption.url).then((response) => response.arrayBuffer()).then((buffer) => { const decoder = new TextDecoder(subtitleOption.encoding); const text = decoder.decode(buffer); switch (subtitleOption.type || getExt(subtitleOption.url)) { case "srt": { const vtt = srtToVtt(text); const vttNew = subtitleOption.onVttLoad(vtt); return vttToBlob(vttNew); } case "ass": { const vtt = assToVtt(text); const vttNew = subtitleOption.onVttLoad(vtt); return vttToBlob(vttNew); } case "vtt": { const vttNew = subtitleOption.onVttLoad(text); return vttToBlob(vttNew); } default: return subtitleOption.url; } }).then((subUrl) => { $subtitle.innerHTML = ""; if (this.url === subUrl) return subUrl; URL.revokeObjectURL(this.url); this.createTrack("metadata", subUrl); return subUrl; }).catch((err) => { $subtitle.innerHTML = ""; notice.show = err; throw err; }); } } class Template { constructor(art) { this.art = art; const { option, constructor } = art; if (option.container instanceof Element) { this.$container = option.container; } else { this.$container = query(option.container); errorHandle(this.$container, `No container element found by ${option.container}`); } errorHandle(supportsFlex(), "The current browser does not support flex layout"); const type = this.$container.tagName.toLowerCase(); errorHandle(type === "div", `Unsupported container element type, only support 'div' but got '${type}'`); errorHandle( constructor.instances.every((ins) => ins.template.$container !== this.$container), "Cannot mount multiple instances on the same dom element" ); this.query = this.query.bind(this); this.$container.dataset.artId = art.id; this.init(); } static get html() { return `
Player version:
${version$1}
Video url:
Video volume:
Video time:
Video duration:
Video resolution:
x
[x]
`; } query(className) { return query(className, this.$container); } init() { const { option } = this.art; if (!option.useSSR) { this.$container.innerHTML = Template.html; } this.$player = this.query(".art-video-player"); this.$video = this.query(".art-video"); this.$track = this.query("track"); this.$poster = this.query(".art-poster"); this.$subtitle = this.query(".art-subtitle"); this.$danmuku = this.query(".art-danmuku"); this.$bottom = this.query(".art-bottom"); this.$progress = this.query(".art-progress"); this.$controls = this.query(".art-controls"); this.$controlsLeft = this.query(".art-controls-left"); this.$controlsCenter = this.query(".art-controls-center"); this.$controlsRight = this.query(".art-controls-right"); this.$layer = this.query(".art-layers"); this.$loading = this.query(".art-loading"); this.$notice = this.query(".art-notice"); this.$noticeInner = this.query(".art-notice-inner"); this.$mask = this.query(".art-mask"); this.$state = this.query(".art-state"); this.$setting = this.query(".art-settings"); this.$info = this.query(".art-info"); this.$infoPanel = this.query(".art-info-panel"); this.$infoClose = this.query(".art-info-close"); this.$contextmenu = this.query(".art-contextmenus"); if (option.proxy) { const video = option.proxy.call(this.art, this.art); errorHandle( video instanceof HTMLVideoElement || video instanceof HTMLCanvasElement, `Function 'option.proxy' needs to return 'HTMLVideoElement' or 'HTMLCanvasElement'` ); replaceElement(video, this.$video); video.className = "art-video"; this.$video = video; } if (option.backdrop) { addClass(this.$player, "art-backdrop"); } if (isMobile) { addClass(this.$player, "art-mobile"); } } destroy(removeHtml) { if (removeHtml) { this.$container.innerHTML = ""; } else { addClass(this.$player, "art-destroy"); } } } class Emitter { on(name, fn, ctx) { const e = this.e || (this.e = {}); (e[name] || (e[name] = [])).push({ fn, ctx }); return this; } once(name, fn, ctx) { const self = this; function listener(...args) { self.off(name, listener); fn.apply(ctx, args); } listener._ = fn; return this.on(name, listener, ctx); } emit(name, ...data) { const evtArr = ((this.e || (this.e = {}))[name] || []).slice(); for (let i = 0; i < evtArr.length; i += 1) { evtArr[i].fn.apply(evtArr[i].ctx, data); } return this; } off(name, callback) { const e = this.e || (this.e = {}); const evts = e[name]; const liveEvents = []; if (evts && callback) { for (let i = 0, len = evts.length; i < len; i += 1) { if (evts[i].fn !== callback && evts[i].fn._ !== callback) liveEvents.push(evts[i]); } } if (liveEvents.length) { e[name] = liveEvents; } else { delete e[name]; } return this; } } let id = 0; const instances = []; class Artplayer extends Emitter { constructor(option, readyCallback) { super(); if (!isBrowser) { throw new Error("Artplayer can only be used in the browser environment"); } this.id = ++id; const mergeOption = mergeDeep(Artplayer.option, option); mergeOption.container = option.container; this.option = validator(mergeOption, scheme); this.isLock = false; this.isReady = false; this.isFocus = false; this.isInput = false; this.isRotate = false; this.isDestroy = false; this.template = new Template(this); this.events = new Events(this); this.storage = new Storage(this); this.icons = new Icons(this); this.i18n = new I18n(this); this.notice = new Notice(this); this.player = new Player(this); this.layers = new Layer(this); this.controls = new Control(this); this.contextmenu = new Contextmenu(this); this.subtitle = new Subtitle(this); this.info = new Info(this); this.loading = new Loading(this); this.hotkey = new Hotkey(this); this.mask = new Mask(this); this.setting = new Setting(this); this.plugins = new Plugins(this); if (typeof readyCallback === "function") { this.on("ready", () => readyCallback.call(this, this)); } if (Artplayer.DEBUG) { const log = (msg) => console.log(`[ART.${this.id}] -> ${msg}`); log(`Version@${Artplayer.version}`); for (let index = 0; index < config$1.events.length; index++) { this.on(`video:${config$1.events[index]}`, (event) => log(`Event@${event.type}`)); } } instances.push(this); } static get instances() { return instances; } static get version() { return version$1; } static get config() { return config$1; } static get utils() { return utils; } static get scheme() { return scheme; } static get Emitter() { return Emitter; } static get validator() { return validator; } static get kindOf() { return validator.kindOf; } static get html() { return Template.html; } static get option() { return { id: "", container: "#artplayer", url: "", poster: "", type: "", theme: "#f00", volume: 0.7, isLive: false, muted: false, autoplay: false, autoSize: false, autoMini: false, loop: false, flip: false, playbackRate: false, aspectRatio: false, screenshot: false, setting: false, hotkey: true, pip: false, mutex: true, backdrop: true, fullscreen: false, fullscreenWeb: false, subtitleOffset: false, miniProgressBar: false, useSSR: false, playsInline: true, lock: false, gesture: true, fastForward: false, autoPlayback: false, autoOrientation: false, airplay: false, proxy: void 0, layers: [], contextmenu: [], controls: [], settings: [], quality: [], highlight: [], plugins: [], thumbnails: { url: "", number: 60, column: 10, width: 0, height: 0, scale: 1 }, subtitle: { url: "", type: "", style: {}, name: "", escape: true, encoding: "utf-8", onVttLoad: (vtt) => vtt }, moreVideoAttr: { controls: false, preload: isSafari ? "auto" : "metadata" }, i18n: {}, icons: {}, cssVar: {}, customType: {}, lang: navigator?.language.toLowerCase() }; } get proxy() { return this.events.proxy; } get query() { return this.template.query; } get video() { return this.template.$video; } reset() { this.video.removeAttribute("src"); this.video.load(); } destroy(removeHtml = true) { if (Artplayer.REMOVE_SRC_WHEN_DESTROY) { this.reset(); } this.events.destroy(); this.template.destroy(removeHtml); instances.splice(instances.indexOf(this), 1); this.isDestroy = true; this.emit("destroy"); } } Artplayer.STYLE = style; Artplayer.DEBUG = false; Artplayer.CONTEXTMENU = true; Artplayer.NOTICE_TIME = 2e3; Artplayer.SETTING_WIDTH = 250; Artplayer.SETTING_ITEM_WIDTH = 200; Artplayer.SETTING_ITEM_HEIGHT = 35; Artplayer.RESIZE_TIME = 200; Artplayer.SCROLL_TIME = 200; Artplayer.SCROLL_GAP = 50; Artplayer.AUTO_PLAYBACK_MAX = 10; Artplayer.AUTO_PLAYBACK_MIN = 5; Artplayer.AUTO_PLAYBACK_TIMEOUT = 3e3; Artplayer.RECONNECT_TIME_MAX = 5; Artplayer.RECONNECT_SLEEP_TIME = 1e3; Artplayer.CONTROL_HIDE_TIME = 3e3; Artplayer.DBCLICK_TIME = 300; Artplayer.DBCLICK_FULLSCREEN = true; Artplayer.MOBILE_DBCLICK_PLAY = true; Artplayer.MOBILE_CLICK_PLAY = false; Artplayer.AUTO_ORIENTATION_TIME = 200; Artplayer.INFO_LOOP_TIME = 1e3; Artplayer.FAST_FORWARD_VALUE = 3; Artplayer.FAST_FORWARD_TIME = 1e3; Artplayer.TOUCH_MOVE_RATIO = 0.5; Artplayer.VOLUME_STEP = 0.1; Artplayer.SEEK_STEP = 5; Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2]; Artplayer.ASPECT_RATIO = ["default", "4:3", "16:9"]; Artplayer.FLIP = ["normal", "horizontal", "vertical"]; Artplayer.FULLSCREEN_WEB_IN_BODY = true; Artplayer.LOG_VERSION = true; Artplayer.USE_RAF = false; Artplayer.REMOVE_SRC_WHEN_DESTROY = true; if (isBrowser) { window.Artplayer = Artplayer; setStyleText("artplayer-style", style); setTimeout(() => { if (Artplayer.LOG_VERSION) { console.log( `%c ArtPlayer %c ${Artplayer.version} %c https://artplayer.org`, "color: #fff; background: #5f5f5f", "color: #fff; background: #4bc729", "" ); } }, 100); } export { Artplayer as default };