Greasy Fork is available in English.

哔哩哔哩自动画质

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

Установить этот скрипт?
Рекомендуемый автором скрипт

Вам также может понравится 哔哩哔哩订阅管理 / 批量取消订阅合集.

Установить этот скрипт
// ==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"));
  };
})();