Greasy Fork

Greasy Fork is available in English.

抖音直播精简

移除相关直播、移除礼物栏、移除用户进入直播间消息、移除粉丝牌&礼物弹幕、移除点赞动画、自动屏蔽礼物特效、移除顶栏客户端&快捷访问按钮、自动网页全屏、自动切原画画质。源代码https://github.com/komomoo/dylive-prune,欢迎pr👏

当前为 2026-01-22 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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
}