Greasy Fork

Greasy Fork is available in English.

抖音优化: 强制最高画质+自动跳过直播

V6.1美化版:右上方胶囊开关。优先检测并自动跳过直播,非直播视频自动强制切换到最高画质 (4K/2K/1080P)。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         抖音优化: 强制最高画质+自动跳过直播
// @namespace    http://tampermonkey.net/
// @version      6.1
// @description  V6.1美化版:右上方胶囊开关。优先检测并自动跳过直播,非直播视频自动强制切换到最高画质 (4K/2K/1080P)。
// @author       You
// @match        https://www.douyin.com/*
// @match        https://live.douyin.com/*
// @grant        none
// @run-at       document-idle
// @license      All Rights Reserved
// ==/UserScript==

(function() {
    'use strict';

    // ==========================================
    //                全局配置参数
    // ==========================================
    const MAIN_INTERVAL = 1000;      // 主循环检测频率 (毫秒)
    const SKIP_COOLDOWN = 1500;      // 跳过直播后的冷却时间
    const QUALITIES = ["超清 4K", "超清 2K", "高清 1080P"]; // 画质优先级

    // ==========================================
    //                全局状态变量
    // ==========================================
    let isSkipping = false;
    // 读取开关状态,默认开启
    let isSkipEnabled = localStorage.getItem('dy_skip_live_enabled') !== 'false';
    let lastVideoSrc = "";
    let isQualityChecked = false;


    // ==========================================
    //            工具函数库
    // ==========================================
    // 1. 模拟按键
    function simulateKeyDown() {
        const event = new KeyboardEvent('keydown', {
            key: 'ArrowDown', code: 'ArrowDown', keyCode: 40, which: 40,
            bubbles: true, cancelable: true
        });
        document.dispatchEvent(event);
        console.log('【抖音助手】执行跳过');
    }

    // 2. 元素可视检测
    function isElementInViewport(el) {
        const rect = el.getBoundingClientRect();
        const windowHeight = (window.innerHeight || document.documentElement.clientHeight);
        const windowWidth = (window.innerWidth || document.documentElement.clientWidth);
        return (rect.top >= 0 && rect.bottom <= windowHeight) && (rect.left < windowWidth * 0.75);
    }

    // 3. 提示 Toast
    function showToast(text) {
        const existing = document.getElementById('dy-helper-toast');
        if(existing) existing.remove();
        const div = document.createElement('div');
        div.id = 'dy-helper-toast';
        div.innerText = text;
        div.style.cssText = `
            position: fixed; top: 20%; left: 50%; transform: translate(-50%, -50%);
            background-color: rgba(0, 0, 0, 0.85); color: #fff; padding: 12px 24px;
            border-radius: 50px; z-index: 100000; font-size: 15px; pointer-events: none;
            box-shadow: 0 5px 15px rgba(0,0,0,0.4); transition: opacity 0.3s; font-weight: bold;
            backdrop-filter: blur(5px);
        `;
        document.body.appendChild(div);
        setTimeout(() => { div.style.opacity = '0'; setTimeout(() => div.remove(), 500); }, 1500);
    }

    // 4. 指针事件
    function triggerPointerEvent(element, eventType) {
        if (!element) return;
        element.dispatchEvent(new PointerEvent(eventType, {
            bubbles: true, cancelable: true, view: window,
            pointerId: 1, width: 1, height: 1, isPrimary: true, pointerType: 'mouse'
        }));
    }

    // 5. 查找画质按钮
    function findResolutionButton() {
        const keywords = ["智能", "标清", "高清", "超清", "4K", "1080P", "720P"];
        const tags = ['span', 'div'];
        for (let tag of tags) {
            const els = document.getElementsByTagName(tag);
            for (let i = els.length - 1; i >= 0; i--) {
                const el = els[i];
                if (!el.innerText) continue;
                const txt = el.innerText.trim();
                if (keywords.some(k => txt.includes(k))) {
                    if (el.clientHeight > 10 && el.clientHeight < 50 && el.clientWidth < 150) {
                        return el;
                    }
                }
            }
        }
        return null;
    }

    // ==========================================
    //            UI 界面模块 (美化版)
    // ==========================================
    function initUI() {
        // 主容器
        const btn = document.createElement('div');
        // 样式:右上方,胶囊形状,鲜艳配色
        const updateStyle = () => {
            const bgColor = isSkipEnabled ? 'linear-gradient(135deg, #00C853, #69F0AE)' : '#333'; // 开启绿色渐变,关闭深灰
            const textColor = isSkipEnabled ? '#004D40' : '#888';
            const icon = isSkipEnabled ? '⚡' : '🚫';
            const shadow = isSkipEnabled ? '0 0 15px rgba(0, 230, 118, 0.6)' : 'none';

            btn.style.background = bgColor;
            btn.style.boxShadow = shadow;
            btn.innerHTML = `
                <span style="font-size: 18px; margin-right: 6px;">${icon}</span>
                <span style="font-weight: 800; color: ${isSkipEnabled?'#fff':'#aaa'}; font-family: system-ui;">跳过直播</span>
                <div style="margin-left: 8px; width: 34px; height: 20px; background: rgba(255,255,255,0.3); border-radius: 10px; position: relative;">
                    <div style="
                        width: 16px; height: 16px; background: #fff; border-radius: 50%;
                        position: absolute; top: 2px; left: ${isSkipEnabled ? '16px' : '2px'};
                        transition: left 0.2s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.2);
                    "></div>
                </div>
            `;
        };

        btn.style.cssText = `
            position: fixed; top: 100px; right: 30px; z-index: 999999;
            display: flex; align-items: center; justify-content: center;
            padding: 8px 16px; border-radius: 50px; cursor: pointer;
            opacity: 0.6; transition: all 0.3s ease; transform: scale(1);
            user-select: none; border: 2px solid rgba(255,255,255,0.1);
            height: 44px; box-sizing: border-box;
        `;

        // 交互效果
        btn.onmouseenter = () => {
            btn.style.opacity = '1';
            btn.style.transform = 'scale(1.05)';
        };
        btn.onmouseleave = () => {
            btn.style.opacity = '0.6';
            btn.style.transform = 'scale(1)';
        };

        // 点击事件
        btn.addEventListener('click', () => {
            isSkipEnabled = !isSkipEnabled;
            localStorage.setItem('dy_skip_live_enabled', isSkipEnabled);
            updateStyle();
            showToast(`自动跳过直播已${isSkipEnabled ? '开启 ⚡' : '关闭 🚫'}`);
        });

        // 初始化
        updateStyle();
        document.body.appendChild(btn);
    }

    // ==========================================
    //            核心模块 A: 直播检测
    // ==========================================
    function checkLiveStream() {
        if (!isSkipEnabled) return false; // 开关关闭则不检测
        if (isSkipping) return true;

        const xpath = "//*[text()='直播中']";
        const result = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

        for (let i = 0; i < result.snapshotLength; i++) {
            let element = result.snapshotItem(i);
            if (isElementInViewport(element)) {
                if (element.offsetParent === null) continue;
                showToast("⚡ 检测到直播,跳过...");
                isSkipping = true;
                simulateKeyDown();
                setTimeout(() => { isSkipping = false; }, SKIP_COOLDOWN);
                return true;
            }
        }
        return false;
    }

    // ==========================================
    //            核心模块 B: 画质调节
    // ==========================================
    function checkVideoQuality() {
        const video = document.querySelector('video');
        if (!video) return;

        if (video.src !== lastVideoSrc) {
            lastVideoSrc = video.src;
            isQualityChecked = false;
        }

        if (isQualityChecked) return;

        const triggerBtn = findResolutionButton();
        if (!triggerBtn) return;

        const currentText = triggerBtn.innerText;
        if (currentText.includes("4K")) {
            isQualityChecked = true;
            return;
        }

        triggerPointerEvent(triggerBtn, 'pointerover');
        triggerPointerEvent(triggerBtn, 'pointerenter');

        setTimeout(() => {
            let foundTarget = false;
            for (let q of QUALITIES) {
                if (currentText.includes(q)) {
                    foundTarget = true;
                    break;
                }
                const allDivs = document.querySelectorAll('div, span, p');
                for (let node of allDivs) {
                    if (node.innerText === q && node !== triggerBtn) {
                        // console.log(`[画质] 切换: ${q}`);
                        triggerPointerEvent(node, 'pointerdown');
                        triggerPointerEvent(node, 'mousedown');
                        triggerPointerEvent(node, 'pointerup');
                        triggerPointerEvent(node, 'mouseup');
                        node.click();
                        foundTarget = true;
                        triggerPointerEvent(triggerBtn, 'pointerout');
                        triggerPointerEvent(triggerBtn, 'pointerleave');
                        const player = document.querySelector('.xgplayer-container') || document.body;
                        triggerPointerEvent(player, 'pointermove');
                        break;
                    }
                }
                if (foundTarget) break;
            }
            if (foundTarget || !currentText.includes("智能")) {
                isQualityChecked = true;
                triggerPointerEvent(triggerBtn, 'pointerleave');
            }
        }, 300);
    }

    // ==========================================
    //                主运行
    // ==========================================
    initUI();
    console.log('【抖音助手】V6.1 启动');
    setInterval(() => {
        if (!checkLiveStream()) {
            checkVideoQuality();
        }
    }, MAIN_INTERVAL);

})();