// ==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.4
// @description 自动跳过直播、屏蔽账号关键字、跳过广告并自动设置最高分辨率,均可通过界面按钮控制开启/关闭
// @author Frequenk
// @license GPL-3.0 License
// @run-at document-start
// ==/UserScript==
/*
功能说明(每次对功能进行修改后,请更新此说明和description、version):
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);
})();