// ==UserScript== // @name bili直播助手 // @namespace https://github.com/wakefun // @author 醒悦哥哥 // @description 浏览器去除直播页面遮挡块。绕过b站强制直播姬开播限制,修复OBS无法获取推流码的问题。自动点赞功能。 // @version 3.0.2 // @match https://link.bilibili.com/p/center/index* // @match https://live.bilibili.com/* // @icon https://i1.hdslb.com/bfs/face/ec3c19295a9ce487bdf670174fd7eaa1da690923.jpg@256w_256h.jpg // @run-at document-idle // @grant none // ==/UserScript== (function () { 'use strict'; const MASK_ID = 'web-player-module-area-mask-panel'; const START_LIVE_URL = 'room/v1/Room/startLive'; // --- 去除直播播放器遮挡 --- function observeMask() { const player = document.getElementById('live-player'); if (!player) return; // 先清除已存在的遮挡 document.getElementById(MASK_ID)?.remove(); new MutationObserver((mutations) => { for (const { addedNodes } of mutations) { for (const node of addedNodes) { if (node.nodeType !== Node.ELEMENT_NODE) continue; const mask = node.id === MASK_ID ? node : node.querySelector?.(`#${MASK_ID}`); if (mask) { mask.remove(); console.log('[bili-live] 已去除遮挡'); } } } }).observe(player, { childList: true, subtree: true }); } // DOM 可能尚未完全就绪,延迟重试 if (document.getElementById('live-player')) { observeMask(); } else { const retry = setInterval(() => { if (document.getElementById('live-player')) { clearInterval(retry); observeMask(); } }, 500); // 30s 后放弃 setTimeout(() => clearInterval(retry), 30000); } // --- 自动点赞 --- const AUTO_LIKE_MAX = 1000; const AUTO_LIKE_INTERVAL = 666; const LIKE_WRAPPER_ID = '__auto-like-wrapper__'; let _likeTimer = null; let _likeCount = 0; function simulateLikeKey() { const opts = { key: 'k', code: 'KeyK', bubbles: true }; document.dispatchEvent(new KeyboardEvent('keydown', opts)); document.dispatchEvent(new KeyboardEvent('keyup', opts)); } function stopLike() { clearInterval(_likeTimer); _likeTimer = null; const wrapper = document.getElementById(LIKE_WRAPPER_ID); if (wrapper) { const btn = wrapper.querySelector('button'); const tip = wrapper.querySelector('span'); if (btn) { btn.textContent = '自动点赞'; btn.style.background = '#fff'; btn.style.color = '#00a1d6'; } if (tip) tip.textContent = ''; } console.log(`[bili-live] 自动点赞停止,共点赞 ${_likeCount} 次`); _likeCount = 0; } function startLike() { _likeCount = 0; const wrapper = document.getElementById(LIKE_WRAPPER_ID); if (wrapper) { const btn = wrapper.querySelector('button'); const tip = wrapper.querySelector('span'); if (btn) { btn.textContent = '停止点赞'; btn.style.background = '#00a1d6'; btn.style.color = '#fff'; } if (tip) tip.textContent = '发弹幕时点赞会暂停,点击其他区域可恢复'; } _likeTimer = setInterval(() => { if (_likeCount >= AUTO_LIKE_MAX) return stopLike(); simulateLikeKey(); _likeCount++; }, AUTO_LIKE_INTERVAL); } function injectLikeBtn() { if (document.getElementById(LIKE_WRAPPER_ID)) return; const chatInput = document.querySelector('#chat-control-panel-vm div.chat-input-ctnr'); if (!chatInput) return; const wrapper = document.createElement('div'); wrapper.id = LIKE_WRAPPER_ID; Object.assign(wrapper.style, { display: 'flex', alignItems: 'center', }); const btn = document.createElement('button'); btn.textContent = _likeTimer ? '停止点赞' : '自动点赞'; Object.assign(btn.style, { padding: '2px 10px', cursor: 'pointer', border: '1px solid #00a1d6', borderRadius: '4px', background: _likeTimer ? '#00a1d6' : '#fff', color: _likeTimer ? '#fff' : '#00a1d6', fontSize: '12px', }); const tip = document.createElement('span'); Object.assign(tip.style, { marginLeft: '6px', fontSize: '11px', color: '#999', }); if (_likeTimer) tip.textContent = '发弹幕时点赞会暂停,点击其他区域可恢复'; btn.addEventListener('click', () => _likeTimer ? stopLike() : startLike()); wrapper.append(btn, tip); chatInput.parentNode.insertBefore(wrapper, chatInput); } // 持续监听 DOM 变化,按钮被框架移除后自动重新插入 new MutationObserver(() => injectLikeBtn()) .observe(document.body, { childList: true, subtree: true }); // 初始注入 + 重试 if (!document.querySelector('#chat-control-panel-vm div.chat-input-ctnr')) { const retryLike = setInterval(() => { if (document.querySelector('#chat-control-panel-vm div.chat-input-ctnr')) { clearInterval(retryLike); injectLikeBtn(); } }, 500); setTimeout(() => clearInterval(retryLike), 30000); } else { injectLikeBtn(); } // --- 绕过强制直播姬开播限制(伪装 platform) --- function patchStartLiveBody(body) { const params = new URLSearchParams(body); params.set('platform', 'android_link'); return params.toString(); } // 拦截 XHR const origXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, ...rest) { if (method === 'POST' && url.includes(START_LIVE_URL)) { const origSend = this.send; this.send = function (body) { origSend.call(this, patchStartLiveBody(body)); }; } origXHROpen.call(this, method, url, ...rest); }; // 拦截 fetch const origFetch = window.fetch; window.fetch = function (input, init) { const url = typeof input === 'string' ? input : input?.url; if (url?.includes(START_LIVE_URL) && init?.method?.toUpperCase() === 'POST' && init.body) { init = { ...init, body: patchStartLiveBody(init.body) }; } return origFetch.call(this, input, init); }; })();