Greasy Fork

Greasy Fork is available in English.

哔哩哔哩自动画质

自动解锁并更改哔哩哔哩视频的画质和音质及直播画质,实现自动选择最高画质、无损音频、杜比全景声。

当前为 2025-01-20 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         哔哩哔哩自动画质
// @namespace    https://github.com/AHCorn/Bilibili-Auto-Quality/
// @version      4.1
// @license      GPL-3.0
// @description  自动解锁并更改哔哩哔哩视频的画质和音质及直播画质,实现自动选择最高画质、无损音频、杜比全景声。
// @author       安和(AHCorn)
// @icon         https://www.bilibili.com/favicon.ico
// @match        *://www.bilibili.com/video/*
// @match        *://www.bilibili.com/list/*
// @match        *://www.bilibili.com/blackboard/*
// @match        *://www.bilibili.com/watchlater/*
// @match        *://www.bilibili.com/bangumi/*
// @match        *://www.bilibili.com/watchroom/*
// @match        *://www.bilibili.com/medialist/*
// @match        *://bangumi.bilibili.com/*
// @exclude      *://live.bilibili.com/
// @exclude      *://live.bilibili.com/*/*
// @match        *://live.bilibili.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function () {
  "use strict";

  if (typeof unsafeWindow === "undefined") {
    unsafeWindow = window;
  }

  window.localStorage.bilibili_player_force_hdr = 1;

  let hiResAudioEnabled = GM_getValue("hiResAudio", false);
  let dolbyAtmosEnabled = GM_getValue("dolbyAtmos", false);
  let userQualitySetting = GM_getValue("qualitySetting", "最高画质");
  let userBackupQualitySetting = GM_getValue("backupQualitySetting", "最高画质");
  let useHighestQualityFallback = GM_getValue("useHighestQualityFallback", true);
  let activeQualityTab = GM_getValue("activeQualityTab", "primary");
  let userHasChangedQuality = false;
  let takeOverQualityControl = GM_getValue("takeOverQualityControl", false);
  let isVipUser = false;
  let vipStatusChecked = false;
  let isLoading = true;
  let isLivePage = false;
  let userLiveQualitySetting = GM_getValue("liveQualitySetting", "原画");

  let devModeEnabled = GM_getValue("devModeEnabled", false);
  let devModeVipStatus = GM_getValue("devModeVipStatus", false);
  let devModeDelay = GM_getValue("devModeDelay", 4000);
  let devModeDisableUA = GM_getValue("devModeDisableUA", false);
  let devModeAudioDelay = GM_getValue("devModeAudioDelay", 4000);
  let devModeAudioRetries = GM_getValue("devModeAudioRetries", 2);
  let injectQualityButton = GM_getValue("injectQualityButton", true);
  let qualityDoubleCheck = GM_getValue("qualityDoubleCheck", true);
  let liveQualityDoubleCheck = GM_getValue("liveQualityDoubleCheck", true);

  try {
    if (!devModeDisableUA || !devModeEnabled) {
      Object.defineProperty(navigator, 'userAgent', {
        value: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15",
        configurable: true
      });
      Object.defineProperty(navigator, 'platform', {
        value: "MacIntel",
        configurable: true
      });
      console.log('UA 和平台标识修改成功');
    } else {
      console.log('开发者模式已禁用 UA 修改');
    }
  } catch (error) {
    console.error('修改 UserAgent 失败,解锁功能可能失效,若需要使用解锁功能,请先关闭与修改 UA 相关的插件及脚本:', error);
  }

  GM_addStyle(`
        #bilibili-quality-selector, #bilibili-live-quality-selector, #bilibili-dev-settings {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: linear-gradient(135deg, #f6f8fa, #e9ecef);
            border-radius: 24px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1), 0 1px 8px rgba(0, 0, 0, 0.06);
            padding: 30px;
            width: 90%;
            max-width: 400px;
            max-height: 85vh;
            overflow-y: auto;
            overflow-x: hidden;
            display: none;
            z-index: 10000;
            font-family: 'Segoe UI', 'Roboto', sans-serif;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            scrollbar-width: thin;
            scrollbar-color: rgba(0, 161, 214, 0.3) transparent;
        }

        .quality-tabs {
            display: flex;
            margin-bottom: 20px;
            border-radius: 12px;
            background: #e8eaed;
            padding: 4px;
        }

        .quality-tab {
            flex: 1;
            padding: 8px;
            text-align: center;
            cursor: pointer;
            border-radius: 8px;
            transition: all 0.3s ease;
            color: #666;
            font-weight: 600;
        }

        .quality-tab.active {
            background: #00a1d6;
            color: white;
        }

        .quality-section {
            display: none;
        }

        .quality-section.active {
            display: block;
        }

        .quality-button-hidden {
            display: none !important;
        }

        .toggle-switch.hide {
            display: none;
        }

        .toggle-switch.show {
            display: flex;
        }

        #bilibili-quality-selector h2, #bilibili-live-quality-selector h2,
        #bilibili-live-quality-selector h3 {
            margin: 0 0 20px;
            color: #00a1d6;
            font-size: 28px;
            text-align: center;
            font-weight: 700;
        }

        #bilibili-live-quality-selector h3 {
            font-size: 24px;
            margin-top: 20px;
        }

        #bilibili-quality-selector p, #bilibili-live-quality-selector p {
            margin: 0 0 25px;
            color: #5f6368;
            font-size: 14px;
            text-align: center;
        }

        .quality-group {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
            gap: 12px;
            margin-bottom: 25px;
        }

        .line-group {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 12px;
            margin-bottom: 25px;
        }

        .quality-button, .line-button {
            background-color: #ffffff;
            border: 2px solid #dadce0;
            border-radius: 12px;
            padding: 10px 8px;
            font-size: 14px;
            color: #3c4043;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            font-weight: 600;
            text-align: center;
        }

        .line-button {
            font-size: 12px;
            padding: 8px 4px;
        }

        .quality-button:hover, .line-button:hover {
            background-color: #f1f3f4;
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        .quality-button.active, .line-button.active {
            background-color: #00a1d6;
            color: white;
            border-color: #00a1d6;
            box-shadow: 0 6px 12px rgba(0, 161, 214, 0.3);
        }

        .quality-button.active.vip-quality {
            background-color: #f25d8e;
            color: white;
            border-color: #f25d8e;
            box-shadow: 0 6px 12px rgba(242, 93, 142, 0.3);
        }

        .quality-button.unavailable {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .toggle-switch {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 12px;
            padding: 10px 15px;
            border-radius: 12px;
            transition: all 0.3s ease;
        }

        .toggle-switch:hover {
            background-color: #e8eaed;
        }

        .toggle-switch label {
            font-size: 16px;
            color: #3c4043;
            font-weight: 600;
        }

        .switch {
            position: relative;
            display: inline-block;
            width: 52px;
            height: 28px;
        }

        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 34px;
        }

        .slider:before {
            position: absolute;
            content: "";
            height: 20px;
            width: 20px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }

        input:checked + .slider {
            background-color: #00a1d6;
        }

        input:checked + .slider.vip-audio {
            background-color: #f25d8e;
        }

        input:checked + .slider:before {
            transform: translateX(24px);
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        @keyframes slideIn {
            from { transform: translate(-50%, -60%); }
            to { transform: translate(-50%, -50%); }
        }

        #bilibili-quality-selector.show, #bilibili-live-quality-selector.show {
            display: block;
            animation: fadeIn 0.3s ease-out, slideIn 0.3s ease-out;
        }

        @media (max-width: 480px) {
            #bilibili-quality-selector, #bilibili-live-quality-selector, #bilibili-dev-settings {
                width: 95%;
                padding: 20px;
                max-height: 80vh;
            }

            .quality-group {
                grid-template-columns: repeat(2, 1fr);
                gap: 8px;
            }

            .line-group {
                grid-template-columns: repeat(2, 1fr);
                gap: 8px;
            }

            .quality-button, .line-button {
                padding: 8px 6px;
                font-size: 13px;
            }

            .live-quality-button {
                padding: 10px 6px;
                font-size: 14px;
            }

            .toggle-switch {
                padding: 8px 12px;
                margin-bottom: 8px;
            }

            .toggle-switch label {
                font-size: 14px;
            }

            .toggle-switch .description {
                font-size: 12px;
            }

            .input-group {
                padding: 12px;
                margin-bottom: 12px;
            }

            .input-group label {
                font-size: 14px;
            }

            .input-group .description {
                font-size: 12px;
            }

            .input-group input[type="number"] {
                width: 70px;
                padding: 6px;
                font-size: 13px;
            }

            .github-link {
                top: 20px;
                right: 20px;
                width: 20px;
                height: 20px;
            }

            h2 {
                font-size: 24px !important;
                margin-bottom: 15px !important;
            }

            .dev-warning {
                font-size: 13px;
                padding: 12px;
                margin-bottom: 15px;
            }

            .warning {
                font-size: 13px;
                padding: 8px;
                margin: 8px 0;
            }

            .status-bar {
                font-size: 13px;
                padding: 8px;
                margin-bottom: 12px;
            }

            .quality-section-title {
                font-size: 15px;
                margin: 15px 0 12px;
            }

            .quality-section-description {
                font-size: 12px;
                margin: -3px 0 12px;
            }
        }

        @media (max-height: 600px) {
            #bilibili-quality-selector, #bilibili-live-quality-selector, #bilibili-dev-settings {
                max-height: 90vh;
                padding: 15px;
            }

            .quality-group, .line-group {
                margin-bottom: 15px;
            }

            .toggle-switch {
                margin-bottom: 6px;
            }

            .input-group {
                margin-bottom: 10px;
            }
        }

        .status-bar {
            padding: 10px;
            border-radius: 8px;
            margin-bottom: 15px;
            text-align: center;
            font-weight: bold;
            transition: all 0.5s ease;
        }

        .status-bar.non-vip {
            background-color: #f0f0f0;
            color: #666666;
        }

        .status-bar.vip {
            background-color: #fff1f5;
            color: #f25d8e;
        }

        .warning {
            background-color: #fce8e6;
            color: #d93025;
            padding: 10px;
            border-radius: 8px;
            margin-top: 12px;
            margin-bottom: 12px;
            text-align: center;
            font-weight: bold;
            transition: all 0.3s ease;
        }

        .warning::before {
            content: "";
            margin-right: 10px;
        }

        #bilibili-dev-settings {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: linear-gradient(135deg, #ffffff, #f8f9fa);
            border-radius: 24px;
            box-shadow: 0 12px 36px rgba(0, 0, 0, 0.15), 0 4px 12px rgba(0, 0, 0, 0.08);
            padding: 32px;
            width: 90%;
            max-width: 420px;
            max-height: 85vh;
            overflow-y: auto;
            overflow-x: hidden;
            display: none;
            z-index: 10000;
            font-family: 'Segoe UI', 'Roboto', sans-serif;
            scrollbar-width: thin;
            scrollbar-color: rgba(0, 161, 214, 0.3) transparent;
        }

        #bilibili-dev-settings.show {
            display: block;
            animation: fadeIn 0.3s ease-out, slideIn 0.3s ease-out;
        }

        #bilibili-dev-settings h2 {
            margin: 0 0 24px;
            color: #f25d8e;
            font-size: 28px;
            text-align: center;
            font-weight: 700;
            letter-spacing: -0.5px;
            text-shadow: 0 2px 4px rgba(242, 93, 142, 0.1);
        }

        #bilibili-dev-settings .dev-warning {
            background: linear-gradient(135deg, #fff1f5, #fce8e6);
            color: #d93025;
            padding: 14px 18px;
            border-radius: 16px;
            margin-bottom: 24px;
            text-align: center;
            font-weight: 600;
            font-size: 14px;
            border: 2px solid rgba(217, 48, 37, 0.1);
            box-shadow: 0 4px 12px rgba(217, 48, 37, 0.05);
        }

        #bilibili-dev-settings .toggle-switch {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 12px;
            padding: 10px 15px;
            background-color: #f1f3f4;
            border-radius: 12px;
            transition: all 0.3s ease;
        }

        #bilibili-dev-settings .toggle-switch .description {
            font-size: 13px;
            color: #666;
            margin-top: 4px;
        }

        #bilibili-dev-settings .toggle-switch label {
            display: flex;
            flex-direction: column;
            font-size: 16px;
            color: #3c4043;
            font-weight: 600;
        }

        #bilibili-dev-settings .input-group {
            background: #f8f9fa;
            border-radius: 16px;
            padding: 15px;
            margin-bottom: 16px;
            border: 2px solid transparent;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            gap: 12px;
        }

        #bilibili-dev-settings .input-group.disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }

        #bilibili-dev-settings .input-group:hover {
            background: #f1f3f4;
            border-color: rgba(242, 93, 142, 0.1);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
        }

        #bilibili-dev-settings .input-group label {
            flex: 1;
            display: flex;
            flex-direction: column;
            color: #3c4043;
            font-weight: 600;
            font-size: 15px;
        }

        #bilibili-dev-settings .input-group .description {
            font-size: 13px;
            color: #666;
            margin-top: 4px;
            font-weight: normal;
        }

        #bilibili-dev-settings .input-group input[type="number"] {
            width: 80px;
            padding: 8px;
            border: 2px solid #dadce0;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 500;
            color: #3c4043;
            transition: all 0.3s ease;
            background: #ffffff;
            -moz-appearance: textfield;
        }

        #bilibili-dev-settings .input-group .unit {
            color: #666;
            font-size: 14px;
            font-weight: normal;
            margin-left: 4px;
        }

        #bilibili-dev-settings .refresh-button {
            width: 100%;
            padding: 12px;
            background: #f25d8e;
            color: white;
            border: none;
            border-radius: 12px;
            font-size: 15px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            margin-top: 20px;
        }

        #bilibili-dev-settings .refresh-button:hover {
            background: #e74d7b;
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(242, 93, 142, 0.2);
        }

        #bilibili-dev-settings .refresh-button:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }

        #bilibili-dev-settings input:checked + .slider {
            background-color: #f25d8e;
        }

        #bilibili-dev-settings input:checked + .slider:before {
            transform: translateX(26px);
            box-shadow: 0 2px 4px rgba(242, 93, 142, 0.2);
        }

        #bilibili-dev-settings input:disabled + .slider {
            opacity: 0.5;
            cursor: not-allowed;
        }

        #bilibili-dev-settings input:disabled + .slider:before {
            box-shadow: none;
        }

        @media (max-width: 480px) {
            #bilibili-dev-settings {
                width: 95%;
                padding: 24px;
            }

            #bilibili-dev-settings .toggle-switch,
            #bilibili-dev-settings .input-group {
                padding: 14px 16px;
            }
        }

        .bpx-player-ctrl-quality.quality-button-hidden {
            display: none !important;
        }

        .quality-section {
            margin-bottom: 20px;
        }
        .quality-label {
            font-size: 14px;
            color: #666;
            margin-bottom: 10px;
            text-align: left;
        }
        .quality-group.backup-quality .quality-button {
            font-size: 12px;
            padding: 8px 6px;
        }

        .quality-settings-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            width: 40px;
            height: 100%;
            opacity: 0.9;
            transition: opacity 0.3s ease;
            position: relative;
        }

        .quality-settings-btn:hover {
            opacity: 1;
        }

        .quality-settings-btn .bpx-player-ctrl-btn-icon {
            width: 22px;
            height: 22px;
            margin-bottom: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .quality-settings-btn svg {
            width: 100%;
            height: 100%;
            stroke: #ffffff;
        }

        .quality-settings-btn::after {
            content: "自动画质面板";
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            background-color: rgba(21, 21, 21, 0.9);
            color: #fff;
            padding: 5px 8px;
            border-radius: 4px;
            font-size: 12px;
            white-space: nowrap;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.2s ease;
            margin-bottom: 5px;
        }

        .quality-settings-btn:hover::after {
            opacity: 1;
        }

        .github-link {
            position: absolute;
            top: 30px;
            right: 30px;
            width: 24px;
            height: 24px;
            cursor: pointer;
            transition: transform 0.3s ease;
        }

        .github-link:hover {
            transform: scale(1.1);
        }

        .github-link svg {
            width: 100%;
            height: 100%;
            fill: #00a1d6;
        }

        .quality-section-title {
            font-size: 16px;
            color: #00a1d6;
            font-weight: 600;
            margin: 20px 0 15px;
            padding-bottom: 8px;
            border-bottom: 2px solid rgba(0, 161, 214, 0.1);
        }

        .quality-section-description {
            font-size: 13px;
            color: #666;
            margin: -5px 0 15px;
            line-height: 1.4;
        }

        .live-quality-group {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
            margin-bottom: 25px;
        }

        .live-quality-button {
            background-color: #ffffff;
            border: 2px solid #dadce0;
            border-radius: 12px;
            padding: 12px 8px;
            font-size: 15px;
            color: #3c4043;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            font-weight: 600;
            text-align: center;
        }

        .live-quality-button:hover {
            background-color: #f1f3f4;
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        .live-quality-button.active {
            background-color: #00a1d6;
            color: white;
            border-color: #00a1d6;
            box-shadow: 0 6px 12px rgba(0, 161, 214, 0.3);
        }

        #bilibili-quality-selector,
        #bilibili-live-quality-selector,
        #bilibili-dev-settings {
            -ms-overflow-style: none;
            scrollbar-width: none;
        }
    `);

  function checkIfLivePage() {
    isLivePage = window.location.href.includes("live.bilibili.com");
  }

  function checkVipStatus() {
    if (devModeEnabled) {
      isVipUser = devModeVipStatus;
      vipStatusChecked = true;
      console.log(`开发者模式:用户是否为大会员: ${isVipUser ? "是" : "否"}`);
    } else {
      const vipElement = document.querySelector(
        ".bili-avatar-icon.bili-avatar-right-icon.bili-avatar-icon-big-vip"
      );

      const currentQuality = document.querySelector(
        ".bpx-player-ctrl-quality-menu-item.bpx-state-active .bpx-player-ctrl-quality-text"
      );

      isVipUser = vipElement !== null ||
                  (currentQuality && currentQuality.textContent.includes("大会员"));

      vipStatusChecked = true;
      console.log(`用户是否为大会员: ${isVipUser ? "是" : "否"}`);
      if (isVipUser) {
        console.log('会员判定依据:',
          vipElement ? '发现会员图标' : '当前使用会员画质'
        );
      }
    }
    updateQualityButtons(document.getElementById("bilibili-quality-selector"));
  }

  function createSettingsPanel() {
    const panel = document.createElement("div");
    panel.id = "bilibili-quality-selector";

    const QUALITIES = [
      "最高画质",
      "8K",
      "杜比视界",
      "HDR",
      "4K",
      "1080P 高码率",
      "1080P 60帧",
      "1080P 高清",
      "720P",
      "480P",
      "360P",
      "默认",
    ];

    panel.innerHTML = `
            <h2>画质设置</h2>
            <a href="https://github.com/AHCorn/Bilibili-Auto-Quality/" target="_blank" class="github-link">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
              </svg>
            </a>
            <div class="status-bar"></div>
            <div class="quality-tabs">
                <div class="quality-tab ${activeQualityTab === 'primary' ? 'active' : ''}" data-tab="primary">首选画质</div>
                <div class="quality-tab ${activeQualityTab === 'backup' ? 'active' : ''}" data-tab="backup">备选画质</div>
            </div>
            <div class="quality-section ${activeQualityTab === 'primary' ? 'active' : ''}" data-section="primary">
                <div class="quality-group">
                    ${QUALITIES.map(
                      (quality) =>
                        `<button class="quality-button" data-quality="${quality}">${quality}</button>`
                    ).join("")}
                </div>
            </div>
            <div class="quality-section ${activeQualityTab === 'backup' ? 'active' : ''}" data-section="backup">
                <div class="quality-group">
                    ${QUALITIES.map(
                      (quality) =>
                        `<button class="quality-button" data-quality="${quality}">${quality}</button>`
                    ).join("")}
                </div>
            </div>
            <div id="non-vip-warning" class="warning" style="display: none;"></div>
            <div id="quality-warning" class="warning" style="display: none;"></div>
            <div class="toggle-switch">
                <label for="hi-res-audio">Hi-Res 音质</label>
                <label class="switch">
                    <input type="checkbox" id="hi-res-audio">
                    <span class="slider vip-audio"></span>
                </label>
            </div>
            <div class="toggle-switch">
                <label for="dolby-atmos">杜比全景声</label>
                <label class="switch">
                    <input type="checkbox" id="dolby-atmos">
                    <span class="slider vip-audio"></span>
                </label>
            </div>
            <div id="audio-warning" class="warning" style="display: none;"></div>
            <div class="toggle-switch ${userBackupQualitySetting !== "最高画质" ? 'show' : 'hide'}" id="highest-quality-fallback-container">
                <label for="highest-quality-fallback">找不到备选画质时使用最高画质</label>
                <label class="switch">
                    <input type="checkbox" id="highest-quality-fallback" ${useHighestQualityFallback ? 'checked' : ''}>
                    <span class="slider"></span>
                </label>
            </div>
            <div class="toggle-switch">
                <label for="inject-quality-button">注入画质选项</label>
                <label class="switch">
                    <input type="checkbox" id="inject-quality-button" checked>
                    <span class="slider"></span>
                </label>
            </div>
        `;

    panel.querySelectorAll('.quality-tab').forEach(tab => {
      tab.addEventListener('click', () => {
        if (!isLoading) {
          const tabName = tab.dataset.tab;
          activeQualityTab = tabName;
          GM_setValue("activeQualityTab", tabName);

          panel.querySelectorAll('.quality-tab').forEach(t => t.classList.remove('active'));
          tab.classList.add('active');

          panel.querySelectorAll('.quality-section').forEach(section => {
            section.classList.toggle('active', section.dataset.section === tabName);
          });
        }
      });
    });

    panel.querySelectorAll(".quality-button").forEach((button) => {
      button.addEventListener("click", () => {
        if (!isLoading) {
          const section = button.closest('.quality-section');
          const quality = button.dataset.quality;

          if (section.dataset.section === 'primary') {
            userQualitySetting = quality;
            GM_setValue("qualitySetting", userQualitySetting);
          } else {
            userBackupQualitySetting = quality;
            GM_setValue("backupQualitySetting", userBackupQualitySetting);
            const fallbackContainer = panel.querySelector('#highest-quality-fallback-container');
            if (fallbackContainer) {
              fallbackContainer.classList.toggle('show', quality !== "最高画质");
              fallbackContainer.classList.toggle('hide', quality === "最高画质");
            }
          }

          updateQualityButtons(panel);
          selectQualityBasedOnSetting();
        }
      });
    });

    const fallbackCheckbox = panel.querySelector("#highest-quality-fallback");
    if (fallbackCheckbox) {
      fallbackCheckbox.addEventListener("change", (e) => {
        if (!isLoading) {
          useHighestQualityFallback = e.target.checked;
          GM_setValue("useHighestQualityFallback", useHighestQualityFallback);
          selectQualityBasedOnSetting();
        }
      });
    }

    panel.querySelector("#hi-res-audio").addEventListener("change", (e) => {
      if (!isLoading) {
        hiResAudioEnabled = e.target.checked;
        GM_setValue("hiResAudio", hiResAudioEnabled);
        updateQualityButtons(panel);
        selectQualityBasedOnSetting();
      }
    });

    panel.querySelector("#dolby-atmos").addEventListener("change", (e) => {
      if (!isLoading) {
        dolbyAtmosEnabled = e.target.checked;
        GM_setValue("dolbyAtmos", dolbyAtmosEnabled);
        updateQualityButtons(panel);
        selectQualityBasedOnSetting();
      }
    });

    panel.querySelector("#inject-quality-button").addEventListener("change", (e) => {
      if (!isLoading) {
        injectQualityButton = e.target.checked;
        GM_setValue("injectQualityButton", injectQualityButton);

        const qualityControlElement = document.querySelector(
          ".bpx-player-ctrl-quality"
        );
        if (qualityControlElement) {
          const settingsButton = document.createElement('div');
          settingsButton.className = 'bpx-player-ctrl-btn quality-settings-btn';
          settingsButton.innerHTML = `
            <div class="bpx-player-ctrl-btn-icon">
              <span class="bpx-common-svg-icon">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <rect x="2" y="4" width="20" height="15" rx="2" ry="2"></rect>
                  <polyline points="8 20 12 20 16 20"></polyline>
                </svg>
              </span>
            </div>
          `;
          settingsButton.addEventListener('click', () => {
            toggleSettingsPanel();
          });

          if (injectQualityButton) {
            if (!qualityControlElement.previousElementSibling?.classList.contains('quality-settings-btn')) {
              qualityControlElement.parentElement.insertBefore(settingsButton, qualityControlElement);
            }
          } else {
            const existingButton = qualityControlElement.previousElementSibling;
            if (existingButton?.classList.contains('quality-settings-btn')) {
              existingButton.remove();
            }
          }
        }
      }
    });

    document.body.appendChild(panel);
    updateQualityButtons(panel);
  }

  function updateQualityButtons(panel) {
    if (!panel) return;

    const statusBar = panel.querySelector(".status-bar");

    if (isLoading) {
      statusBar.textContent = "加载中,请稍候...";
      statusBar.className = "status-bar";
      panel
        .querySelectorAll(".quality-button, .toggle-switch")
        .forEach((el) => (el.style.opacity = "0.5"));
    } else {
      panel
        .querySelectorAll(".quality-button, .toggle-switch")
        .forEach((el) => (el.style.opacity = "1"));

      if (vipStatusChecked) {
        statusBar.textContent = isVipUser
          ? "您是大会员用户,可正常使用所有选项。"
          : "您不是大会员用户,部分会员选项不可用。";
        statusBar.className = `status-bar ${isVipUser ? "vip" : "non-vip"}`;
      }
    }

    panel.querySelectorAll('.quality-tab').forEach(tab => {
      tab.classList.toggle('active', tab.dataset.tab === activeQualityTab);
    });

    panel.querySelectorAll('.quality-section').forEach(section => {
      section.classList.toggle('active', section.dataset.section === activeQualityTab);
    });

    panel.querySelectorAll(".quality-button").forEach((button) => {
      const section = button.closest('.quality-section');
      button.classList.remove("active", "vip-quality");

      if (section.dataset.section === 'primary' && button.dataset.quality === userQualitySetting ||
          section.dataset.section === 'backup' && button.dataset.quality === userBackupQualitySetting) {
        button.classList.add("active");
        if (
          ["8K", "杜比视界", "HDR", "4K", "1080P 高码率", "1080P 60帧"].includes(button.dataset.quality)
        ) {
          button.classList.add("vip-quality");
        }
      }
    });

    const fallbackContainer = panel.querySelector('#highest-quality-fallback-container');
    if (fallbackContainer) {
      fallbackContainer.classList.toggle('show', userBackupQualitySetting !== "最高画质");
      fallbackContainer.classList.toggle('hide', userBackupQualitySetting === "最高画质");
    }

    const hiResAudioSwitch = panel.querySelector("#hi-res-audio");
    hiResAudioSwitch.checked = hiResAudioEnabled;

    const dolbyAtmosSwitch = panel.querySelector("#dolby-atmos");
    dolbyAtmosSwitch.checked = dolbyAtmosEnabled;

    const fallbackCheckbox = panel.querySelector("#highest-quality-fallback");
    if (fallbackCheckbox) {
      fallbackCheckbox.checked = useHighestQualityFallback;
    }

    updateWarnings(panel);
  }

  function updateWarnings(panel) {
    if (!panel || isLoading || !vipStatusChecked) return;

    const nonVipWarning = panel.querySelector("#non-vip-warning");
    const qualityWarning = panel.querySelector("#quality-warning");
    const audioWarning = panel.querySelector("#audio-warning");

    if (
      !isVipUser &&
      ["8K", "杜比视界", "HDR", "4K", "1080P 高码率", "1080P 60帧"].includes(
        userQualitySetting
      )
    ) {
      nonVipWarning.textContent =
        "无法使用此会员画质。已自动选择最高可用画质。";
      nonVipWarning.style.display = "block";
    } else {
      nonVipWarning.style.display = "none";
    }

    if (!isVipUser && (hiResAudioEnabled || dolbyAtmosEnabled)) {
      audioWarning.textContent = "非大会员用户不能使用高级音频选项。";
      audioWarning.style.display = "block";
    } else {
      audioWarning.style.display = "none";
    }
  }

  function selectQualityBasedOnSetting() {
    if (isLivePage) {
      selectLiveQuality();
    } else {
      selectVideoQuality();
    }
  }

  function selectVideoQuality() {
    if (!vipStatusChecked) {
      checkVipStatus();
    }

    const setAudioQuality = (retryCount = 0) => {
      if (!isVipUser) {
        console.log('非会员用户,略过音质设置');
        return;
      }

      const maxRetries = devModeEnabled ? devModeAudioRetries : 2;
      const retryInterval = devModeEnabled ? devModeAudioDelay : 4000;

      const trySetHiRes = () => {
        const hiResButton = document.querySelector(".bpx-player-ctrl-flac");
        if (!hiResButton) {
          return true;
        }
        if (isVipUser) {
          if (hiResAudioEnabled && !hiResButton.classList.contains("bpx-state-active")) {
            console.log(`尝试开启Hi-Res音质 (第${retryCount + 1}次)`);
            hiResButton.click();
          } else if (!hiResAudioEnabled && hiResButton.classList.contains("bpx-state-active")) {
            console.log(`尝试关闭Hi-Res音质 (第${retryCount + 1}次)`);
            hiResButton.click();
          }
        }
        return true;
      };

      const trySetDolby = () => {
        const dolbyButton = document.querySelector(".bpx-player-ctrl-dolby");
        if (!dolbyButton) {
          return true;
        }
        if (isVipUser) {
          if (dolbyAtmosEnabled && !dolbyButton.classList.contains("bpx-state-active")) {
            console.log(`尝试开启杜比全景声 (第${retryCount + 1}次)`);
            dolbyButton.click();
          } else if (!dolbyAtmosEnabled && dolbyButton.classList.contains("bpx-state-active")) {
            console.log(`尝试关闭杜比全景声 (第${retryCount + 1}次)`);
            dolbyButton.click();
          }
        }
        return true;
      };

      const shouldRetry = () => {
        const hiResButton = document.querySelector(".bpx-player-ctrl-flac");
        const dolbyButton = document.querySelector(".bpx-player-ctrl-dolby");

        const needRetryHiRes = hiResButton && (
          (isVipUser && hiResAudioEnabled !== hiResButton.classList.contains("bpx-state-active")) ||
          (!isVipUser && hiResButton.classList.contains("bpx-state-active"))
        );
        const needRetryDolby = dolbyButton && (
          (isVipUser && dolbyAtmosEnabled !== dolbyButton.classList.contains("bpx-state-active")) ||
          (!isVipUser && dolbyButton.classList.contains("bpx-state-active"))
        );

        return (needRetryHiRes || needRetryDolby) && retryCount < maxRetries;
      };

      console.log(`开始第${retryCount + 1}次音质设置...`);
      trySetHiRes();
      trySetDolby();

      if (shouldRetry()) {
        console.log(`音质设置未完成,${retryInterval/1000}秒后重试...`);
        setTimeout(() => {
          setAudioQuality(retryCount + 1);
        }, retryInterval);
      } else {
        console.log("音质设置完成或达到最大重试次数");
        if (takeOverQualityControl) {
          const qualityControlElement = document.querySelector(
            ".bpx-player-ctrl-btn.bpx-player-ctrl-quality"
          );
          if (qualityControlElement) {
            qualityControlElement.classList.add('quality-button-hidden');
          }
        } else {
          const qualityControlElement = document.querySelector(
            ".bpx-player-ctrl-btn.bpx-player-ctrl-quality"
          );
          if (qualityControlElement) {
            qualityControlElement.classList.remove('quality-button-hidden');
          }
        }
        updateWarnings(document.getElementById("bilibili-quality-selector"));
      }
    };

    let currentQuality = document.querySelector(
      ".bpx-player-ctrl-quality-menu-item.bpx-state-active .bpx-player-ctrl-quality-text"
    ).textContent;
    console.log(`当前画质: ${currentQuality}`);
    console.log(`目标画质: ${userQualitySetting}`);

    const qualityItems = document.querySelectorAll(
      ".bpx-player-ctrl-quality-menu .bpx-player-ctrl-quality-menu-item"
    );
    const availableQualities = Array.from(qualityItems).map((item) => ({
      name: item.textContent.trim(),
      element: item,
      isVipOnly: !!item.querySelector(".bpx-player-ctrl-quality-badge-bigvip"),
      isFreeNow: !!item.querySelector(".bpx-player-ctrl-quality-badge-bigvip")?.textContent.includes("限免中")
    }));

    console.log(
      `当前视频可用画质:`,
      availableQualities.map((q) => q.name)
    );

    const qualityPreferences = [
      "8K",
      "杜比视界",
      "HDR",
      "4K",
      "1080P 高码率",
      "1080P 60帧",
      "1080P 高清",
      "720P 60帧",
      "720P",
      "480P",
      "360P",
      "默认",
    ];

    availableQualities.sort((a, b) => {
      const getQualityIndex = (name) => {
        for (let i = 0; i < qualityPreferences.length; i++) {
          if (name.includes(qualityPreferences[i])) {
            return i;
          }
        }
        return qualityPreferences.length;
      };
      return getQualityIndex(a.name) - getQualityIndex(b.name);
    });

    let targetQuality;
    const cleanQuality = (quality) => quality?.replace(/大会员|限免中/g, '').trim();
    const currentCleanQuality = cleanQuality(currentQuality);

    if (userQualitySetting === "最高画质") {
      const hasFreeVipQualities = availableQualities.some(quality => quality.isFreeNow);
      if (isVipUser || hasFreeVipQualities) {
        targetQuality = availableQualities[0];
      } else {
        targetQuality = availableQualities.find(quality =>
          cleanQuality(quality.name).includes(userBackupQualitySetting)
        );
        if (!targetQuality && useHighestQualityFallback) {
          targetQuality = availableQualities.find(quality => !quality.isVipOnly);
        }
        if (!targetQuality && !useHighestQualityFallback) {
          console.log("未找到备选画质,且未启用最高画质兜底,保持当前画质");
          const hiResButton = document.querySelector(".bpx-player-ctrl-flac");
          const dolbyButton = document.querySelector(".bpx-player-ctrl-dolby");

          console.log(`是否存在Hi-Res音质按钮:${hiResButton ? '是' : '否'}`);
          let needSetHiRes = false;
          if (hiResButton) {
            const hiResActive = hiResButton.classList.contains("bpx-state-active");
            console.log(`Hi-Res音质按钮当前状态:${hiResActive ? '已开启' : '已关闭'}`);
            needSetHiRes = (isVipUser && hiResAudioEnabled !== hiResActive) ||
                          (!isVipUser && hiResActive);
          }

          console.log(`是否存在杜比全景声按钮:${dolbyButton ? '是' : '否'}`);
          let needSetDolby = false;
          if (dolbyButton) {
            const dolbyActive = dolbyButton.classList.contains("bpx-state-active");
            console.log(`杜比全景声按钮当前状态:${dolbyActive ? '已开启' : '已关闭'}`);
            needSetDolby = (isVipUser && dolbyAtmosEnabled !== dolbyActive) ||
                          (!isVipUser && dolbyActive);
          }

          if (needSetHiRes || needSetDolby) {
            console.log("开始设置音质...");
            setAudioQuality();
          } else {
            console.log("音质按钮状态已符合设置,无需调整");
          }
          return;
        }
      }
    } else if (userQualitySetting === "默认") {
      console.log("使用默认画质");
      const hiResButton = document.querySelector(".bpx-player-ctrl-flac");
      const dolbyButton = document.querySelector(".bpx-player-ctrl-dolby");

      console.log(`是否存在Hi-Res音质按钮:${hiResButton ? '是' : '否'}`);
      let needSetHiRes = false;
      if (hiResButton) {
        const hiResActive = hiResButton.classList.contains("bpx-state-active");
        console.log(`Hi-Res音质按钮当前状态:${hiResActive ? '已开启' : '已关闭'}`);
        needSetHiRes = (isVipUser && hiResAudioEnabled !== hiResActive) ||
                      (!isVipUser && hiResActive);
      }

      console.log(`是否存在杜比全景声按钮:${dolbyButton ? '是' : '否'}`);
      let needSetDolby = false;
      if (dolbyButton) {
        const dolbyActive = dolbyButton.classList.contains("bpx-state-active");
        console.log(`杜比全景声按钮当前状态:${dolbyActive ? '已开启' : '已关闭'}`);
        needSetDolby = (isVipUser && dolbyAtmosEnabled !== dolbyActive) ||
                      (!isVipUser && dolbyActive);
      }

      if (needSetHiRes || needSetDolby) {
        console.log("开始设置音质...");
        setAudioQuality();
      } else {
        console.log("音质按钮状态已符合设置,无需调整");
      }
      return;
    } else {
      targetQuality = availableQualities.find((quality) =>
        cleanQuality(quality.name).includes(userQualitySetting)
      );
      if (!targetQuality) {
        console.log(`未找到目标画质 ${userQualitySetting}, 尝试使用备选画质`);
        targetQuality = availableQualities.find(quality =>
          cleanQuality(quality.name).includes(userBackupQualitySetting)
        );
        if (!targetQuality && useHighestQualityFallback) {
          targetQuality = isVipUser
            ? availableQualities[0]
            : availableQualities.find((quality) => !quality.isVipOnly);
        }
        if (!targetQuality && !useHighestQualityFallback) {
          console.log("未找到备选画质,且未启用最高画质兜底,保持当前画质");
          const hiResButton = document.querySelector(".bpx-player-ctrl-flac");
          const dolbyButton = document.querySelector(".bpx-player-ctrl-dolby");

          console.log(`是否存在Hi-Res音质按钮:${hiResButton ? '是' : '否'}`);
          let needSetHiRes = false;
          if (hiResButton) {
            const hiResActive = hiResButton.classList.contains("bpx-state-active");
            console.log(`Hi-Res音质按钮当前状态:${hiResActive ? '已开启' : '已关闭'}`);
            needSetHiRes = (isVipUser && hiResAudioEnabled !== hiResActive) ||
                          (!isVipUser && hiResActive);
          }

          console.log(`是否存在杜比全景声按钮:${dolbyButton ? '是' : '否'}`);
          let needSetDolby = false;
          if (dolbyButton) {
            const dolbyActive = dolbyButton.classList.contains("bpx-state-active");
            console.log(`杜比全景声按钮当前状态:${dolbyActive ? '已开启' : '已关闭'}`);
            needSetDolby = (isVipUser && dolbyAtmosEnabled !== dolbyActive) ||
                          (!isVipUser && dolbyActive);
          }

          if (needSetHiRes || needSetDolby) {
            console.log("开始设置音质...");
            setAudioQuality();
          } else {
            console.log("音质按钮状态已符合设置,无需调整");
          }
          return;
        }
      }
    }

    console.log(`实际目标画质: ${targetQuality.name}`);
    targetQuality.element.click();

    if (qualityDoubleCheck) {
      setTimeout(() => {
        const currentQualityAfterSwitch = document.querySelector(
          ".bpx-player-ctrl-quality-menu-item.bpx-state-active .bpx-player-ctrl-quality-text"
        )?.textContent;

        const cleanQuality = (quality) => quality?.replace(/大会员|限免中/g, '').trim();
        const currentCleanQuality = cleanQuality(currentQualityAfterSwitch);
        const targetCleanQuality = cleanQuality(targetQuality.name);

        if (currentQualityAfterSwitch && currentCleanQuality !== targetCleanQuality) {
          console.log(`画质切换可能未成功,当前画质: ${currentQualityAfterSwitch},目标画质: ${targetQuality.name},执行二次切换...`);
          targetQuality.element.click();
        } else {
          console.log(`画质切换验证成功,当前画质: ${currentQualityAfterSwitch}`);
        }

        const hiResButton = document.querySelector(".bpx-player-ctrl-flac");
        const dolbyButton = document.querySelector(".bpx-player-ctrl-dolby");

        console.log(`是否存在Hi-Res音质按钮:${hiResButton ? '是' : '否'}`);
        let needSetHiRes = false;
        if (hiResButton) {
          const hiResActive = hiResButton.classList.contains("bpx-state-active");
          console.log(`Hi-Res音质按钮当前状态:${hiResActive ? '已开启' : '已关闭'}`);
          needSetHiRes = (isVipUser && hiResAudioEnabled !== hiResActive) ||
                        (!isVipUser && hiResActive);
        }

        console.log(`是否存在杜比全景声按钮:${dolbyButton ? '是' : '否'}`);
        let needSetDolby = false;
        if (dolbyButton) {
          const dolbyActive = dolbyButton.classList.contains("bpx-state-active");
          console.log(`杜比全景声按钮当前状态:${dolbyActive ? '已开启' : '已关闭'}`);
          needSetDolby = (isVipUser && dolbyAtmosEnabled !== dolbyActive) ||
                        (!isVipUser && dolbyActive);
        }

        if (needSetHiRes || needSetDolby) {
          console.log("开始设置音质...");
          setAudioQuality();
        } else {
          console.log("音质按钮状态已符合设置,无需调整");
        }
      }, 5000);
    }
  }

  function createLiveSettingsPanel() {
    const panel = document.createElement("div");
    panel.id = "bilibili-live-quality-selector";

    const updatePanel = () => {
      const qualityCandidates =
        unsafeWindow.livePlayer.getPlayerInfo().qualityCandidates;
      const LIVE_QUALITIES = ["原画", "蓝光","超清", "高清"];

      const lineSelector = document.querySelector(".YccudlUCmLKcUTg_yzKN");
      const lines = lineSelector
        ? Array.from(lineSelector.children).map((li) => li.textContent)
        : ["加载中..."];
      const currentLineIndex = lineSelector
        ? Array.from(lineSelector.children).findIndex((li) =>
            li.classList.contains("fG2r2piYghHTQKQZF8bl")
          )
        : 0;

      panel.innerHTML = `
            <h2>直播设置</h2>
            <a href="https://github.com/AHCorn/Bilibili-Auto-Quality/" target="_blank" class="github-link">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
              </svg>
            </a>
            <div class="quality-section-title">线路选择</div>
            <div class="line-group">
                ${lines
                  .map(
                    (line, index) =>
                      `<button class="line-button ${
                        index === currentLineIndex ? "active" : ""
                      }" data-line="${index}">${line}</button>`
                  )
                  .join("")}
            </div>
            <div class="quality-section-title">画质选择</div>
            <div class="live-quality-group">
                ${LIVE_QUALITIES.map(
                  (quality) =>
                    `<button class="live-quality-button ${
                      quality === userLiveQualitySetting ? "active" : ""
                    }" data-quality="${quality}">${quality}</button>`
                ).join("")}
            </div>
        `;

      panel.querySelectorAll(".line-button").forEach((button) => {
        button.addEventListener("click", () => {
          const lineIndex = parseInt(button.dataset.line);
          changeLine(lineIndex);
        });
      });

      panel.querySelectorAll(".live-quality-button").forEach((button) => {
        button.addEventListener("click", () => {
          userLiveQualitySetting = button.dataset.quality;
          GM_setValue("liveQualitySetting", userLiveQualitySetting);
          updatePanel();
          selectLiveQuality();
        });
      });
    };

    document.body.appendChild(panel);
    panel.updatePanel = updatePanel;
    updatePanel();
  }

  function selectLiveQuality() {
    return new Promise((resolve) => {
      const timer = setInterval(() => {
        if (
          unsafeWindow.livePlayer &&
          unsafeWindow.livePlayer.getPlayerInfo &&
          unsafeWindow.livePlayer.getPlayerInfo().playurl &&
          unsafeWindow.livePlayer.switchQuality
        ) {
          clearInterval(timer);
          resolve();
        }
      }, 1000);
    }).then(() => {
      const qualityCandidates =
        unsafeWindow.livePlayer.getPlayerInfo().qualityCandidates;

      console.log("可用画质选项:");
      qualityCandidates.forEach((quality, index) => {
        console.log(`${index + 1}. ${quality.desc} (qn: ${quality.qn})`);
      });

      console.log(`选择的画质: ${userLiveQualitySetting}`);

      let targetQuality;

      targetQuality = qualityCandidates.find(
        (q) => q.desc === userLiveQualitySetting
      );

      if (!targetQuality) {
        const qualityPriority = ["原画", "蓝光", "超清", "高清"];
        for (let quality of qualityPriority) {
          targetQuality = qualityCandidates.find((q) => q.desc === quality);
          if (targetQuality) break;
        }
      }

      if (!targetQuality) {
        targetQuality = qualityCandidates[0];
      }

      const targetQualityNumber = targetQuality.qn;
      const targetQualityName = targetQuality.desc;

      console.log(
        `目标画质:${targetQualityName} (qn: ${targetQualityNumber})`
      );

      const switchQuality = () => {
        const currentQualityNumber =
          unsafeWindow.livePlayer.getPlayerInfo().quality;
        if (currentQualityNumber !== targetQualityNumber) {
          unsafeWindow.livePlayer.switchQuality(targetQualityNumber);
          console.log(`已切换到目标画质:${targetQualityName}`);
          userLiveQualitySetting = targetQualityName;
          GM_setValue("liveQualitySetting", userLiveQualitySetting);
          updateLiveSettingsPanel();

          if (liveQualityDoubleCheck) {
            setTimeout(() => {
              const currentQualityAfterSwitch = unsafeWindow.livePlayer.getPlayerInfo().quality;
              if (currentQualityAfterSwitch !== targetQualityNumber) {
                console.log(`直播画质切换可能未成功,当前画质: ${currentQualityAfterSwitch},目标画质: ${targetQualityNumber},执行二次切换...`);
                unsafeWindow.livePlayer.switchQuality(targetQualityNumber);
              } else {
                console.log(`直播画质切换验证成功,当前画质: ${targetQualityName}`);
              }
            }, 5000);
          }
        } else {
          console.log(`已经是目标画质:${targetQualityName}`);
        }
      };

      switchQuality();
    });
  }

  function changeLine(lineIndex) {
    const lineSelector = document.querySelector(".YccudlUCmLKcUTg_yzKN");
    if (lineSelector && lineSelector.children[lineIndex]) {
      lineSelector.children[lineIndex].click();
      console.log(
        `已切换到线路:${lineSelector.children[lineIndex].textContent}`
      );
      const panel = document.getElementById("bilibili-live-quality-selector");
      if (panel) {
        panel.querySelectorAll(".line-button").forEach((button, index) => {
          if (index === lineIndex) {
            button.classList.add("active");
          } else {
            button.classList.remove("active");
          }
        });
      }
    } else {
      console.log("无法切换线路");
    }
  }

  function observeLineChanges() {
    const lineSelector = document.querySelector(".YccudlUCmLKcUTg_yzKN");
    if (lineSelector) {
      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (
            mutation.type === "attributes" &&
            mutation.attributeName === "class"
          ) {
            const currentLineIndex = Array.from(
              lineSelector.children
            ).findIndex((li) => li.classList.contains("fG2r2piYghHTQKQZF8bl"));
            updateLiveSettingsPanel();
          }
        });
      });

      observer.observe(lineSelector, {
        attributes: true,
        subtree: true,
        attributeFilter: ["class"],
      });
    }
  }

  function updateLiveSettingsPanel() {
    const panel = document.getElementById("bilibili-live-quality-selector");
    if (panel && typeof panel.updatePanel === "function") {
      panel.updatePanel();
    }
  }

  function togglePanel(panelId, createPanelFunc, updateFunc) {
    let panel = document.getElementById(panelId);
    if (!panel) {
      createPanelFunc();
      panel = document.getElementById(panelId);
    }

    const handleOutsideClick = (event) => {
      if (panel && !panel.contains(event.target)) {
        panel.classList.remove("show");
        document.removeEventListener("mousedown", handleOutsideClick);
      }
    };

    if (!panel.classList.contains("show")) {
      ["bilibili-quality-selector", "bilibili-live-quality-selector", "bilibili-dev-settings"].forEach(id => {
        if (id !== panelId) {
          const otherPanel = document.getElementById(id);
          if (otherPanel?.classList.contains("show")) {
            otherPanel.classList.remove("show");
          }
        }
      });

      panel.classList.add("show");
      document.addEventListener("mousedown", handleOutsideClick);
    } else {
      panel.classList.remove("show");
      document.removeEventListener("mousedown", handleOutsideClick);
    }

    if (updateFunc) {
      updateFunc(panel);
    }
  }

  function toggleSettingsPanel() {
    togglePanel("bilibili-quality-selector", createSettingsPanel, updateQualityButtons);
  }

  function toggleLiveSettingsPanel() {
    togglePanel("bilibili-live-quality-selector", createLiveSettingsPanel, updateLiveSettingsPanel);
  }

  function toggleDevSettingsPanel() {
    togglePanel("bilibili-dev-settings", createDevSettingsPanel, panel => {
      const removeQualityButton = panel.querySelector('#remove-quality-button');
      if (removeQualityButton) {
        removeQualityButton.checked = takeOverQualityControl;
      }
    });
  }

  GM_registerMenuCommand("设置面板", () => {
    checkIfLivePage();
    if (isLivePage) {
      toggleLiveSettingsPanel();
    } else {
      toggleSettingsPanel();
    }
  });

  GM_registerMenuCommand("开发者选项", toggleDevSettingsPanel);

  window.addEventListener("load", () => {
    if (isLivePage) {
      observeLineChanges();
    }
  });

  window.onload = function () {
    checkIfLivePage();
    if (isLivePage) {
      selectLiveQuality().then(() => {
        createLiveSettingsPanel();
      });
    } else {
      const DOM = {
        selectors: {
          qualityControl: '.bpx-player-ctrl-quality',
          qualityButton: '.bpx-player-ctrl-btn.bpx-player-ctrl-quality',
          playerControls: '.bpx-player-control-wrap',
          headerAvatar: '.v-popover-wrap.header-avatar-wrap',
          vipIcon: '.bili-avatar-icon.bili-avatar-right-icon.bili-avatar-icon-big-vip',
          qualityMenu: '.bpx-player-ctrl-quality-menu',
          qualityMenuItem: '.bpx-player-ctrl-quality-menu-item',
          activeQuality: '.bpx-player-ctrl-quality-menu-item.bpx-state-active .bpx-player-ctrl-quality-text',
          controlBottomRight: '.bpx-player-control-bottom-right'
        },
        elements: {
          qualitySelector: null,
          playerControls: null,
          headerAvatar: null,
          controlBottomRight: null
        },
        get: function(key) {
          if (!this.elements[key]) {
            this.elements[key] = document.querySelector(this.selectors[key]);
          }
          return this.elements[key];
        },
        refresh: function(key) {
          this.elements[key] = document.querySelector(this.selectors[key]);
          return this.elements[key];
        },
        clear: function() {
          this.elements = {
            qualitySelector: null,
            playerControls: null,
            headerAvatar: null,
            controlBottomRight: null
          };
        }
      };

      const hideQualityButton = () => {
        const qualityControl = DOM.get('qualityButton');
        if (qualityControl && takeOverQualityControl) {
          qualityControl.classList.add('quality-button-hidden');
        }
      };

      const initQualitySettingsButton = () => {
        const controlBottomRight = DOM.get('controlBottomRight');
        const qualityControl = DOM.get('qualityControl');

        if (controlBottomRight && qualityControl && injectQualityButton) {
          const existingSettingsBtn = controlBottomRight.querySelector('.quality-settings-btn');
          if (!existingSettingsBtn) {
            const settingsButton = document.createElement('div');
            settingsButton.className = 'bpx-player-ctrl-btn quality-settings-btn';
            settingsButton.innerHTML = `
              <div class="bpx-player-ctrl-btn-icon">
                <span class="bpx-common-svg-icon">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <rect x="2" y="4" width="20" height="15" rx="2" ry="2"></rect>
                    <polyline points="8 20 12 20 16 20"></polyline>
                  </svg>
                </span>
              </div>
            `;
            settingsButton.addEventListener('click', toggleSettingsPanel);
            qualityControl.parentElement.insertBefore(settingsButton, qualityControl);

            if (playerControlsObserver) {
              playerControlsObserver.disconnect();
            }
          }
        }
      };

      hideQualityButton();
      initQualitySettingsButton();

      const playerControlsObserver = new MutationObserver((mutations) => {
        const qualityControl = DOM.refresh('qualityControl');
        if (qualityControl) {
          hideQualityButton();
          initQualitySettingsButton();
        }
      });

      const playerControls = DOM.get('playerControls');
      if (playerControls) {
        playerControlsObserver.observe(playerControls, {
          childList: true,
          subtree: true
        });
      }

      let hasElementAppeared = false;
      const vipCheckObserver = new MutationObserver(function (mutations, me) {
        const headerElement = DOM.get('headerAvatar');
        if (headerElement) {
          hasElementAppeared = true;
          console.log("正在判断用户是否为会员...");

          me.disconnect();

          setTimeout(() => {
            initializeQualitySettings();
            console.log(`脚本开始运行,${devModeEnabled ? devModeDelay/1000 : 2.5}秒后切换画质`);
          }, devModeEnabled ? devModeDelay : 2500);
        }
      });

      vipCheckObserver.observe(document.body, {
        childList: true,
        subtree: true,
      });

      window.addEventListener('popstate', () => DOM.clear());
      window.addEventListener('beforeunload', () => DOM.clear());
    }
  };

  const parentElement = document.body;

  let initialTitle = '';
  let isFirstLoad = true;

  function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  const initTitleObserver = setInterval(() => {
    const titleElement = document.querySelector('.video-title');
    if (titleElement) {
      if (!document.hidden) {
        if (isFirstLoad) {
          initialTitle = titleElement.textContent.trim();
          console.log('记录初始标题:', initialTitle);
          isFirstLoad = false;
        }

        titleObserver.observe(titleElement, {
          childList: true,
          subtree: true,
          characterData: true
        });
        console.log('已启动标题监听器');
      }
      clearInterval(initTitleObserver);
    }
  }, 3000);

  const handleTitleChange = debounce((title) => {
    if (title === initialTitle) {
      console.log('标题未发生变化');
      return;
    }

    console.log('视频标题发生变化:', title);
    initialTitle = title;
    isLoading = true;
    updateQualityButtons(document.getElementById("bilibili-quality-selector"));
    setTimeout(() => {
      isLoading = false;
      checkVipStatus();
      selectQualityBasedOnSetting();
      updateQualityButtons(document.getElementById("bilibili-quality-selector"));
    }, 3500);
  }, 100);

  const titleObserver = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.target.classList.contains('video-title')) {
        handleTitleChange(mutation.target.textContent.trim());
      }
    });
  });

  function handleVisibilityChange() {
    const titleElement = document.querySelector('.video-title');
    if (document.hidden) {
      console.log('页面不可见,暂停标题监听');
      titleObserver.disconnect();
    } else {
      console.log('页面可见,恢复标题监听');
      if (titleElement) {
        titleObserver.observe(titleElement, {
          childList: true,
          subtree: true,
          characterData: true
        });
      }
    }
  }

  document.addEventListener('visibilitychange', handleVisibilityChange);

  window.addEventListener('unload', () => {
    clearInterval(initTitleObserver);
    titleObserver.disconnect();
    document.removeEventListener('visibilitychange', handleVisibilityChange);
  });

  function createDevSettingsPanel() {
    const panel = document.createElement("div");
    panel.id = "bilibili-dev-settings";

    panel.innerHTML = `
        <h2>开发者设置</h2>
        <a href="https://github.com/AHCorn/Bilibili-Auto-Quality/" target="_blank" class="github-link">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
            <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
          </svg>
        </a>
        <div class="dev-warning">以下选项的错误配置可能会影响脚本正常工作</div>
        <div class="toggle-switch">
            <label for="dev-mode">
              开发者模式
              <div class="description">启用后可以使用开发者选项</div>
            </label>
            <label class="switch">
                <input type="checkbox" id="dev-mode" ${devModeEnabled ? 'checked' : ''}>
                <span class="slider"></span>
            </label>
        </div>
        <div class="toggle-switch">
            <label for="quality-double-check">
              视频画质二次验证
              <div class="description">启用后将在视频画质切换5秒后进行验证</div>
            </label>
            <label class="switch">
                <input type="checkbox" id="quality-double-check" ${qualityDoubleCheck ? 'checked' : ''} ${!devModeEnabled ? 'disabled' : ''}>
                <span class="slider"></span>
            </label>
        </div>
        <div class="toggle-switch">
            <label for="live-quality-double-check">
              直播画质二次验证
              <div class="description">启用后将在直播画质切换5秒后进行验证</div>
            </label>
            <label class="switch">
                <input type="checkbox" id="live-quality-double-check" ${liveQualityDoubleCheck ? 'checked' : ''} ${!devModeEnabled ? 'disabled' : ''}>
                <span class="slider"></span>
            </label>
        </div>
        <div class="toggle-switch">
            <label for="dev-vip">
              模拟大会员状态
              <div class="description">模拟脚本所识别到的大会员状态,<b>并非破解</b></div>
            </label>
            <label class="switch">
                <input type="checkbox" id="dev-vip" ${devModeVipStatus ? 'checked' : ''} ${!devModeEnabled ? 'disabled' : ''}>
                <span class="slider"></span>
            </label>
        </div>
        <div class="toggle-switch">
            <label for="dev-ua">
              禁用 UA 修改
              <div class="description">禁用后部分旧版本浏览器可能无法解锁画质</div>
            </label>
            <label class="switch">
                <input type="checkbox" id="dev-ua" ${devModeDisableUA ? 'checked' : ''} ${!devModeEnabled ? 'disabled' : ''}>
                <span class="slider"></span>
            </label>
        </div>
        <div class="toggle-switch">
            <label for="remove-quality-button">
              移除清晰度按钮
              <div class="description">启用后将隐藏播放器的清晰度按钮</div>
            </label>
            <label class="switch">
                <input type="checkbox" id="remove-quality-button" ${takeOverQualityControl ? 'checked' : ''} ${!devModeEnabled ? 'disabled' : ''}>
                <span class="slider"></span>
            </label>
        </div>
        <div class="input-group ${!devModeEnabled ? 'disabled' : ''}">
            <label for="dev-delay">
              画质切换延迟
              <div class="description">画质切换的等待时间</div>
            </label>
            <input type="number" id="dev-delay" value="${devModeDelay}" min="0" max="10000" step="100" ${!devModeEnabled ? 'disabled' : ''}>
            <span class="unit">毫秒</span>
        </div>
        <div class="input-group ${!devModeEnabled ? 'disabled' : ''}">
            <label for="dev-audio-delay">
              音质切换延迟
              <div class="description">音质切换的等待时间</div>
            </label>
            <input type="number" id="dev-audio-delay" value="${devModeAudioDelay}" min="0" max="10000" step="100" ${!devModeEnabled ? 'disabled' : ''}>
            <span class="unit">毫秒</span>
        </div>
        <div class="input-group ${!devModeEnabled ? 'disabled' : ''}">
            <label for="dev-audio-retries">
              音质切换重试次数
              <div class="description">音质切换失败后的重试次数</div>
            </label>
            <input type="number" id="dev-audio-retries" value="${devModeAudioRetries}" min="0" max="5" step="1" ${!devModeEnabled ? 'disabled' : ''}>
            <span class="unit" style="margin-left: 15px;">次</span>
        </div>
        <div id="dev-warning" class="warning" style="display: none;"></div>
        <button class="refresh-button" ${!devModeEnabled ? 'disabled' : ''}>确认并刷新页面</button>
    `;

    document.body.appendChild(panel);

    panel.querySelector('#dev-mode').addEventListener('change', (e) => {
        devModeEnabled = e.target.checked;
        GM_setValue("devModeEnabled", devModeEnabled);

        const vipSwitch = panel.querySelector('#dev-vip');
        const uaSwitch = panel.querySelector('#dev-ua');
        const delayInput = panel.querySelector('#dev-delay');
        const audioDelayInput = panel.querySelector('#dev-audio-delay');
        const audioRetriesInput = panel.querySelector('#dev-audio-retries');
        const qualityDoubleCheckSwitch = panel.querySelector('#quality-double-check');
        const removeQualityButton = panel.querySelector('#remove-quality-button');
        const refreshButton = panel.querySelector('.refresh-button');
        const warning = panel.querySelector('#dev-warning');
        const inputGroups = panel.querySelectorAll('.input-group');

        [vipSwitch, uaSwitch, delayInput, audioDelayInput, audioRetriesInput,
         qualityDoubleCheckSwitch, removeQualityButton].forEach(control => {
            if (control) {
                control.disabled = !devModeEnabled;
            }
        });

        if (refreshButton) {
            refreshButton.disabled = !devModeEnabled;
        }

        inputGroups.forEach(group => {
            group.classList.toggle('disabled', !devModeEnabled);
        });

        if (!devModeEnabled) {
            vipStatusChecked = false;
            checkVipStatus();
            if (devModeDisableUA) {
                warning.textContent = "开发者模式已关闭,UA 修改将在刷新页面后恢复";
                warning.style.display = "block";
            }
        } else {
            warning.style.display = "none";
        }
    });

    panel.querySelector('#dev-vip').addEventListener('change', (e) => {
        devModeVipStatus = e.target.checked;
        GM_setValue("devModeVipStatus", devModeVipStatus);
        if (devModeEnabled) {
            isVipUser = devModeVipStatus;
            vipStatusChecked = true;
            updateQualityButtons(document.getElementById("bilibili-quality-selector"));
        }
    });

    panel.querySelector('#dev-ua').addEventListener('change', (e) => {
        devModeDisableUA = e.target.checked;
        GM_setValue("devModeDisableUA", devModeDisableUA);
        const warning = panel.querySelector('#dev-warning');
        warning.textContent = devModeDisableUA ?
            "UA 修改已禁用,请刷新页面生效" :
            "UA 修改已启用,请刷新页面生效";
        warning.style.display = "block";
    });

    panel.querySelector('#remove-quality-button').addEventListener('change', (e) => {
        takeOverQualityControl = e.target.checked;
        GM_setValue("takeOverQualityControl", takeOverQualityControl);
        const qualityControlElement = document.querySelector(
            ".bpx-player-ctrl-btn.bpx-player-ctrl-quality"
        );
        if (qualityControlElement) {
            qualityControlElement.classList.toggle('quality-button-hidden', takeOverQualityControl);
        }
        const warning = panel.querySelector('#dev-warning');
        warning.textContent = "清晰度按钮设置已更改,请刷新页面生效";
        warning.style.display = "block";
    });

    panel.querySelector('#dev-delay').addEventListener('change', (e) => {
        devModeDelay = parseInt(e.target.value);
        GM_setValue("devModeDelay", devModeDelay);
    });

    panel.querySelector('#dev-audio-delay').addEventListener('change', (e) => {
        devModeAudioDelay = parseInt(e.target.value);
        GM_setValue("devModeAudioDelay", devModeAudioDelay);
    });

    panel.querySelector('#dev-audio-retries').addEventListener('change', (e) => {
        devModeAudioRetries = parseInt(e.target.value);
        GM_setValue("devModeAudioRetries", devModeAudioRetries);
    });

    panel.querySelector('.refresh-button').addEventListener('click', () => {
        location.reload();
    });

    panel.querySelector('#quality-double-check').addEventListener('change', (e) => {
        qualityDoubleCheck = e.target.checked;
        GM_setValue("qualityDoubleCheck", qualityDoubleCheck);
    });

    panel.querySelector('#live-quality-double-check').addEventListener('change', (e) => {
        liveQualityDoubleCheck = e.target.checked;
        GM_setValue("liveQualityDoubleCheck", liveQualityDoubleCheck);
    });

    return panel;
  }

  const initializeQualitySettings = () => {
    isLoading = false;
    checkVipStatus();
    selectVideoQuality();
    updateQualityButtons(document.getElementById("bilibili-quality-selector"));
  };
})();