Greasy Fork

Greasy Fork is available in English.

哔哩哔哩自动画质

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

当前为 2024-05-19 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         哔哩哔哩自动画质
// @namespace    https://github.com/AHCorn/Bilibili-Auto-Quality/
// @version      2.5.2
// @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/*
// @match        *://live.bilibili.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function () {
    'use strict';

    Object.defineProperty(navigator, 'userAgent', {
        value: "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15"
    });

    window.localStorage['bilibili_player_force_DolbyAtmos&8K&HDR'] = 1;

    GM_addStyle(`
        #bilibili-quality-selector {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #f8f8f8;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
            display: none;
            z-index: 10000;
            width: 300px;
            text-align: center;
            border: 1px solid #ddd;
        }
        #bilibili-quality-selector button {
            display: block;
            width: 90%;
            margin: 5px auto;
            padding: 10px;
            border: 1px solid #007bff;
            border-radius: 5px;
            background-color: white;
            color: #007bff;
            cursor: pointer;
            font-size: 16px;
            transition: all 0.3s ease;
        }
        #bilibili-quality-selector button.active {
            background-color: #007bff;
            color: white;
        }
        #bilibili-quality-selector button:hover {
            background-color: #0056b3;
            color: white;
        }
        #bilibili-quality-selector button.active:hover {
            background-color: #003f7f;
        }
    `);

    let hiResAudioEnabled = GM_getValue('hiResAudio', false);
    let dolbyAtmosEnabled = GM_getValue('dolbyAtmos', false);
    let userQualitySetting = GM_getValue('qualitySetting', ' 自动选择最高画质 ');
    let userHasChangedQuality = false;
    let takeOverQualityControl = GM_getValue('takeOverQualityControl', false);

    function isVipUser() {
        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');
        return vipElement !== null || (currentQuality && currentQuality.textContent.includes('大会员'));
    }

    function selectQualityBasedOnSetting() {
        if (userHasChangedQuality) return;

        const isVip = isVipUser();
        console.log(`用户是否为大会员:${isVip ? '是' : '否'}`);
        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')
            }))
            .filter(quality => isVip || !quality.isVipOnly);

        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;
        if (userQualitySetting === ' 自动选择最高画质 ') {
            targetQuality = availableQualities[0];
        } else {
            targetQuality = availableQualities.find(quality => quality.name.includes(userQualitySetting));
            if (!targetQuality) {
                console.log(`未找到目标画质 ${userQualitySetting},将选择最高可用画质`);
                targetQuality = availableQualities[0];
            }
        }

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

        setTimeout(() => {
            currentQuality = document.querySelector('.bpx-player-ctrl-quality-menu-item.bpx-state-active .bpx-player-ctrl-quality-text').textContent;

            const getCurrentQualityIndex = (name) => {
                for (let i = 0; i < qualityPreferences.length; i++) {
                    if (name.includes(qualityPreferences[i])) {
                        return i;
                    }
                }
                return qualityPreferences.length;
            };

            const currentQualityIndex = getCurrentQualityIndex(currentQuality);
            const targetQualityIndex = getCurrentQualityIndex(targetQuality.name);

            console.log(`二次切换检查 - 当前画质索引:${currentQualityIndex}, 目标画质索引:${targetQualityIndex}`); // 索引画质,延后10秒比对,不相符就再切换一次
            if (currentQualityIndex === targetQualityIndex) {
                console.log("当前画质和目标画质相符,无需执行二次切换");
            } else {
                console.log("当前画质和目标画质不相符,执行二次切换");
                targetQuality.element.click();
            }
        }, 7000);

            const hiResButton = document.querySelector('.bpx-player-ctrl-flac');
            if (hiResButton) {
                if (isVip) {
                    if (hiResAudioEnabled && !hiResButton.classList.contains('bpx-state-active')) {
                        hiResButton.click();
                    } else if (!hiResAudioEnabled && hiResButton.classList.contains('bpx-state-active')) {
                        hiResButton.click();
                    }
                } else {
                    if (hiResButton.classList.contains('bpx-state-active')) {
                        hiResButton.click();
                    }
                }
            }

            const dolbyButton = document.querySelector('.bpx-player-ctrl-dolby');
            if (dolbyButton) {
                if (isVip) {
                    if (dolbyAtmosEnabled && !dolbyButton.classList.contains('bpx-state-active')) {
                        dolbyButton.click();
                    } else if (!dolbyAtmosEnabled && dolbyButton.classList.contains('bpx-state-active')) {
                        dolbyButton.click();
                    }
                } else {
                    if (dolbyButton.classList.contains('bpx-state-active')) {
                        dolbyButton.click();
                    }
                }
            }

            if (takeOverQualityControl) {
                const qualityControlElement = document.querySelector('.bpx-player-ctrl-btn.bpx-player-ctrl-quality');
                if (qualityControlElement) {
                    qualityControlElement.remove();
                }
            }
        }

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

            const QUALITIES = [' 自动选择最高画质 ', '8K', '杜比视界','HDR', '4K', '1080P 高码率', '1080P 60 帧', '1080P', '720P', '480P', '360P']; // 本次更新:移除 720P 60帧选项,该画质似乎已被B站移除
            QUALITIES.forEach(quality => {
                const button = document.createElement('button');
                button.textContent = quality;
                button.onclick = () => {
                    userQualitySetting = quality;
                    GM_setValue('qualitySetting', quality);
                    userHasChangedQuality = true;
                    updateQualityButtons(panel);
                    selectQualityBasedOnSetting();
                };
                panel.appendChild(button);
            });

            const hiResButton = document.createElement('button');
            hiResButton.textContent = 'Hi-Res 音质';
            hiResButton.onclick = () => {
                hiResAudioEnabled = !hiResAudioEnabled;
                GM_setValue('hiResAudio', hiResAudioEnabled);
                updateQualityButtons(panel);
                selectQualityBasedOnSetting();
            };
            panel.appendChild(hiResButton);

            const dolbyAtmosButton = document.createElement('button');
            dolbyAtmosButton.textContent = '杜比全景声';
            dolbyAtmosButton.onclick = () => {
                dolbyAtmosEnabled = !dolbyAtmosEnabled;
                GM_setValue('dolbyAtmos', dolbyAtmosEnabled);
                updateQualityButtons(panel);
                selectQualityBasedOnSetting();
            };
            panel.appendChild(dolbyAtmosButton);

            const takeOverQualityControlButton = document.createElement('button');
            takeOverQualityControlButton.textContent = '移除清晰度按钮(Beta)';
            takeOverQualityControlButton.onclick = () => {
                takeOverQualityControl = !takeOverQualityControl;
                GM_setValue('takeOverQualityControl', takeOverQualityControl);
                updateQualityButtons(panel);
                selectQualityBasedOnSetting();
            };
            panel.appendChild(takeOverQualityControlButton);

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

        function updateQualityButtons(panel) {
            panel.querySelectorAll('button').forEach(button => {
                button.classList.remove('active');
                if (button.textContent === userQualitySetting ||
                    (button.textContent === 'Hi-Res 音质' && hiResAudioEnabled) ||
                    (button.textContent === '杜比全景声' && dolbyAtmosEnabled) ||
                    (button.textContent === '移除清晰度按钮(Beta)' && takeOverQualityControl)) {
                    button.classList.add('active');
                }
            });
        }

        function toggleSettingsPanel() {
            let panel = document.getElementById('bilibili-quality-selector');
            if (!panel) {
                createSettingsPanel();
                panel = document.getElementById('bilibili-quality-selector');
            }
            panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
        }

        document.addEventListener('mousedown', function (event) {
            const panel = document.getElementById('bilibili-quality-selector');
            if (panel && !panel.contains(event.target)) {
                panel.style.display = 'none';
            }
        });

        GM_registerMenuCommand("设置画质和音质", toggleSettingsPanel);

    window.onload = function () {
        let hasElementAppeared = false;
        const observer = new MutationObserver(function (mutations, me) {
            const element = document.querySelector('.v-popover-wrap.header-avatar-wrap');
            if (element) {
                hasElementAppeared = true;
                setTimeout(selectQualityBasedOnSetting, 4000);
                console.log(`脚本开始运行,4秒后切换画质`);
                me.disconnect();
            }
        });

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

        setTimeout(function() {
            observer.disconnect();
            if (!hasElementAppeared) {
                console.error("等待超时,尝试执行中...");
                selectQualityBasedOnSetting();
            }
        }, 12000);
    };


        const parentElement = document.body;

        parentElement.addEventListener('click', function(event) {
            const targetElement = event.target;

            if (targetElement.tagName === 'DIV' || targetElement.tagName === 'P') {
                if (targetElement.hasAttribute('title') || targetElement.classList.contains('title')) {
                    setTimeout(selectQualityBasedOnSetting, 6000);
                    console.log('页面发生切换:', targetElement.textContent.trim());
                }
            }
        });

    })();