Greasy Fork is available in English.
移除相关直播、移除礼物栏、移除用户进入直播间消息、移除粉丝牌&礼物弹幕、移除点赞动画、自动屏蔽礼物特效、移除顶栏客户端&快捷访问按钮、自动网页全屏、自动切原画画质。源代码https://github.com/komomoo/dylive-prune,欢迎pr👏
当前为
// ==UserScript==
// @name 抖音直播精简
// @namespace https://github.com/komomoo/dylive-prune
// @version 0.50
// @description 移除相关直播、移除礼物栏、移除用户进入直播间消息、移除粉丝牌&礼物弹幕、移除点赞动画、自动屏蔽礼物特效、移除顶栏客户端&快捷访问按钮、自动网页全屏、自动切原画画质。源代码https://github.com/komomoo/dylive-prune,欢迎pr👏
// @author komo
// @match https://live.douyin.com/*
// @icon https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico
// @grant none
// @run-at document-end
// ==/UserScript==
;(function () {
'use strict'
const css = `
/* 移除礼物栏 */
#BottomLayout{display: none !important;}
.xg-inner-controls{bottom: 8px !important;}
/* 移除相关直播 */
.evLAq21m{display: none !important;}
/* 网页全屏:全屏高度 */
.EDvjMGPs.FKQqfehj{height:100% !important;}
/* 弹幕区用户进入直播间消息 */
.YqmICJp1, .webcast-chatroom___bottom-message{opacity:0 !important;}
.Mv2estbj{display:none !important;}
.Y0sC3Fms{display:none !important;}
/* 粉丝牌&礼物弹幕 */
.ZUGMpBcb{display:none !important;}
/* 点赞动画 */
#flutterLike{display:none !important;}
/* 礼物特效栏 */
.VLPIYL3T{display:none !important;}
.gpFFz2G_{display:none !important;}
/* 小黄车 */
.x5oLQRf4{display:none !important;}
`
const style = document.createElement('style')
style.appendChild(document.createTextNode(css))
document.querySelector('head').append(style)
// 点击关闭礼物按钮
// function giftClickClose() {
// const giftBtnDom = '.xg-inner-controls > xg-right-grid > xg-icon:nth-child(5) > div > div:nth-child(2)'
// const target = document.querySelector('.basicPlayer')
// const observer = new MutationObserver((records) => {
// const giftBtn = document.querySelector(giftBtnDom)
// const giftBtnTxt = document.querySelector(
// '.xg-inner-controls > xg-right-grid > xg-icon:nth-child(5) div.WoNKVQmY.Z20k_Nsy'
// )
// if (giftBtn && giftBtnTxt?.textContent === '屏蔽礼物特效') {
// // console.log('---', giftBtn)
// giftBtn.click()
// } else if (giftBtnTxt?.textContent === '开启礼物特效') {
// observer.disconnect()
// }
// })
// observer.observe(target, { childList: true })
// }
// setTimeout(giftClickClose, 50)
// 模拟点击关闭礼物&弹幕
async function giftClick() {
const giftBtn = document.querySelector(
'div.douyin-player-controls > div > div.douyin-player-controls-right > slot:nth-child(5) > div'
)
const dmBtn = document.querySelector(
'div.douyin-player-controls > div > div.douyin-player-controls-right > slot:nth-child(6) > div > div'
)
if (giftBtn) {
await simulateHover(giftBtn)
document
.querySelector(
'div.douyin-player-controls > div > div.douyin-player-controls-right > slot:nth-child(5) > div > div.BMrAdi9a > div:nth-child(2) > div > div:nth-child(1) > div > div > div'
)
.click()
await simulateHover(dmBtn)
// 1. 定义要操作的项目
const targetNames = ['送礼信息', '福袋口令']
// 2. 遍历所有可能的行容器
const rows = document.querySelectorAll('.tvFMszYY')
rows.forEach((row) => {
const span = row.querySelector('.FTgH60Qj')
if (span && targetNames.includes(span.innerText.trim())) {
// 找到该行内的开关容器
const switchContainer = row.querySelector('.dNuSIvAp')
if (switchContainer) {
// 判断逻辑:
// 开启状态类名包含 'SpsbqNUm' (对应你的HTML)
// 或者简单通过类名数量判断:3个是开,2个是关
const isChecked = switchContainer.classList.contains('SpsbqNUm') || switchContainer.classList.length > 2
if (isChecked) {
console.log(`[${span.innerText}] 当前是打开状态,正在执行关闭...`)
switchContainer.click()
} else {
console.log(`[${span.innerText}] 已经是关闭状态,跳过。`)
}
}
}
})
// 隐藏面板
simulateLeave(giftBtn)
simulateLeave(dmBtn)
} else {
setTimeout(giftClick, 2000)
}
}
setTimeout(giftClick, 2000)
// 网页全屏
function fullClick() {
const btn = document.querySelector(
'div.douyin-player-controls > div > div.douyin-player-controls-right > slot:nth-child(2)'
)
if (btn) {
btn.click()
} else {
setTimeout(fullClick, 1000)
}
}
// fullClick()
// 切原画
function qualityClick() {
const btnWrap = document.querySelector(
'div.douyin-player-controls > div > div.douyin-player-controls-right > slot:nth-child(8) > div > div > div > div.N16rDtOa.vOEJT9B5 > div'
)
if (btnWrap) {
btnWrap.querySelectorAll('.xMYYJi25').forEach((el) => {
if (el.textContent.includes('原画')) el.click()
})
} else {
setTimeout(qualityClick, 1000)
}
}
setTimeout(qualityClick, 5000)
})()
/**
* 模拟鼠标悬停操作并调出隐藏面板
* @param {HTMLElement} el - 目标元素
* @param {number} delay - 悬停后的等待毫秒数(用于后续操作)
*/
async function simulateHover(el, delay = 500) {
// 1. 自动穿透:如果目标是 slot,尝试获取其内部实际渲染的元素
const target = el.tagName === 'SLOT' && el.assignedElements ? el.assignedElements()[0] || el : el
// 2. 获取精确的中心点坐标
const rect = target.getBoundingClientRect()
const centerX = rect.left + rect.width / 2
const centerY = rect.top + rect.height / 2
// 3. 构建事件序列 (模拟真实人类鼠标进入的物理过程)
const eventOptions = {
view: window,
bubbles: true,
cancelable: true,
clientX: centerX,
clientY: centerY,
buttons: 0,
}
const events = [
new MouseEvent('mouseover', eventOptions),
new MouseEvent('mouseenter', eventOptions),
new MouseEvent('mousemove', eventOptions),
]
// 4. 执行触发
events.forEach((ev) => target.dispatchEvent(ev))
// 5. 微小位移补偿(触发某些对静止鼠标不敏感的监听器)
target.dispatchEvent(
new MouseEvent('mousemove', {
...eventOptions,
clientX: centerX + 1,
clientY: centerY + 1,
})
)
console.log('✅ 模拟悬停指令已发送')
// 6. 等待面板渲染并返回目标元素,方便链式调用
await new Promise((resolve) => setTimeout(resolve, delay))
return target
}
/**
* 模拟鼠标移出操作并触发面板隐藏
* @param {HTMLElement} el - 目标元素
* @param {number} delay - 移出后的等待毫秒数
*/
async function simulateLeave(el, delay = 500) {
// 1. 穿透处理
const target = el.tagName === 'SLOT' && el.assignedElements ? el.assignedElements()[0] || el : el
// 2. 获取坐标:为了更真实,我们将坐标设为元素左上角再往外偏移一点
const rect = target.getBoundingClientRect()
const outX = rect.left - 10
const outY = rect.top - 10
// 3. 构建移出事件序列
const eventOptions = {
view: window,
bubbles: true,
cancelable: true,
clientX: outX,
clientY: outY,
buttons: 0,
}
// 重点在于 mouseout 和 mouseleave
const events = [
new MouseEvent('mousemove', eventOptions), // 先移到外面
new MouseEvent('mouseout', eventOptions),
new MouseEvent('mouseleave', eventOptions),
]
// 4. 执行触发
events.forEach((ev) => target.dispatchEvent(ev))
// 5. 抖音这类框架可能还在面板本身挂载了监听,如果是这样,面板也需要触发 leave
const panel = document.querySelector('.UMXOdhRc')
if (panel) {
panel.dispatchEvent(new MouseEvent('mouseleave', eventOptions))
}
console.log('✅ 模拟移出指令已发送')
// 6. 等待隐藏动画完成
await new Promise((resolve) => setTimeout(resolve, delay))
return target
}