视频网页全屏(改)

让所有视频网页全屏,开启画中画功能,支持自定义按钮位置。

当前为 2025-10-15 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                视频网页全屏(改)
// @name:en             Maximize Video(Modify)
// @name:zh-CN          视频网页全屏(改)
// @name:zh-TW          視頻網頁全屏(改)
// @name:ja             ビデオページ全画面(変更)
// @name:ko             비디오 웹페이지 전체화면(수정)
// @namespace           https://greasyfork.org/zh-CN/users/178351-yesilin
// @description         让所有视频网页全屏,开启画中画功能,支持自定义按钮位置。
// @description:en      Maximize all video players.Support Piture-in-picture and custom button position.
// @description:zh-CN   让所有视频网页全屏,开启画中画功能,支持自定义按钮位置。
// @description:zh-TW   讓所有視頻網頁全屏,開啟子母畫面,支援自定義按鈕位置。
// @description:ja      すべての動画ページを全画面表示し、ピクチャ・イン・ピクチャ機能を有効にします。ボタン位置のカスタマイズにも対応しています。
// @description:ko      모든 비디오 웹페이지를 전체화면으로 전환하고, PIP(화면 속 화면) 기능과 사용자 지정 버튼 위치를 지원합니다.
// @author              冻猫, RyomaHan, YeSilin
// @include             *
// @exclude             *www.w3school.com.cn*
// @version             12.5.48
// @run-at              document-end
// @license             MIT
// @grant               GM_setValue
// @grant               GM_getValue
// @icon                data:image/svg+xml;base64,PHN2ZyBpZD0i5Zu+5bGCXzEiIGRhdGEtbmFtZT0i5Zu+5bGCIDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIwMCAxNTIuNiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiMyMzE4MTU7fS5jbHMtM3tmaWxsOiMwNDAwMDA7fS5jbHMtNHtmaWxsOiNmNWM5MDA7fTwvc3R5bGU+PC9kZWZzPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE3MCwxNzEuNDZIMjguMzhhOC42Miw4LjYyLDAsMCwxLTguNi04LjU5Vjc1LjA3QTIxLjU1LDIxLjU1LDAsMCwxLDQxLjI3LDUzLjU4SDE1Ny4xMmEyMS41NSwyMS41NSwwLDAsMSwyMS40OSwyMS40OXY4Ny44YTguNjIsOC42MiwwLDAsMS04LjYsOC41OVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTIzLjcpIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMTczLjEzLDE3Ni4zSDI3YTEyLjI0LDEyLjI0LDAsMCwxLTEyLjI1LTEyLjI1Vjc2LjM2QTI2Ljg3LDI2Ljg3LDAsMCwxLDQxLjU5LDQ5LjQ5SDE1OC40MWEyNi44NywyNi44NywwLDAsMSwyNi44NiwyNi44N3Y4Ny42OWExMi4wOCwxMi4wOCwwLDAsMS0xMi4xNCwxMi4yNVpNNDEuNTksNTYuMjZBMjAuMTQsMjAuMTQsMCwwLDAsMjEuNSw3Ni4zNnY4Ny42OUE1LjUsNS41LDAsMCwwLDI3LDE2OS41M0gxNzMuMTNhNS41LDUuNSwwLDAsMCw1LjQ4LTUuNDhWNzYuMzZhMjAuMTQsMjAuMTQsMCwwLDAtMjAuMS0yMC4xWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjMuNykiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik02OC44OSwxMjEuMjhIMy4xMkEzLjEyLDMuMTIsMCwwLDEsMCwxMTguMTYsMywzLDAsMCwxLDMuMTIsMTE1SDY4Ljg5YTMuMTIsMy4xMiwwLDEsMSwwLDYuMjRabTEyOCwwaC02Ni4yYTMuMTIsMy4xMiwwLDEsMSwwLTYuMjRoNjYuMmEzLjEyLDMuMTIsMCwwLDEsMy4xMSwzLjEyQTMsMywwLDAsMSwxOTYuODgsMTIxLjI4Wm0tMTI4LDE1LjM2aC0zNmEzLjEyLDMuMTIsMCwxLDEsMC02LjIzaDM2YTMuMTIsMy4xMiwwLDEsMSwwLDYuMjNabTk4LDBIMTMwLjY4YTMuMTIsMy4xMiwwLDEsMSwwLTYuMjNIMTY2LjlhMy4xMiwzLjEyLDAsMCwxLDMuMTEsMy4xMkEzLDMsMCwwLDEsMTY2LjksMTM2LjY0Wk05MS4xMywxMjQuMDdhMTAsMTAsMCwwLDEtOC43LTUsMS4zMywxLjMzLDAsMSwxLDIuMjYtMS40LDcuNDgsNy40OCwwLDAsMCwxNC0zLjc2LDEuMjksMS4yOSwwLDEsMSwyLjU4LDAsMTAuMTgsMTAuMTgsMCwwLDEtMTAuMTEsMTAuMjFaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yMy43KSIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTEwOC42NSwxMjQuMDdBMTAuMDgsMTAuMDgsMCwwLDEsOTguNTUsMTE0YTEuMjksMS4yOSwwLDEsMSwyLjU4LDAsNy41NCw3LjU0LDAsMCwwLDcuNTIsNy41Miw3LjI3LDcuMjcsMCwwLDAsNi40NS0zLjc2LDEuMzMsMS4zMywwLDEsMSwyLjI2LDEuNCwxMC4xNSwxMC4xNSwwLDAsMS04LjcxLDQuOTRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yMy43KSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTYyLjMzLDk4LjkzYTguNzEsOC43MSwwLDAsMCwxNy40MSwwaDBhOC43MSw4LjcxLDAsMCwwLTE3LjQxLDBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yMy43KSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTEyMC4zNiw5OC45M2E4LjcxLDguNzEsMCwxLDAsOC43MS04LjcxQTguNzEsOC43MSwwLDAsMCwxMjAuMzYsOTguOTNaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yMy43KSIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTEwMC4wNSw3Mi45MmEzLjYyLDMuNjIsMCwwLDEtMy42NS0zLjY1VjYxLjg1YTMuNjYsMy42NiwwLDAsMSw3LjMxLDB2Ny40MkEzLjYyLDMuNjIsMCwwLDEsMTAwLjA1LDcyLjkyWm0tMTQsMGEzLjYyLDMuNjIsMCwwLDEtMy42NS0zLjY1VjYxLjg1YTMuNjYsMy42NiwwLDAsMSw3LjMxLDB2Ny40MkEzLjYyLDMuNjIsMCwwLDEsODYuMDgsNzIuOTJabTI3Ljk0LDBhMy42MiwzLjYyLDAsMCwxLTMuNjUtMy42NVY2MS44NWEzLjY2LDMuNjYsMCwwLDEsNy4zMSwwdjcuNDJBMy42MiwzLjYyLDAsMCwxLDExNCw3Mi45MloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTIzLjcpIi8+PHBhdGggY2xhc3M9ImNscy00IiBkPSJNMzkuMjMsNTIuODJzNC4wOC0yNS44OSwxOC0yNS44OVM3NS4xMiw1Mi44Miw3NS4xMiw1Mi44MloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTIzLjcpIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNzUuMTIsNTUuOTRIMzkuMjNhMy4wNywzLjA3LDAsMCwxLTIuMzYtMS4wNywzLjE0LDMuMTQsMCwwLDEtLjc2LTIuNThjLjIyLTEuMTksNC43My0yOC41OSwyMS4wNy0yOC41OVM3OCw1MS4xLDc4LjI0LDUyLjI5YTMuMzUsMy4zNSwwLDAsMS0uNzUsMi41OEEzLjEsMy4xLDAsMCwxLDc1LjEyLDU1Ljk0Wm0tMzItNi4yM0g3MS4yNUM2OS40Myw0Mi4wOCw2NC45MSwzMCw1Ny4xOCwzMCw0OSwzMCw0NC43MSw0My4xNSw0My4xLDQ5LjcxWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjMuNykiLz48cGF0aCBjbGFzcz0iY2xzLTQiIGQ9Ik0xMjQuNjYsNTIuODJzNC4wOS0yNS44OSwxOC0yNS44OSwxNy45NSwyNS44OSwxNy45NSwyNS44OVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTIzLjcpIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMTYwLjQ1LDU1Ljk0SDEyNC41NmEzLjE2LDMuMTYsMCwwLDEtMy4xMi0zLjY1Yy4yMS0xLjE5LDQuNzMtMjguNTksMjEuMDYtMjguNTlzMjAuODUsMjcuNCwyMS4wNiwyOC41OWEzLjMxLDMuMzEsMCwwLDEtLjc1LDIuNThBMy4wNywzLjA3LDAsMCwxLDE2MC40NSw1NS45NFptLTMxLjkyLTYuMjNoMjguMTZDMTU0Ljg2LDQyLjA4LDE1MC4zNSwzMCwxNDIuNjEsMzAsMTM0LjMzLDMwLDEzMC4xNCw0My4xNSwxMjguNTMsNDkuNzFaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yMy43KSIvPjwvc3ZnPg==
// ==/UserScript==

(() => {
  // 全局变量存储对象 (global variables)
  const gv = {
    // 状态标记类
    isFull: false, // 是否处于全屏状态
    isIframe: false, // 当前页面是否在 iframe 中
    useCssFullscreen: false, // 是否使用了 CSS 注入的网页全屏方式
    ytbStageChange: false, // YouTube 舞台模式切换标记
    autoCheckCount: 0, // 自动检测计数器

    // 播放器相关
    player: null, // 当前激活的视频播放器元素
    playerChilds: [], // 播放器子元素列表
    playerParents: [], // 播放器父元素列表
    backControls: null, // 全屏前视频控件状态
    restoreClick: null, // 恢复视频默认单击行为

    // 页面结构备份
    backHtmlId: "", // 全屏前 html 元素的 ID
    backBodyId: "", // 全屏前 body 元素的 ID

    // 滚动与交互状态
    scrollTop: 0, // 保存进入全屏前的垂直滚动位置
    scrollLeft: 0, // 保存进入全屏前的水平滚动位置
    scrollFixTimer: null, // 滚动修正定时器
    mouseoverEl: null, // 鼠标悬停元素

    // 按钮配置与元素
    btnText: {}, // 按钮文本(多语言支持)
    btnPosition: GM_getValue("buttonPosition", "top-right"), // 按钮位置,默认右上角
    picinpicBtn: null, // 画中画按钮
    controlBtn: null, // 网页全屏按钮
    leftBtn: null, // 左侧边缘退出网页全屏按钮
    rightBtn: null, // 右侧边缘退出网页全屏按钮

    // 界面元素
    contextMenu: null, // 右键菜单元素
  };

  // Html5播放器规则[播放器最外层],适用于无法自动识别的自适应大小HTML5播放器
  // 键为域名,值为该域名下播放器元素的选择器对象
  const html5Rules = {
    "www.bilibili.com": {
      player: ["#bilibiliPlayer"],
      fullscreen: [".bpx-player-ctrl-web"],
      pip: [".bpx-player-ctrl-pip"],
    },
    "v.qq.com": {
      player: ["#player-container"],
      fullscreen: [".txp_btn_fake"],
      pip: [".txp_btn_pip"],
    },
    "www.youtube.com": {
      player: ["#ytd-player"],
    },
    "www.twitch.tv": {
      player: [".player"],
    },
    "www.huya.com": {
      player: ["#videoContainer"],
    },
    "www.douyu.com": {
      player: ["#js-player-video-case"],
    },
    "www.acfun.cn": {
      player: ["#ACPlayer"],
      fullscreen: [".fullscreen-web"],
    },
    "www.miguvideo.com": {
      player: ["#mod-player"],
    },
    "www.yy.com": {
      player: ["#player"],
    },
    "v.huya.com": {
      player: ["#video_embed_flash>div"],
    },
    "*weibo.com": {
      player: ['[aria-label="Video Player"]', ".html5-video-live .html5-video", ".FeedPlayer_feedVideo_39PLs"],
      fullscreen: ["#videoFull"],
    },
  };

  // 通用html5播放器选择器,用于匹配常见的视频播放器类名
  const generalPlayerRules = [
    ".dplayer",
    ".video-js",
    ".jwplayer",
    "[data-player]",
    ".art-video-player", // Artplayer.js
  ];

  // 判断当前页面是否在iframe中
  if (window.top !== window.self) {
    gv.isIframe = true;
  }

  // 根据浏览器语言设置按钮文本
  if (navigator.language.toLocaleLowerCase() == "zh-cn") {
    gv.btnText = {
      max: "网页全屏",
      maxTooltip: "切换网页全屏(ESC),右键选择按钮位置", // 悬浮提示
      pip: "画中画",
      pipTooltip: "切换画中画(F2),右键选择按钮位置", // 悬浮提示
      tip: "Iframe内视频,请用鼠标点击视频后重试",
      menuTitle: "选择按钮位置",
      topLeft: "左上角",
      topRight: "右上角",
    };
  } else {
    gv.btnText = {
      max: "Maximize",
      maxTooltip: "Toggle fullscreen (ESC). Right-click to choose button position", // 悬浮提示
      pip: "PicInPic",
      pipTooltip: "Toggle Picture-in-Picture (F2). Right-click to choose button position", // 悬浮提示
      tip: "Iframe video. Please click on the video and try again",
      menuTitle: "Choose Button Position",
      topLeft: "Top Left",
      topRight: "Top Right",
    };
  }

  // 工具函数集合
  const tool = {
    /**
     * 带时间戳的日志打印
     * @param {string} log - 日志内容
     */
    print(log) {
      const now = new Date();
      const format = (n) => String(n).padStart(2, "0");
      const timenow = `[${now.getFullYear()}-${format(now.getMonth() + 1)}-${format(now.getDate())} ${format(
        now.getHours()
      )}:${format(now.getMinutes())}:${format(now.getSeconds())}]`;
      console.log(`${timenow}[Maximize Video(Modify)] >`, log);
    },

    /**
     * 获取元素的位置信息
     * @param {HTMLElement} element - 目标元素
     * @returns {Object} 包含页面坐标和屏幕坐标的对象
     */
    getRect(element) {
      const rect = element.getBoundingClientRect();
      const scroll = tool.getScroll();
      return {
        pageX: rect.left + scroll.left, // 元素左上角在页面中的X坐标
        pageY: rect.top + scroll.top, // 元素左上角在页面中的Y坐标
        screenX: rect.left, // 元素左上角在视口中的X坐标
        screenY: rect.top, // 元素左上角在视口中的Y坐标
        width: rect.width,
        height: rect.height,
      };
    },

    /**
     * 判断元素是否半全屏显示(宽或高接近视口)
     * @param {HTMLElement} element - 目标元素
     * @returns {boolean} 是否半全屏
     */
    isHalfFullClient(element) {
      const client = tool.getClient();
      const rect = tool.getRect(element);
      // 宽或高接近视口,且元素居中显示
      if (
        (Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) ||
        (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)
      ) {
        if (
          Math.abs(element.offsetWidth / 2 + rect.screenX - client.width / 2) < 21 &&
          Math.abs(element.offsetHeight / 2 + rect.screenY - client.height / 2) < 21
        ) {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    },

    /**
     * 判断元素是否完全全屏显示(宽和高都接近视口)
     * @param {HTMLElement} element - 目标元素
     * @returns {boolean} 是否完全全屏
     */
    isAllFullClient(element) {
      const client = tool.getClient();
      const rect = tool.getRect(element);
      // 宽和高都接近视口,且位置在左上角附近
      if (
        Math.abs(client.width - element.offsetWidth) < 21 &&
        rect.screenX < 20 &&
        Math.abs(client.height - element.offsetHeight) < 21 &&
        rect.screenY < 10
      ) {
        return true;
      } else {
        return false;
      }
    },

    /**
     * 获取页面滚动距离
     * @returns {Object} 包含左右和上下滚动距离的对象
     */
    getScroll() {
      return {
        left: document.documentElement.scrollLeft || document.body.scrollLeft,
        top: document.documentElement.scrollTop || document.body.scrollTop,
      };
    },

    /**
     * 获取视口尺寸
     * @returns {Object} 包含视口宽高的对象
     */
    getClient() {
      return {
        width: document.compatMode == "CSS1Compat" ? document.documentElement.clientWidth : document.body.clientWidth,
        height:
          document.compatMode == "CSS1Compat" ? document.documentElement.clientHeight : document.body.clientHeight,
      };
    },

    /**
     * 向页面添加CSS样式
     * @param {string} css - CSS样式字符串
     * @returns {HTMLElement} 创建的 style 元素
     */
    addStyle(css) {
      const style = document.createElement("style");
      style.className = "maximize-video-style";
      style.appendChild(document.createTextNode(css));
      document.head.appendChild(style);
      return style;
    },

    /**
     * 匹配字符串与规则(支持通配符*)
     * @param {string} str - 要匹配的字符串
     * @param {string} rule - 包含*的规则字符串
     * @returns {boolean} 是否匹配
     */
    matchRule(str, rule) {
      return new RegExp("^" + rule.split("*").join(".*") + "$").test(str);
    },

    /**
     * 创建按钮元素
     * @param {string} id - 按钮id
     * @param {string} title - 按钮提示文本
     * @param {Function} [clickHandler] - 可选的按钮点击事件处理函数
     * @returns {HTMLElement} 创建的按钮元素
     */
    createButton(id, title, clickHandler) {
      const btn = document.createElement("tbdiv");
      btn.id = id;
      btn.title = title; // 设置提示文本

      // 如果提供了点击处理函数,则绑定
      if (typeof clickHandler === "function") {
        btn.onclick = clickHandler;
      }
      document.body.appendChild(btn);
      return btn;
    },

    /**
     * 显示提示信息
     * @param {string} str - 提示文本
     * @returns {Promise} 提示显示完成的Promise
     */
    async addTip(str) {
      if (!document.getElementById("catTip")) {
        const tip = document.createElement("tbdiv");
        tip.id = "catTip";
        tip.innerHTML = str;
        tip.style.cssText =
          'transition: all 0.8s ease-out;background: none repeat scroll 0 0 #27a9d8;color: #FFFFFF;font: 1.1em "微软雅黑";margin-left: -250px;overflow: hidden;padding: 10px;position: fixed;text-align: center;bottom: 100px;z-index: 300;';
        document.body.appendChild(tip);
        tip.style.right = -tip.offsetWidth - 5 + "px";

        // 显示提示动画
        await new Promise((resolve) => {
          tip.style.display = "block";
          setTimeout(() => {
            tip.style.right = "25px";
            resolve("OK");
          }, 300);
        });

        // 停留一段时间
        await new Promise((resolve) => {
          setTimeout(() => {
            tip.style.right = -tip.offsetWidth - 5 + "px";
            resolve("OK");
          }, 3500);
        });

        // 移除提示元素
        await new Promise((resolve) => {
          setTimeout(() => {
            document.body.removeChild(tip);
            resolve("OK");
          }, 1000);
        });
      }
    },

    /**
     * 查找并触发网站原生按钮(全屏/画中画)
     * @param {string} type - 按钮类型,支持 'fullscreen' 或 'pip'
     * @returns {boolean} 是否找到并触发了原生按钮
     */
    triggerNativeButton(type, prompt) {
      const hostname = document.location.hostname;
      // 遍历规则匹配当前域名
      for (let domain in html5Rules) {
        const ruleSet = html5Rules[domain];
        if (
          tool.matchRule(hostname, domain) &&
          ruleSet.hasOwnProperty(type) &&
          Array.isArray(ruleSet[type]) &&
          ruleSet[type].length > 0
        ) {
          for (let selector of ruleSet[type]) {
            const nativeBtn = document.querySelector(selector);
            if (nativeBtn) {
              nativeBtn.click();
              tool.print(`优先使用 ${domain} 的原生${prompt}按钮`);
              return true;
            }
          }
          break;
        }
      }
      return false; // 未找到原生按钮
    },

    /**
     * 节流函数:控制函数在指定时间内最多执行一次
     * @param {Function} fn - 需要节流的函数
     * @param {number} delay - 延迟时间(毫秒),默认100ms
     * @returns {Function} 节流后的函数
     */
    throttle(fn, delay = 100) {
      let lastTime = 0;
      return function (...args) {
        const now = Date.now();
        if (now - lastTime > delay) {
          fn.apply(this, args); // 保持原函数的this和参数
          lastTime = now;
        }
      };
    },

    /**
     * 防抖函数:在事件停止触发一段时间后执行一次
     * @param {Function} fn - 要执行的函数
     * @param {number} delay - 延迟时间(毫秒),默认 300ms
     * @returns {Function} 防抖后的函数
     */
    debounce(fn, delay = 300) {
      let timer = null;
      return function (...args) {
        clearTimeout(timer); // 清除前一次等待
        timer = setTimeout(() => {
          fn.apply(this, args); // 执行最后一次触发
        }, delay);
      };
    },

    /**
     * 覆盖视频元素的默认点击行为,自定义点击时的播放/暂停逻辑,并返回恢复默认行为的函数
     * @param {HTMLVideoElement} video - 目标视频元素
     * @returns {Function|undefined} 用于恢复视频默认点击行为的函数;若传入的不是有效视频元素则返回undefined
     */
    overrideVideoClick(video) {
      if (!video || video.nodeName !== "VIDEO") return;
      // 记录视频当前的播放状态,初始为未知(null)
      let isPlaying = null;
      const onPlaying = () => {
        isPlaying = true;
      };
      const onPause = () => {
        isPlaying = false;
      };
      // 为视频添加播放状态监听:播放时触发onPlaying,暂停时触发onPause
      video.addEventListener("playing", onPlaying);
      video.addEventListener("pause", onPause);
      const blockClick = function (e) {
        // 阻止事件冒泡和默认行为(避免触发浏览器默认的视频点击逻辑)
        e.stopImmediatePropagation();
        e.preventDefault();
        // 若播放状态未知(初始状态),通过视频属性推测状态:
        // currentTime > 0 表示视频有播放进度(非初始状态)
        // readyState > 2 表示视频至少已加载当前帧数据(可播放)
        // 同时满足则推测为正在播放状态
        if (isPlaying === null) {
          isPlaying = video.currentTime > 0 && video.readyState > 2;
        }
        // 根据当前播放状态切换视频状态:正在播放则暂停,已暂停则播放
        if (isPlaying) {
          video.pause();
        } else {
          video.play();
        }
      };
      // 为视频添加点击事件监听(使用捕获阶段,确保优先处理)
      video.addEventListener("click", blockClick, true);
      /**
       * 恢复函数:移除所有自定义事件监听,恢复视频默认点击行为
       * @returns {void}
       */
      return function restore() {
        // 移除自定义点击事件监听
        video.removeEventListener("click", blockClick, true);
        // 移除播放状态监听
        video.removeEventListener("playing", onPlaying);
        video.removeEventListener("pause", onPause);
      };
    },
  };

  // 按钮设置相关方法
  const setButton = {
    /**
     * 初始化按钮
     */
    init() {
      if (!document.getElementById("playerControlBtn")) {
        init();
      }
      // 如果在iframe中且播放器半全屏,向父窗口发送消息
      if (gv.isIframe && tool.isHalfFullClient(gv.player)) {
        window.parent.postMessage("iframeVideo", "*");
        return;
      }
      this.show();
    },

    /**
     * 显示按钮并设置事件监听
     */
    show() {
      // 移除并重新添加鼠标离开事件监听,避免重复绑定
      gv.player.removeEventListener("mouseleave", handle.leavePlayer, false);
      gv.player.addEventListener("mouseleave", handle.leavePlayer, false);

      // 非全屏状态下添加滚动监听,用于修正按钮位置
      if (!gv.isFull) {
        document.removeEventListener("scroll", handle.scrollFix, false);
        document.addEventListener("scroll", handle.scrollFix, false);
      }
      gv.controlBtn.style.display = "block";
      gv.controlBtn.style.visibility = "visible";

      // 支持画中画功能且播放器不是OBJECT/EMBED时显示画中画按钮
      if (document.pictureInPictureEnabled && gv.player.nodeName != "OBJECT" && gv.player.nodeName != "EMBED") {
        gv.picinpicBtn.style.display = "block";
        gv.picinpicBtn.style.visibility = "visible";
      }
      this.locate();
    },

    /**
     * 定位按钮位置(基于播放器位置和用户设置)
     */
    locate() {
      let escapeHTMLPolicy;
      // 处理可信类型(Trusted Types)安全策略
      const hasTrustedTypes = Boolean(window.trustedTypes && window.trustedTypes.createPolicy);
      if (hasTrustedTypes) {
        escapeHTMLPolicy = window.trustedTypes.createPolicy("myEscapePolicy", {
          createHTML: (string, sink) => string,
        });
      }

      const playerRect = tool.getRect(gv.player);
      const client = tool.getClient();

      // 设置按钮样式和位置(根据用户配置)
      gv.controlBtn.style.opacity = "1.0";
      gv.controlBtn.innerHTML = hasTrustedTypes ? escapeHTMLPolicy.createHTML(gv.btnText.max) : gv.btnText.max;

      gv.picinpicBtn.style.opacity = "1.0";
      gv.picinpicBtn.innerHTML = hasTrustedTypes ? escapeHTMLPolicy.createHTML(gv.btnText.pip) : gv.btnText.pip;

      // 根据用户设置的位置放置按钮
      if (gv.btnPosition === "top-left") {
        // 左上角位置
        gv.controlBtn.style.top = playerRect.screenY - gv.controlBtn.offsetHeight + "px";
        gv.controlBtn.style.left = playerRect.screenX + "px";

        gv.picinpicBtn.style.top = gv.controlBtn.style.top;
        gv.picinpicBtn.style.left = playerRect.screenX + gv.controlBtn.offsetWidth + 1 + "px";
      } else {
        // 右上角位置(默认)
        gv.controlBtn.style.top = playerRect.screenY - gv.controlBtn.offsetHeight + "px";
        gv.controlBtn.style.left = playerRect.screenX + gv.player.offsetWidth - gv.controlBtn.offsetWidth + "px";

        gv.picinpicBtn.style.top = gv.controlBtn.style.top;
        gv.picinpicBtn.style.left = parseFloat(gv.controlBtn.style.left) - gv.picinpicBtn.offsetWidth - 1 + "px";
      }
    },
  };

  // 事件处理相关方法
  const handle = {
    /**
     * 获取鼠标悬停的播放器元素(优化版:仅从鼠标路径中筛选)
     * @param {MouseEvent} e - 鼠标事件对象
     */
    getPlayer(e) {
      if (gv.isFull) return;

      gv.mouseoverEl = e.target;
      const hostname = document.location.hostname;
      const elements = document.elementsFromPoint(e.clientX, e.clientY);

      // 1. 优先使用站点特定规则匹配播放器
      for (let i in html5Rules) {
        if (tool.matchRule(hostname, i)) {
          for (let playerSelector of html5Rules[i].player) {
            for (let el of elements) {
              if (el.matches(playerSelector)) {
                gv.player = el;
                setButton.init();
                return;
              }
            }
          }
          break; // 匹配到站点规则后不再继续
        }
      }

      // 2. 使用通用规则匹配
      for (let generalPlayerRule of generalPlayerRules) {
        for (let el of elements) {
          if (el.matches(generalPlayerRule)) {
            gv.player = el;
            setButton.init();
            return;
          }
        }
      }

      // 3. 匹配 video 元素(尺寸足够大)
      for (let el of elements) {
        if (el.nodeName === "VIDEO" && el.offsetWidth > 399 && el.offsetHeight > 220) {
          gv.player = handle.autoCheck(el);
          gv.autoCheckCount = 1;
          setButton.init();
          return;
        }
      }

      // 4. 兜底处理 VIDEO/OBJECT/EMBED(直接目标元素)
      switch (e.target.nodeName) {
        case "VIDEO":
        case "OBJECT":
        case "EMBED":
          if (e.target.offsetWidth > 399 && e.target.offsetHeight > 220) {
            gv.player = e.target;
            setButton.init();
            return;
          }
          break;
      }

      // 5. 未匹配到播放器,清除状态
      handle.leavePlayer();
    },

    /**
     * 自动检测播放器的父容器(寻找与视频尺寸相近的容器)
     * @param {HTMLElement} v - 视频元素
     * @returns {HTMLElement} 最合适的播放器容器
     */
    autoCheck(v) {
      let tempPlayer,
        el = v;
      gv.playerChilds = [];
      gv.playerChilds.push(v);
      // 向上遍历父节点,寻找与视频尺寸相近的容器
      while ((el = el.parentNode)) {
        if (Math.abs(v.offsetWidth - el.offsetWidth) < 15 && Math.abs(v.offsetHeight - el.offsetHeight) < 15) {
          tempPlayer = el;
          gv.playerChilds.push(el);
        } else {
          break;
        }
      }
      return tempPlayer;
    },

    /**
     * 处理鼠标离开播放器的事件(隐藏按钮)
     */
    leavePlayer() {
      if (gv.controlBtn.style.visibility == "visible") {
        gv.controlBtn.style.opacity = "";
        gv.controlBtn.style.visibility = "";
        gv.picinpicBtn.style.opacity = "";
        gv.picinpicBtn.style.visibility = "";
        gv.player.removeEventListener("mouseleave", handle.leavePlayer, false);
        document.removeEventListener("scroll", handle.scrollFix, false);
      }
    },

    /**
     * 处理滚动事件(延迟修正按钮位置,避免频繁触发)
     */
    scrollFix(e) {
      clearTimeout(gv.scrollFixTimer);
      gv.scrollFixTimer = setTimeout(() => {
        setButton.locate();
      }, 20);
    },

    /**
     * 处理键盘快捷键
     * @param {KeyboardEvent} e - 键盘事件对象
     */
    hotKey(e) {
      // ESC键:切换全屏状态(默认)
      if (e.key === "Escape") {
        // 阻止事件传播和默认行为,避免网站监听到ESC键
        e.preventDefault(); // 阻止默认行为(如浏览器默认的ESC行为)
        e.stopPropagation(); // 阻止事件冒泡到父元素
        e.stopImmediatePropagation(); // 阻止当前元素上的其他监听器执行
        maximize.playerControl();
      }
      // F2键:切换画中画(默认)
      if (e.code === "F2") {
        handle.pictureInPicture();
      }
    },

    /**
     * 处理鼠标中键点击事件
     * @param {MouseEvent} e - 鼠标事件对象
     */
    mouseMiddleClick(e) {
      // 中键点击(button=1)时触发
      if (e.button === 1) {
        // const clickedInsidePlayer = gv.player && gv.player.contains(e.target);
        // 改为鼠标穿透
        const elements = document.elementsFromPoint(e.clientX, e.clientY);
        const clickedInsidePlayer = gv.player && elements.includes(gv.player);
        const isFullscreenActive = gv.isFull === true;
        // 如果点击在播放器内,或当前处于网页全屏状态,则触发控制逻辑
        if (clickedInsidePlayer || isFullscreenActive) {
          e.preventDefault(); // 阻止默认行为(如打开链接)
          e.stopPropagation(); // 阻止事件继续传递给视频元素
          maximize.playerControl(); // 切换网页全屏
        }
      }
    },

    /**
     * 处理跨窗口消息
     * @param {MessageEvent} e - 消息事件对象
     */
    async receiveMessage(e) {
      switch (e.data) {
        case "iframePicInPic":
          tool.print("messege:iframePicInPic");
          // 处理iframe中的画中画请求
          if (!document.pictureInPictureElement) {
            await document
              .querySelector("video")
              .requestPictureInPicture()
              .catch((error) => {
                tool.addTip(gv.btnText.tip);
              });
          } else {
            await document.exitPictureInPicture();
          }
          break;
        case "iframeVideo":
          tool.print("messege:iframeVideo");
          // 处理iframe中的视频全屏请求
          if (!gv.isFull) {
            gv.player = gv.mouseoverEl;
            setButton.init();
          }
          break;
        case "parentFull":
          tool.print("messege:parentFull");
          // 处理父窗口的全屏请求
          gv.player = gv.mouseoverEl;
          if (gv.isIframe) {
            window.parent.postMessage("parentFull", "*");
          }
          maximize.checkParent();
          maximize.fullWin();
          // 修正特定播放器的位置
          if (getComputedStyle(gv.player).left != "0px") {
            tool.addStyle(
              "#htmlToothbrush #bodyToothbrush .playerToothbrush {left:0px !important;width:100vw !important;}"
            );
          }
          gv.isFull = true;
          break;
        case "parentSmall":
          tool.print("messege:parentSmall");
          // 处理父窗口的退出全屏请求
          if (gv.isIframe) {
            window.parent.postMessage("parentSmall", "*");
          }
          maximize.smallWin();
          break;
        case "innerFull":
          tool.print("messege:innerFull");
          // 处理iframe内部的全屏请求
          if (gv.player.nodeName == "IFRAME") {
            gv.player.contentWindow.postMessage("innerFull", "*");
          }
          maximize.checkParent();
          maximize.fullWin();
          break;
        case "innerSmall":
          tool.print("messege:innerSmall");
          // 处理iframe内部的退出全屏请求
          if (gv.player.nodeName == "IFRAME") {
            gv.player.contentWindow.postMessage("innerSmall", "*");
          }
          maximize.smallWin();
          break;
      }
    },

    /**
     * 处理画中画功能切换
     */
    pictureInPicture() {
      // 优先使用网站原生画中画按钮(调用通用函数)
      if (tool.triggerNativeButton("pip", "画中画")) {
        return; // 找到并触发后终止
      }

      // 原有画中画逻辑
      if (!document.pictureInPictureElement) {
        if (gv.player) {
          if (gv.player.nodeName == "IFRAME") {
            // 向iframe发送画中画请求
            gv.player.contentWindow.postMessage("iframePicInPic", "*");
          } else {
            // 直接请求画中画
            gv.player.parentNode.querySelector("video").requestPictureInPicture();
          }
        } else {
          // 没有指定播放器时使用第一个video元素
          document.querySelector("video").requestPictureInPicture();
        }
      } else {
        // 退出画中画
        document.exitPictureInPicture();
      }
    },

    /**
     * 显示右键菜单
     * @param {MouseEvent} e - 鼠标事件对象
     */
    showContextMenu(e) {
      e.preventDefault();
      // 先移除已存在的菜单
      handle.hideContextMenu();

      // 创建菜单元素
      gv.contextMenu = document.createElement("div");
      gv.contextMenu.id = "btnContextMenu";

      // 添加菜单项
      const menuTitle = document.createElement("div");
      menuTitle.textContent = gv.btnText.menuTitle;
      gv.contextMenu.appendChild(menuTitle);

      // 左上角选项
      const topLeftItem = document.createElement("div");
      topLeftItem.textContent = gv.btnText.topLeft;
      topLeftItem.addEventListener("click", () => {
        handle.setButtonPosition("top-left");
        handle.hideContextMenu();
      });
      gv.contextMenu.appendChild(topLeftItem);

      // 右上角选项
      const topRightItem = document.createElement("div");
      topRightItem.textContent = gv.btnText.topRight;
      topRightItem.addEventListener("click", () => {
        handle.setButtonPosition("top-right");
        handle.hideContextMenu();
      });
      gv.contextMenu.appendChild(topRightItem);

      document.body.appendChild(gv.contextMenu);

      // 计算菜单位置,确保在视口内
      const client = tool.getClient();
      const menuRect = gv.contextMenu.getBoundingClientRect();

      // 初始位置在鼠标下方
      let left = e.clientX;
      let top = e.clientY;

      // 边界检测 - 水平方向
      if (left + menuRect.width > client.width) {
        left = client.width - menuRect.width;
      }

      // 边界检测 - 垂直方向
      if (top + menuRect.height > client.height) {
        top = client.height - menuRect.height;
      }

      // 应用位置
      gv.contextMenu.style.left = left + "px";
      gv.contextMenu.style.top = top + "px";

      // 点击页面其他地方关闭菜单
      document.addEventListener("click", handle.hideContextMenuOnce);
    },

    /**
     * 隐藏右键菜单
     */
    hideContextMenu() {
      if (gv.contextMenu && gv.contextMenu.parentNode) {
        document.body.removeChild(gv.contextMenu);
        gv.contextMenu = null;
      }
    },

    /**
     * 一次性隐藏菜单的事件处理
     */
    hideContextMenuOnce() {
      handle.hideContextMenu();
      document.removeEventListener("click", handle.hideContextMenuOnce);
    },

    /**
     * 设置按钮位置并保存
     * @param {string} position - 位置值 ('top-left' 或 'top-right')
     */
    setButtonPosition(position) {
      gv.btnPosition = position;
      GM_setValue("buttonPosition", position);
      setButton.locate(); // 重新定位按钮
    },
  };

  // 网页全屏相关方法
  const maximize = {
    /**
     * 播放器控制(切换全屏/退出全屏)
     */
    playerControl() {
      if (!gv.player) {
        return;
      }

      // 优先使用网站原生全屏按钮(调用通用函数)
      if (!gv.useCssFullscreen && tool.triggerNativeButton("fullscreen", "网页全屏")) {
        gv.isFull = !gv.isFull; // 更新状态
        return;
      }

      // 原有全屏逻辑(只有未使用网站按钮时才会执行)
      this.checkParent();
      if (!gv.isFull) {
        // 进入全屏
        if (gv.isIframe) {
          window.parent.postMessage("parentFull", "*");
        }
        if (gv.player.nodeName == "IFRAME") {
          gv.player.contentWindow.postMessage("innerFull", "*");
        }
        this.fullWin();

        // 自动调整播放器容器(最多尝试10次)
        if (gv.autoCheckCount > 0 && !tool.isHalfFullClient(gv.playerChilds[0])) {
          if (gv.autoCheckCount > 10) {
            for (let v of gv.playerChilds) {
              v.classList.add("videoToothbrush");
            }
            return;
          }
          const tempPlayer = handle.autoCheck(gv.playerChilds[0]);
          gv.autoCheckCount++;
          maximize.playerControl();
          gv.player = tempPlayer;
          maximize.playerControl();
        } else {
          gv.autoCheckCount = 0;
        }
      } else {
        // 退出全屏
        if (gv.isIframe) {
          window.parent.postMessage("parentSmall", "*");
        }
        if (gv.player.nodeName == "IFRAME") {
          gv.player.contentWindow.postMessage("innerSmall", "*");
        }
        this.smallWin();
      }
    },

    /**
     * 记录播放器的父元素链(用于退出全屏时恢复)
     */
    checkParent() {
      if (gv.isFull) {
        return;
      }
      gv.playerParents = [];
      let full = gv.player;
      // 遍历父节点直到body
      while ((full = full.parentNode)) {
        if (full.nodeName == "BODY") {
          break;
        }
        if (full.getAttribute) {
          gv.playerParents.push(full);
        }
      }
    },

    /**
     * 进入全屏状态
     */
    fullWin() {
      if (!gv.isFull) {
        // 移除鼠标悬停监听(全屏状态不需要)
        document.removeEventListener("mouseover", handle.getPlayer, false);
        // 保存原始id(用于恢复)
        gv.backHtmlId = document.body.parentNode.id;
        gv.backBodyId = document.body.id;
        // 保存当前滚动位置
        gv.scrollTop = tool.getScroll().top;
        gv.scrollLeft = tool.getScroll().left;
        // 显示辅助按钮
        gv.leftBtn.style.display = "block";
        gv.rightBtn.style.display = "block";
        gv.picinpicBtn.style.display = "";
        gv.controlBtn.style.display = "";
        // 添加全屏相关样式类
        this.addClass();
        const hostname = document.location.hostname;

        // YouTube特殊处理:切换剧院模式
        if (hostname.includes("www.youtube.com")) {
          const flexy = document.querySelector("#page-manager > ytd-watch-flexy");
          // 是否处于剧院模式
          const isTheaterMode =
            flexy && getComputedStyle(flexy).getPropertyValue("--ytd-watch-flexy-chat-max-height").trim() === "460px";
          // 不是剧院模式就自动进入宽屏模式
          if (!isTheaterMode) {
            document.querySelector("#movie_player .ytp-size-button").click();
            gv.ytbStageChange = true;
          }
        }
      }
      gv.isFull = true;
      gv.useCssFullscreen = true;
    },

    /**
     * 为全屏状态添加样式类
     */
    addClass() {
      // 修改根元素id(用于CSS选择器定位)
      document.body.parentNode.id = "htmlToothbrush";
      document.body.id = "bodyToothbrush";
      // 为父元素添加样式类
      for (let v of gv.playerParents) {
        v.classList.add("parentToothbrush");
        // 处理fixed定位的父元素(避免层级问题)
        if (getComputedStyle(v).position == "fixed") {
          v.classList.add("absoluteToothbrush");
        }
      }
      // 为播放器添加样式类
      gv.player.classList.add("playerToothbrush");
      // 确保video元素显示控件
      if (gv.player.nodeName == "VIDEO") {
        gv.backControls = gv.player.controls;
        gv.player.controls = true;
        gv.restoreClick = tool.overrideVideoClick(gv.player); // 注入逻辑
      }
      // 触发resize事件(刷新播放器尺寸)
      window.dispatchEvent(new Event("resize"));
    },

    /**
     * 退出全屏状态(恢复原始状态)
     */
    smallWin() {
      // 恢复原始id
      document.body.parentNode.id = gv.backHtmlId;
      document.body.id = gv.backBodyId;
      // 恢复滚动位置
      window.scrollTo(gv.scrollLeft, gv.scrollTop);
      // 移除父元素的样式类
      for (let v of gv.playerParents) {
        v.classList.remove("parentToothbrush");
        v.classList.remove("absoluteToothbrush");
      }
      // 移除播放器的样式类
      gv.player.classList.remove("playerToothbrush");

      // YouTube特殊处理:恢复剧院模式
      if (document.location.hostname == "www.youtube.com" && gv.ytbStageChange) {
        document.querySelector("#movie_player .ytp-size-button").click();
        gv.ytbStageChange = false;
      }

      // 恢复video控件状态
      if (gv.player.nodeName == "VIDEO") {
        gv.player.controls = gv.backControls;
        gv.restoreClick(); // 恢复原始点击行为
      }
      // 隐藏辅助按钮
      gv.leftBtn.style.display = "";
      gv.rightBtn.style.display = "";
      gv.controlBtn.style.display = "";
      // 恢复鼠标悬停监听
      document.addEventListener("mouseover", handle.getPlayer, false);
      // 触发resize事件
      window.dispatchEvent(new Event("resize"));
      gv.isFull = false;
      gv.useCssFullscreen = false;
    },
  };

  /**
   * 初始化脚本
   */
  const init = () => {
    // 创建画中画按钮
    gv.picinpicBtn = tool.createButton("picinpicBtn", gv.btnText.pipTooltip, () => {
      handle.pictureInPicture();
    });
    gv.picinpicBtn.addEventListener("contextmenu", handle.showContextMenu); // 添加右键菜单事件

    // 创建网页全屏按钮
    gv.controlBtn = tool.createButton("playerControlBtn", gv.btnText.maxTooltip, () => {
      maximize.playerControl();
    });
    gv.controlBtn.addEventListener("contextmenu", handle.showContextMenu); // 添加右键菜单事件

    // 创建左侧边缘退出按钮
    gv.leftBtn = tool.createButton("leftFullStackButton", "", () => {
      maximize.playerControl();
    });
    // 创建右侧边缘退出按钮
    gv.rightBtn = tool.createButton("rightFullStackButton", "", () => {
      maximize.playerControl();
    });

    // 确保全局样式只添加一次
    if (getComputedStyle(gv.controlBtn).position != "fixed") {
      tool.addStyle(`
/* 针对B站播放器的特殊样式 */
#htmlToothbrush #bodyToothbrush .parentToothbrush .bilibili-player-video {
  margin: 0 !important;
}

/* 全屏状态下禁止页面滚动 */
#htmlToothbrush,
#bodyToothbrush {
  overflow: hidden !important;
  zoom: 100% !important;
  opacity: 1 !important;
}

/* 父元素清障样式 */
#htmlToothbrush #bodyToothbrush .parentToothbrush {
  /* 清除 transform,避免触发包含块影响 fixed 定位 */
  transform: none !important;
  /* 清除滤镜效果,同样会触发包含块 */
  filter: none !important;
  /* 清除 3D 透视属性,避免影响定位上下文 */
  perspective: none !important;
  /* 清除背景滤镜,避免触发新的渲染图层 */
  backdrop-filter: none !important;
  /* 禁止提前优化 transform,防止浏览器创建隔离层 */
  will-change: auto !important;
  /* 禁用布局隔离,避免阻断 fixed 元素继承视口定位 */
  contain: none !important;
  /* 关闭图层隔离,避免 stacking context 干扰 */
  isolation: auto !important;
  /* 禁用 3D transform 的子元素继承,恢复为 2D 渲染 */
  -webkit-transform-style: flat !important;
  /* 清除裁剪路径,避免 fixed 元素被遮挡或裁剪 */
  clip-path: none !important;
  /* 清除遮罩图层,避免 fixed 元素被遮挡 */
  mask: none !important;
  /* 禁用混合模式,防止 fixed 元素与背景发生视觉混合 */
  mix-blend-mode: normal !important;
  /* 恢复默认背面可见性,防止 3D 翻转影响渲染 */
  backface-visibility: visible !important;
  /* 恢复默认层级,避免 stacking context 干扰 fixed 元素 */
  z-index: auto !important;
  /* 恢复默认滚动行为,避免 overscroll 影响 fixed 元素交互 */
  overscroll-behavior: auto !important;
  /* 清除父级透明度,确保子元素视觉不被包裹影响 */
  opacity: 1 !important;
}

/* 修正fixed定位的父元素 */
#htmlToothbrush #bodyToothbrush .absoluteToothbrush {
  position: absolute !important;
}

/* 播放器元素全屏样式 */
#htmlToothbrush #bodyToothbrush .playerToothbrush {
  /* 固定定位,确保相对于视口而不是父元素定位 */
  position: fixed !important;
  /* 铺满整个视口宽高 */
  top: 0 !important;
  left: 0 !important;
  width: 100vw !important;
  height: 100vh !important;
  /* 清除尺寸限制,确保播放器能真正铺满视口 */
  max-width: none !important;
  max-height: none !important;
  min-width: 0 !important;
  min-height: 0 !important;
  /* 清除边距和内边距,避免布局偏移 */
  margin: 0 !important;
  padding: 0 !important;
  /* 设置盒模型为 border-box,避免尺寸计算异常 */
  box-sizing: border-box;
  /* 清除边框,避免视觉干扰 */
  border: none !important;
  /* 设置背景色,防止底层内容透出(可根据主题调整) */
  background-color: black;
  /* 保证在所有元素之上显示(接近最大 z-index) */
  z-index: 2147483646 !important;
  /* 清除自身 transform,避免定位错乱 */
  transform: none !important;
  /* 清除滤镜效果,避免视觉异常 */
  filter: none !important;
  /* 清除 3D 透视影响 */
  perspective: none !important;
  /* 禁止提前优化 transform,避免触发包含块 */
  will-change: auto !important;
  /* 防止播放器被裁剪或遮罩 */
  clip-path: none !important;
  mask: none !important;
  /* 禁用混合模式,确保视觉纯净 */
  mix-blend-mode: normal !important;
  /* 限制布局影响范围,提升渲染性能 */
  contain: strict;
  /* 创建独立图层,避免混合模式污染 */
  isolation: isolate;
  /* 启用硬件加速,提升渲染性能 */
  backface-visibility: hidden;
  /* 禁用动画过渡,避免进入/退出全屏时出现闪烁或延迟 */
  transition: none !important;
  /* 清除圆角,避免触发图层重排或抗锯齿变化 */
  border-radius: 0 !important;
}

/* 视频内容适配 */
#htmlToothbrush #bodyToothbrush .parentToothbrush video {
  object-fit: contain !important;
}

/* 视频元素全屏样式 */
#htmlToothbrush #bodyToothbrush .parentToothbrush .videoToothbrush {
  width: 100vw !important;
  height: 100vh !important;
}

/* 网页全屏按钮和画中画按钮样式 */
#playerControlBtn,
#picinpicBtn {
  /* 布局定位 */
  position: fixed;
  z-index: 2147483647;
  /* 盒模型 */
  display: none;
  width: auto;
  height: 20px;
  margin: 0;
  padding: 0 10px;
  line-height: 20px;
  border-radius: 2px;
  /* 排版样式 */
  text-align: center;
  font-size: 12px;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
  /* 视觉样式 */
  background-color: rgba(39, 169, 216, 0.7);
  color: rgba(255, 255, 255, 0.95);
  text-shadow: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
  backdrop-filter: blur(3px); /* 毛玻璃模糊 */
  -webkit-backdrop-filter: blur(3px); /* Safari 支持 */
  /* 动效与交互 */
  cursor: pointer;
  user-select: none;
  visibility: hidden;
  opacity: 0;
  transition: all 0.5s ease;
}
#playerControlBtn:hover,
#picinpicBtn:hover {
  visibility: visible;
  opacity: 1;
  background-color: rgba(39, 116, 216, 0.7);
}

/* 左右退出全屏按钮样式 */
#leftFullStackButton,
#rightFullStackButton {
  display: none;
  position: fixed;
  width: 1px;
  height: 100vh;
  top: 0;
  z-index: 2147483647;
  background: #000;
}
#leftFullStackButton {
  left: 0;
}
#rightFullStackButton {
  right: 0;
}

/* 右键菜单样式,默认浅色主题 */
#btnContextMenu {
  position: fixed;
  background: rgba(250, 250, 250, 0.72);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 14px;
  padding: 6px 0;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.08);
  z-index: 2147483647;
  width: 220px;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
  font-size: 13px;
}
#btnContextMenu > div:first-child {
  padding: 8px 20px;
  color: rgba(60, 60, 67, 0.6);
  font-weight: 500;
  font-size: 13px;
  border-bottom: 1px solid rgba(60, 60, 67, 0.15);
  cursor: default;
}
#btnContextMenu > div:not(:first-child) {
  padding: 5px 10px;
  cursor: pointer;
  color: #1c1c1e;
  border-radius: 8px;
  transition: background-color 0.2s ease;
}
#btnContextMenu > div:not(:first-child):hover {
  background-color: rgba(0, 0, 0, 0.06);
}
/* 右键菜单深色主题 */
@media (prefers-color-scheme: dark) {
  #btnContextMenu {
    background: rgba(28, 28, 30, 0.72);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.32);
    color: rgba(255, 255, 255, 0.85);
  }
  #btnContextMenu > div:first-child {
    color: rgba(235, 235, 245, 0.6);
    border-bottom: 1px solid rgba(84, 84, 88, 0.65);
  }
  #btnContextMenu > div:not(:first-child) {
    color: rgba(255, 255, 255, 0.85);
  }
  #btnContextMenu > div:not(:first-child):hover {
    /* background-color: rgba(255, 255, 255, 0.10); */
    /* 修改为蓝色,为了兼容 Dark Reader */
    background-color: rgba(27, 134, 187, 0.2);
  }
}
`);
    }

    // 添加事件监听
    document.addEventListener("mouseover", tool.debounce(handle.getPlayer), false);
    document.addEventListener("keydown", handle.hotKey, false);
    window.addEventListener("message", handle.receiveMessage, false);
    // 添加鼠标中键点击事件监听
    document.addEventListener("pointerdown", handle.mouseMiddleClick, true);
    // 添加全局右键点击事件,用于关闭菜单
    document.addEventListener("contextmenu", (e) => {
      if (
        gv.contextMenu &&
        !gv.contextMenu.contains(e.target) &&
        e.target.id !== "playerControlBtn" &&
        e.target.id !== "picinpicBtn"
      ) {
        handle.hideContextMenu();
      }
    });

    tool.print("Ready");
  };

  // 初始化脚本
  init();
})();