Greasy Fork

Greasy Fork is available in English.

抖音体验增强

自动跳过直播、屏蔽指定账号并自动设置最高分辨率,均可控制开启/关闭

当前为 2025-06-19 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name 抖音体验增强
// @namespace Violentmonkey Scripts
// @match https://www.douyin.com/?*
// @match *://*.douyin.com/*
// @match *://*.iesdouyin.com/*
// @exclude *://lf-zt.douyin.com*
// @grant none
// @version 1.3
// @description 自动跳过直播、屏蔽指定账号并自动设置最高分辨率,均可控制开启/关闭
// @author -
// @license GPL-3.0 License
// @run-at document-start
// ==/UserScript==

/*
功能说明(每次对功能进行修改后,请更新此说明):
1. 跳过直播功能
    - 自动检测当前视频是否为直播
    - 如果是直播,自动按下向下键切换到下一个视频
    - 可通过界面按钮开启/关闭此功能

2. 自动最高分辨率功能
    - 自动检测视频播放器的分辨率选项
    - 按优先级关键词顺序选择:4K > 2K > 1080P > 720P > 540P > 智能 (例如,"1080P 高清" 会匹配 "1080P")
    - 找到4K分辨率后会自动关闭此功能
    - 可通过界面按钮开启/关闭此功能

3. 屏蔽账号关键字功能
    - 自动检测当前视频的账号名称
    - 如果账号名称包含预设的关键字,自动按下向下键切换到下一个视频
    - 可通过界面按钮开启/关闭此功能
    - 屏蔽关键字数组可在代码顶部 `blockedAccountKeywords` 变量中配置

4. 自动跳过广告功能
    - 自动检测当前视频是否为广告
    - 如果是广告,自动按下向下键切换到下一个视频
    - 可通过界面按钮开启/关闭此功能

5. 界面控制
    - 在视频播放器设置面板中添加四个开关按钮
    - 如果页面存在多个视频播放器,则会在每个播放器的设置面板中插入这些按钮
    - 实时显示功能开启/关闭状态
    - 支持动态切换功能状态

6. 兼容性
    - 支持抖音主站及子域名
    - 自动适配页面变化
    - 使用原生JavaScript,无需额外依赖
*/

(function() {
    'use strict';

    // --- 配置区域 ---
    let skipLiveEnabled = true;
    let autoHighResolutionEnabled = true;
    let blockAccountByKeywordEnabled = true;
    let skipAdEnabled = true;
    
    const priorityOrder = ["4K", "2K", "1080P", "720P", "540P", "智能"];
    const blockedAccountKeywords = ["店", "甄选"];
    
    const SELECTORS = {
        activeVideo: "[data-e2e='feed-active-video']",
        resolutionOptions: ".xgplayer-playing div.virtual > div.item",
        accountName: '[data-e2e="feed-video-nickname"]',
        settingsPanel: 'xg-icon.xgplayer-autoplay-setting',
        adIndicator: 'svg[viewBox="0 0 30 16"]'
    };

    // --- UI 渲染 ---
    function updateToggleButtons(className, isEnabled) {
        document.querySelectorAll(`.${className} .xg-switch`).forEach(sw => {
            sw.classList.toggle('xg-switch-checked', isEnabled);
            sw.setAttribute('aria-checked', String(isEnabled));
        });
    }

    function createToggleButton(text, className, isEnabled, onToggle) {
        const btnContainer = document.createElement('xg-icon');
        btnContainer.className = `xgplayer-autoplay-setting ${className}`;
        btnContainer.innerHTML = `
            <div class="xgplayer-icon">
                <div class="xgplayer-setting-label">
                    <button aria-checked="${isEnabled}" class="xg-switch ${isEnabled ? 'xg-switch-checked' : ''}">
                        <span class="xg-switch-inner"></span>
                    </button>
                    <span class="xgplayer-setting-title">${text}</span>
                </div>
            </div>`;
        btnContainer.querySelector('button').addEventListener('click', (e) => {
            const newState = e.currentTarget.getAttribute('aria-checked') === 'false';
            updateToggleButtons(className, newState);
            onToggle(newState);
        });
        return btnContainer;
    }

    const buttonConfigs = [
        { text: '跳过直播', className: 'skip-live-button', get: () => skipLiveEnabled, set: state => skipLiveEnabled = state },
        { text: '寻找最高分辨率', className: 'auto-high-resolution-button', get: () => autoHighResolutionEnabled, set: state => autoHighResolutionEnabled = state },
        { text: '屏蔽账号关键字', className: 'block-account-keyword-button', get: () => blockAccountByKeywordEnabled, set: state => blockAccountByKeywordEnabled = state },
        { text: '跳过广告', className: 'skip-ad-button', get: () => skipAdEnabled, set: state => skipAdEnabled = state }
    ];

    function insertButtons() {
        document.querySelectorAll(SELECTORS.settingsPanel).forEach(panel => {
            const parent = panel.parentNode;
            if (!parent) return;
            let lastInjectedButton = panel;
            buttonConfigs.forEach(config => {
                let button = parent.querySelector(`.${config.className}`);
                if (!button) {
                    button = createToggleButton(config.text, config.className, config.get(), config.set);
                    parent.insertBefore(button, lastInjectedButton.nextSibling);
                }
                lastInjectedButton = button;
            });
        });
    }

    // --- 核心功能 ---
    function skipVideo() {
        document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 40, bubbles: true }));
    }

    function mainLoop() {
        insertButtons();
        const activeVideo = document.querySelector(SELECTORS.activeVideo);

        if (!activeVideo) {
            if (skipLiveEnabled) skipVideo();
            return;
        }

        if (skipAdEnabled) {
            const adIndicator = activeVideo.querySelector(SELECTORS.adIndicator);
            if (adIndicator) {
                console.log("检测到广告,已跳过");
                skipVideo();
                return;
            }
        }

        if (blockAccountByKeywordEnabled) {
            const accountNameEl = activeVideo.querySelector(SELECTORS.accountName);
            const accountName = accountNameEl?.textContent.trim();
            if (accountName && blockedAccountKeywords.some(keyword => accountName.includes(keyword))) {
                console.log(`检测到屏蔽关键字,已跳过账号: ${accountName}`);
                skipVideo();
                return;
            }
        }

        if (autoHighResolutionEnabled) {
            const options = Array.from(activeVideo.querySelectorAll(SELECTORS.resolutionOptions))
                .map(el => {
                    const text = el.textContent.trim().toUpperCase();
                    return { element: el, text, priority: priorityOrder.findIndex(p => text.includes(p)) };
                })
                .filter(opt => opt.priority !== -1)
                .sort((a, b) => a.priority - b.priority);

            if (options.length > 0 && !options[0].element.classList.contains("selected")) {
                const bestOption = options[0];
                bestOption.element.click();
                console.log(`已切换至最高分辨率: ${bestOption.element.textContent}`);
                
                if (bestOption.text.includes("4K")) {
                    autoHighResolutionEnabled = false;
                    updateToggleButtons('auto-high-resolution-button', false);
                    console.log("已找到4K分辨率,自动关闭寻找最高分辨率功能");
                }
            }
        }
    }

    setInterval(mainLoop, 300);
})();