好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 untitled15X 于 2026-10-4 13:32 编辑
在网页版和pc客户端刷抖音刷一段时间就很卡,所以指挥AI开发了这个脚本。
在数据层拦截抖音网页版信息流中的直播条目,使直播卡片和播放器不会加载和渲染。可在油叉或脚本猫搜索“抖音屏蔽直播”快捷安装
[color=lab(34.924 0 0)]
功能特性- 数据层拦截:Hook 抖音网页版的 fetch / XMLHttpRequest 数据接口(推荐、精选、搜索、关注、用户主页等),在直播数据到达页面之前就把直播条目过滤掉
- 直播完全不加载:直播卡片、直播播放器、直播流地址(flv 拉流)根本不会进入页面
- 覆盖全场景:推荐信息流、精选页、关注页信息流、关注页 "正在直播" 列表、搜索页直播卡片
- 不误伤普通视频:只删除被确认为直播的条目,普通视频、图集、用户信息全部保留
- 直播间自动跳回首页:误入 live.douyin.com 或 /live/ 直播间地址时自动跳转
- DOM 兜底:万一有漏网的直播标签("直播中" 角标)和直播入口,也会被隐藏
- 兼容性好:同时 Hook fetch 与 XMLHttpRequest,适配抖音各种请求方式
使用声明- 无需任何配置,装完即用
- 仅对抖音网页版生效(www.douyin.com / douyin.com 全站)
- 卸载:在管理面板删除本脚本即可
免责声明- 本脚本仅用于个人学习与使用
- 请遵守平台用户协议与相关法律法规
- 脚本发布时功能在本机测试可用,不保证在其他设备或页面改版后依然有效
[color=lab(34.924 0 0)][backcolor=lab(95.4818 0.411302 -6.78529)]
[JavaScript] 纯文本查看 复制代码 // ==UserScript==
// @name 抖音屏蔽直播
// @namespace DouyinUnlive
// @version 4.0.2
// @description 在数据层拦截抖音网页版信息流中的直播条目(推荐/精选/搜索/关注等接口),直播数据不进入页面,直播卡片和播放器完全不会创建和加载。
// @AuThor Doubao
// @license MIT
// @match *://*.douyin.com/*
// @match *://douyin.com/*
// @run-at document-start
// @grant none
// @noframes
// ==/UserScript==
(function () {
'use strict';
/* 自检标记:脚本成功运行后,<html> 标签会带有 data-dy-unlive="4.0.2",
* 同时 window.__douyinUnlive 存在。若页面上没有这些标记,说明脚本未运行
* (油猴未启用 / 脚本被禁用 / 装错浏览器),请先检查油猴管理面板。 */
try {
document.documentElement.setAttribute('data-dy-unlive', '4.0.2');
window.__douyinUnlive = '4.0.2';
} catch (e) { }
/* =========================================================
* 一、数据层拦截(主力):hook fetch / XHR,过滤直播条目
* 直播数据根本到不了页面 -> 直播卡片/播放器不会渲染和加载
* ========================================================= */
var HIDDEN_ATTR = 'data-dy-unlive-hidden';
var HOOKED = false;
// 判断一个响应 URL 是否值得处理(抖音网页版数据接口)
// 覆盖 aweme 系列(信息流/关注/搜索/直播卡片)、webcast(关注页正在直播列表)、
// 以及所有 feed 接口;宽匹配防漏
function shouldHandle(url) {
if (!url) return false;
return /aweme\/|webcast\/|\/feed\/|\/search\//i.test(url);
}
// 判断一个条目是不是直播(字段经真实抓包确认)
// 注意:不要求 isEntry 前置,cell_room 是直播卡片专用字段,含它即直播
function isLiveEntry(o) {
if (!o || typeof o !== 'object' || Array.isArray(o)) return false;
if ('cell_room' in o) return true; // 信息流直播卡片:内含直播房间数据+拉流地址(实测)
if (o.aweme_type === 101) return true; // 信息流直播条目类型(实测)
if ('rid' in o && 'live_status' in o) return true; // 直播房间条目 webcast 接口(实测:live_reason 常为空串)
if (o.live_reason) return true; // 直播原因字段(数字/字符串非空)
if (o.is_live === true || o.is_live === 1 || o.is_live === '1') return true;
if (o.live_info && typeof o.live_info === 'object' && Object.keys(o.live_info).length) return true;
if (o.live_card && typeof o.live_card === 'object' && Object.keys(o.live_card).length) return true;
if (o.room && typeof o.room === 'object' && Object.keys(o.room).length) return true;
return false;
}
// 递归净化 JSON:仅删除直播条目,普通视频/用户/配置结构一律保留
function sanitize(root) {
if (Array.isArray(root)) {
var kept = [];
for (var i = 0; i < root.length; i++) {
var it = root[i];
if (it && typeof it === 'object' && !Array.isArray(it) && isLiveEntry(it)) {
continue; // 直播条目:删除
}
kept.push(sanitize(it));
}
return kept;
}
if (root && typeof root === 'object') {
var keys = Object.keys(root);
for (var k = 0; k < keys.length; k++) {
root[keys[k]] = sanitize(root[keys[k]]);
}
}
return root;
}
// 对响应文本做净化;不是 JSON 就原样返回
function transformResponse(text) {
var j;
try { j = JSON.parse(text); } catch (e) { return text; }
try {
var cleaned = sanitize(j);
var out = JSON.stringify(cleaned);
return out.length <= text.length ? out : text; // 防异常膨胀
} catch (e) { return text; }
}
// ---- hook fetch ----
function hookFetch() {
var origFetch = window.fetch;
if (!origFetch) return;
window.fetch = function () {
var args = arguments;
var url = (typeof args[0] === 'string') ? args[0] : (args[0] && args[0].url) || '';
var p = origFetch.apply(this, args);
if (!shouldHandle(url)) return p;
return p.then(function (res) {
try {
var ct = (res.headers && res.headers.get && res.headers.get('content-type')) || '';
if (ct && ct.indexOf('json') === -1) return res;
return res.clone().text().then(function (text) {
var out = transformResponse(text);
if (out === text) return res;
// 只保留 content-type,去掉 content-length / content-encoding 等
// 长度相关头,防止新 body 与旧头不一致导致前端解析失败
var headers = {};
if (ct) headers['content-type'] = ct;
return new Response(out, {
status: res.status,
statusText: res.statusText,
headers: headers
});
});
} catch (e) { return res; }
});
};
}
// ---- hook XMLHttpRequest(兼容老请求方式)----
function hookXHR() {
var origOpen = XMLHttpRequest.prototype.open;
var origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url) {
this.__dyUrl = url;
var xhr = this;
// 在 open 阶段尽早注册,保证先于页面自身监听器改写 responseText
this.addEventListener('readystatechange', function () {
if (xhr.readyState !== 4) return;
try {
var u = xhr.__dyUrl || '';
if (!shouldHandle(u)) return;
var rt = xhr.responseType || '';
if (rt && rt !== '' && rt !== 'text') return; // 非文本响应不处理
var text = xhr.responseText;
if (!text) return;
var out = transformResponse(text);
if (out === text) return;
Object.defineProperty(xhr, 'responseText', { get: function () { return out; }, configurable: true });
Object.defineProperty(xhr, 'response', { get: function () { return out; }, configurable: true });
} catch (e) { }
});
return origOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function () {
return origSend.apply(this, arguments);
};
}
function installHooks() {
if (HOOKED) return;
HOOKED = true;
try { hookFetch(); } catch (e) { }
try { hookXHR(); } catch (e) { }
}
/* =========================================================
* 二、直播间页面自动跳回首页
* ========================================================= */
function inLiveRoom() {
return location.hostname === 'live.douyin.com' ||
location.pathname.indexOf('/live/') !== -1;
}
if (inLiveRoom()) {
location.replace('https://www.douyin.com/');
return; // 直播页直接跳走,不装任何东西
}
/* =========================================================
* 三、DOM 兜底层:数据层漏网时,隐藏直播卡片与直播入口
* (渲染后隐藏,仅作保底;正常情况下数据层已拦截,不会走到这里)
* 注意:LivePlayer_PreCreatePlayer / douyin-player-loading 是
* 普通视频播放器共用的组件,绝不能隐藏,否则会破坏视频播放。
* ========================================================= */
// CSS:隐藏直播标签、直播入口;不动任何视频播放组件
var style = document.createElement('style');
style.textContent = [
'[data-e2e="user-info-living"] { display: none !important; }',
'[data-e2e*="living" i] { display: none !important; }',
'a[href*="live.douyin.com"] { display: none !important; }',
'[aria-label*="直播" i] { display: none !important; }'
].join('\n');
(document.head || document.documentElement).appendChild(style);
function hide(el) {
if (!el || el.nodeType !== 1) return;
if (el.hasAttribute(HIDDEN_ATTR)) return;
el.setAttribute(HIDDEN_ATTR, '1');
el.style.setProperty('display', 'none', 'important');
}
// 找到直播卡片根并隐藏。
// 实测:直播卡片根是 .discover-video-card-item 这类卡片容器(无 data-e2e),
// 从直播标签(data-e2e="user-info-living")向上找 card-item / video-card / feed-item 特征容器。
function hideLiveCardFrom(node) {
var card = node;
for (var i = 0; card && i < 10; i++) {
var cls = (card.className || '').toString();
if (/card-item|video-card|feed-item/i.test(cls)) break;
card = card.parentElement;
}
if (!card || card === document.body) {
card = node.closest ? (node.closest('[data-e2e]') || node) : node;
}
hide(card);
}
function scan(root) {
if (!root || root.nodeType !== 1) return;
var nodes;
try {
nodes = root.querySelectorAll('[data-e2e="user-info-living"], [data-e2e*="living" i]');
} catch (e) { return; }
for (var i = 0; i < nodes.length; i++) hideLiveCardFrom(nodes[i]);
}
// MutationObserver:新内容进场即查
var observer = null;
var pending = false;
function attachObserver() {
if (observer || !document.body) return;
observer = new MutationObserver(function () {
if (pending) return;
pending = true;
setTimeout(function () {
pending = false;
scan(document.body);
}, 150);
});
observer.observe(document.body, { childList: true, subtree: true });
}
// 定时兜底:防 SPA 跳直播路由 + 定期复查
setInterval(function () {
if (inLiveRoom()) {
location.replace('https://www.douyin.com/');
return;
}
scan(document.body);
}, 3000);
/* =========================================================
* 四、启动
* ========================================================= */
installHooks(); // document-start 尽早 hook,抢在页面请求前
scan(document.documentElement);
if (document.body) {
attachObserver();
} else {
document.addEventListener('DOMContentLoaded', attachObserver);
}
console.info('[抖音屏蔽直播] 数据层拦截已启用');
})();
|
免费评分
-
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|
|