// ==UserScript== // @name 海角社区m3u8提取+去广告-原位替换终极播放版(binghe修改版) // @namespace haijiao-analyst // @version 4.0 // @author binghe修改版 // @description (手机版) 1.自动屏蔽广告 2.捕获m3u8 3.内置HLS引擎+强力播放器节点注入兜底 // @license MIT // @match *://*.haijiao.com/* // @match *://*/post/details* // @grant GM_addStyle // @grant unsafeWindow // @run-at document-start // ==/UserScript== (function () { 'use strict'; let capturedM3u8Url = ''; let hasReplaced = false; const btnId = 'hj-play-btn-mobile'; // ========================================== // 模块一:强力去广告 (静态数据清洗) // ========================================== const adStyles = ` [class*="guanggao"], [class*="ads"], [id*="ads"], [class*="banner"], [id*="banner"], .float-box, .float-window, .couplet, .top-ad, .bottom-ad, .sidebar, .ad-container, .gg-box, iframe:not([src*="m3u8"]):not([src*="player"]):not([id*="play"]), .ad-box img, a[href*="bocai"] { display: none !important; visibility: hidden !important; height: 0 !important; width: 0 !important; opacity: 0 !important; pointer-events: none !important; } body { overflow-x: hidden; } `; GM_addStyle(adStyles); function cleanUpAds() { document.querySelectorAll('div[style*="z-index: 999"]').forEach(div => div.remove()); document.querySelectorAll('div[style*="position: fixed"]').forEach(div => { if (div.id !== btnId && div.id !== 'hj-toast-mobile') { if (div.querySelector('iframe') || div.querySelector('img')) { div.style.display = 'none'; } } }); } setInterval(cleanUpAds, 2000); // ========================================== // 模块二:数据下行拦截 (拦截真实 M3U8 地址) // ========================================== GM_addStyle(` #${btnId} { position: fixed; bottom: 80px; right: 15px; width: 55px; height: 55px; border-radius: 50%; background-color: rgba(30, 60, 114, 0.95); color: white; border: 2px solid rgba(255,255,255,0.3); z-index: 999999; font-size: 24px; text-align: center; line-height: 51px; user-select: none; -webkit-tap-highlight-color: transparent; display: none; box-shadow: 0 4px 10px rgba(0,0,0,0.4); transition: transform 0.2s, background-color 0.3s; } #${btnId}.show { display: block; } #${btnId}.active { transform: scale(0.9); } #${btnId}.played { background-color: rgba(46, 139, 87, 0.95); } #hj-toast-mobile { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.8); color: #fff; padding: 12px 24px; border-radius: 8px; z-index: 1000000; font-size: 15px; pointer-events: none; opacity: 0; transition: opacity 0.3s; } `); const getRealVideoSrc = (content, requestUrl) => { if (!content) return ""; try { if (content.includes("#EXTM3U")) { const baseUrl = requestUrl.substring(0, requestUrl.lastIndexOf('/') + 1); const filenameMatch = content.match(/([\w_]+_?)[\d]+\.ts/); if (filenameMatch) return baseUrl + filenameMatch[1] + ".m3u8"; } else { const lines = content.split("\n"); const tsLine = lines.find(line => line.includes('.ts')); if (tsLine) { let reg = tsLine.match(/([\w_]+_?)[\d]+\.ts/); if (reg) return tsLine.replace(reg[0], reg[1] + ".m3u8").trim(); } } } catch (e) { } return ""; }; const bareDecode = (text) => { try { return JSON.parse(atob(atob(atob(text)))); } catch (e) { return null; } }; const decodeEncryptString = (text) => { let data = text; try { if (typeof text === 'string') { let tmpDataObj = null; try { tmpDataObj = JSON.parse(text); } catch (e) { } if (tmpDataObj) { if (tmpDataObj.data && typeof tmpDataObj.data === 'object') { data = tmpDataObj.data; } else if (tmpDataObj.data && typeof tmpDataObj.data === 'string') { data = bareDecode(tmpDataObj.data); } } } } catch (error) { } return data; }; const originalXHR = unsafeWindow.XMLHttpRequest; unsafeWindow.XMLHttpRequest = function () { const xhr = new originalXHR(); const originalOpen = xhr.open; const originalSend = xhr.send; let requestUrl = ''; xhr.open = function (method, url) { requestUrl = url || ''; return originalOpen.apply(this, arguments); }; xhr.send = function () { if (requestUrl.includes("/api/address/") || requestUrl.includes("/api/topic/")) { xhr.addEventListener('load', function () { setTimeout(() => handleXhrLoad(xhr, requestUrl), 0); }); } return originalSend.apply(this, arguments); }; return xhr; }; async function handleXhrLoad(xhr, url) { try { if (url.includes("/api/address/")) { const videoSrc = getRealVideoSrc(xhr.responseText, url); if (videoSrc) updateState(videoSrc); } else if (/\/api\/topic\/\d+/.test(url)) { const data = decodeEncryptString(xhr.responseText); if (data?.attachments) { for (const element of data.attachments) { if (element.category === "video" && element.remoteUrl) { fetch(element.remoteUrl) .then(res => res.text()) .then(content => { const videoSrc = getRealVideoSrc(content, element.remoteUrl); if (videoSrc) updateState(videoSrc); }).catch(() => { }); } } } } } catch (e) { } } function updateState(src) { if (!src || src === capturedM3u8Url) return; capturedM3u8Url = src; showToast('已捕获完整 M3U8 索引,正在自动替换播放器...'); // 自动替换播放器,无需手动点击 if (!hasReplaced) { replaceAndPlayVideo(); } } function showToast(msg) { let toast = document.getElementById('hj-toast-mobile'); if (!toast) { toast = document.createElement('div'); toast.id = 'hj-toast-mobile'; document.body.appendChild(toast); } toast.textContent = msg; toast.style.opacity = '1'; setTimeout(() => { toast.style.opacity = '0'; }, 3000); } // ========================================== // 模块三:高级解析节点注入与 M3U8 流媒体接管 // ========================================== function replaceAndPlayVideo() { if (hasReplaced) return; hasReplaced = true; showToast('开始启动数据管线与播放器构筑...'); // 【1】暴力清洗阶段:寻址页面现有的全部正在播放的原生媒体,将其静音并断肠式销毁 document.querySelectorAll('video').forEach(v => { v.pause(); v.removeAttribute('src'); v.load(); const potentialParent = v.closest('.video-div') || v.closest('.dplayer') || v.parentElement; if (potentialParent) potentialParent.innerHTML = ''; // 清空它原来的播放外壳 }); // 隐藏那些“付费提示”或“只有30秒预览”等脏数据文案 document.querySelectorAll('.sell_line1, .sell_line2, .preview-title').forEach(el => { el.style.display = 'none'; }); // 【2】模糊寻址阶段:采用多维度的探针去锁定可能作为注入地点的容器 // 选择器兼容:原始特征、ID特征、购买容器特征 let videoContainers = document.querySelectorAll('.video-div, .dplayer, [id^="video_"], .sell-btn, .post-details'); let targetContainer = null; if (videoContainers.length > 0) { targetContainer = videoContainers[0]; // 抓住锁定的第一个点位 console.log("【数据洞察】常规寻址成功"); } else { // 【3】主动兜底机制 (Proactive Fallback):节点异形变幻到无法寻址? // 我们直接凭空生造一个 div 插入在最前面! console.log("【数据洞察】常规寻址脱靶,触发降级注入策略!"); showToast('启用兜底注入模式,强制渲染播放器'); targetContainer = document.createElement('div'); targetContainer.style.margin = '15px 0'; let anchorNode = document.querySelector('body'); // 尝试挂载到内容顶部附近 let contentBody = document.querySelector('.post-content') || document.querySelector('.article-content') || document.body; contentBody.insertBefore(targetContainer, contentBody.firstChild); } // 初始化容器 (如果是老的Dplayer容器,就把里面的脏DOM连根拔起) targetContainer.innerHTML = ''; const newPlayerId = 'clean-core-player-' + Date.now(); // 【提取页面标题】 let pageTitle = '未知标题'; const titleSpan = document.querySelector('.header h2 > span'); if (titleSpan) { pageTitle = titleSpan.textContent.trim(); } else if (document.title) { pageTitle = document.title.trim(); } console.log('[Clean Player] 提取到页面标题:', pageTitle); // 挂载全新的展示区(一体化无缝卡片设计) targetContainer.innerHTML = `
`; const newVideoTag = targetContainer.querySelector(`#${newPlayerId}`); const targetM3u8 = capturedM3u8Url; // 【4】M3U8 智能解析路由 if (newVideoTag.canPlayType('application/vnd.apple.mpegurl')) { // 场景 1: 原生硬解 (iOS) newVideoTag.src = targetM3u8; showToast('iOS 原生硬解就绪,点击播放'); } else { // 场景 2: 软解加载 (Android / PC) const GlobalHls = unsafeWindow.Hls || window.Hls; if (GlobalHls && GlobalHls.isSupported()) { let hls = new GlobalHls(); hls.loadSource(targetM3u8); hls.attachMedia(newVideoTag); hls.on(GlobalHls.Events.MANIFEST_PARSED, function () { console.log('HLS 解析完毕,播放器就绪'); }); showToast('HLS 引擎解码就绪,点击播放'); } else { showToast('主动下载边缘节点 M3U8 解析器...'); let script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest'; script.onload = () => { const InjectedHls = unsafeWindow.Hls || window.Hls; if (InjectedHls && InjectedHls.isSupported()) { let hls = new InjectedHls(); hls.loadSource(targetM3u8); hls.attachMedia(newVideoTag); hls.on(InjectedHls.Events.MANIFEST_PARSED, () => { console.log('外置 HLS 引擎解析完毕,播放器就绪'); showToast('外置引擎挂载完毕,点击播放'); }); } else { showToast('您的设备底层解码器版本不兼容。'); } }; document.head.appendChild(script); } } const btn = document.getElementById(btnId); if (btn) { btn.classList.add('played'); btn.textContent = '✔️'; } } function init() { if (document.getElementById(btnId)) return; const btn = document.createElement('div'); btn.id = btnId; btn.textContent = '▶️'; btn.onclick = function () { btn.classList.add('active'); setTimeout(() => btn.classList.remove('active'), 100); if (capturedM3u8Url) { // 备用手动触发(若自动替换未执行) hasReplaced = false; replaceAndPlayVideo(); } else { showToast('后台尚未捕获到完整数据的索引参数,请等待刷新或播放原视频探寻。'); } }; document.body.appendChild(btn); } if (document.readyState === 'loading') { window.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('popstate', () => { capturedM3u8Url = ''; hasReplaced = false; const btn = document.getElementById(btnId); if (btn) { btn.classList.remove('show'); btn.classList.remove('played'); btn.textContent = '▶️'; } setTimeout(cleanUpAds, 500); }); })();