// ==UserScript== // @name B站优化小助手 // @namespace http://tampermonkey.net/ // @version 1.9.9 // @description 优化Bilibili主页、文章和直播页面,增加禁用追踪、日志和 P2P CDN 功能 // @match https://www.bilibili.com/ // @match *://www.bilibili.com/read/* // @match *://live.bilibili.com/* // @match *://www.bilibili.com/video/* // @match *://*.bilibili.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM.notification // @grant unsafeWindow // @run-at document-start // @downloadURL https://update.greasyfork.icu/scripts/503306/B%E7%AB%99%E4%BC%98%E5%8C%96%E5%B0%8F%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/503306/B%E7%AB%99%E4%BC%98%E5%8C%96%E5%B0%8F%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; // --- 辅助函数 Start --- const o$1 = ()=>{}; // 空函数 const r = ()=>true; // 返回 true 的函数 const e$2 = ()=>false; // 返回 false 的函数 // 定义只读属性 function defineReadonlyProperty(target, key, value, enumerable = true) { Object.defineProperty(target, key, { get () { return value; }, set: o$1, // 设置为空函数,防止修改 configurable: false, enumerable }); } // 简化的 URL 匹配函数 function createUrlMatcher(patterns) { return function(url) { if (!url) return false; for (const pattern of patterns) { if (typeof pattern === 'string') { if (url.includes(pattern)) { return true; } } else if (pattern instanceof RegExp) { // 允许 RegExp if (pattern.test(url)) { return true; } } } return false; }; } // 从请求信息中获取 URL function getUrlFromRequest(request) { if (typeof request === 'string') { return request; } if (request && 'href' in request) { // URL object return request.href; } if (request && 'url' in request) { // Request object return request.url; } console.warn('Tampermonkey Script "优化Bilibili": 无法从请求中获取 URL', request); return null; } // 创建 Mock 类 (简化版) function createMockClass(className) { const fakeClassInstance = new Proxy(o$1, { get (target, prop) { return (...args)=>{ // console.log(`Mock Call: (new ${className})[${String(prop)}] called with arguments:`, args); }; } }); return new Proxy(class {}, { construct () { return fakeClassInstance; }, get (target, prop) { return (...args)=>{ // console.log(`Mock Call: window.${className}[${String(prop)}] called with arguments:`, args); }; } }); } // 新增:从 URL 字符串或 URL 对象获取查询参数 function getUrlParams(url) { const params = {}; let search; if (typeof url === 'string') { try { search = new URL(url, url.startsWith('/') ? window.location.origin : undefined).search; } catch (e) { search = url.includes('?') ? url.substring(url.indexOf('?')) : ''; } } else if (url instanceof URL) { search = url.search; } else { return params; } const urlSearchParams = new URLSearchParams(search); for (const [key, value] of urlSearchParams) { params[key] = value; } return params; } // 新增: 强力替换mcdn URL的函数 function forceMcdnReplace(url) { if (!url || typeof url !== 'string') return url; // 如果不包含mcdn,直接返回 if (!url.includes('.mcdn.bilivideo')) return url; try { // 优先使用缓存中的CDN,或默认值 const cdnHost = intelligentCdnCache.length > 0 ? intelligentCdnCache[0] : 'upos-sz-mirrorcos.bilivideo.com'; // 更激进的替换模式,无需复杂匹配 const newUrl = url.replace(/(https?:\/\/|\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(:[0-9]+)?(\/.*)?/i, (match, protocol, subdomain, domain, port, path) => { const secureProtocol = (protocol === '//') ? 'https://' : 'https://'; return `${secureProtocol}${cdnHost}${path || ''}`; }); if (newUrl !== url) { console.log(`[强力替换]: ${url} -> ${newUrl}`); return newUrl; } } catch (e) { console.error('[强力替换]: 处理URL出错', e, url); } return url; } // --- 辅助函数 End --- // --- LRU Cache (flru) Start --- // REMOVED - No longer needed // --- LRU Cache (flru) End --- // --- ErrorCounter Start --- // REMOVED - No longer needed // --- ErrorCounter End --- // --- 功能常量 Start --- // ** 追踪相关 ** const TRACKING_URLS = [ 'data.bilibili.com', 'cm.bilibili.com', 'api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi', // 添加其他可能的追踪 URL '/log/web', '//config.bilibili.com/relation/log', '//config.bilibili.com/player/log', 'api.bilibili.com/x/web-interface/popular/precious', // 首页宝箱 'api.bilibili.com/x/click-interface/click/web/h5', 'api.bilibili.com/x/web-interface/web/report', 'api.bilibili.com/x/click-interface/heartbeat', 'message.bilibili.com/api/log/upload.json', // 私信日志 ]; const shouldBlockUrl = createUrlMatcher(TRACKING_URLS); const TRACKING_LOCALSTORAGE_KEYS = [ /^web_player_config/, /^__LOG.*__$/, /^bp_video_ep_enabled_/, /^bili_report/, /^live_bf_track_/, 'player_last_filter_play_time', 'bilibili_player_report_log', 'PCDN-client-peerid', // 添加其他可能的追踪 LocalStorage Key ]; const shouldRemoveLsKey = (key) => { if (!key) return false; for (const pattern of TRACKING_LOCALSTORAGE_KEYS) { if (typeof pattern === 'string') { if (key === pattern) return true; } else if (pattern instanceof RegExp) { if (pattern.test(key)) return true; } } return false; }; const TRACKING_IDB_NAMES = [ 'PLAYER__LOG', 'MIRROR_TRACK_V2', 'pbp3', 'BILI_MIRROR_REPORT_POOL', 'BILI_MIRROR_RESOURCE_TIME', 'bp_nc_loader_config', 'reporter-pb', 'pcdn', 'nc_loader', 'iconify', // 添加其他可能的追踪 IndexedDB Name ]; const USELESS_URL_PARAMS = [ 'buvid', 'is_story_h5', 'launch_id', 'live_from', 'mid', 'session_id', 'timestamp', 'up_id', 'vd_source', /^share/, /^spm/, /^from/, /^seid/, /^plat_id/, // 添加其他可能的追踪 URL 参数 ]; // ** 直播增强相关 ** const liveCdnUrlKwFilter = createUrlMatcher(['.bilivideo.', '.m3u8', '.m4s', '.flv']); // Keep for potential future use or identifying live streams const LIVE_ENHANCEMENT_STYLE_ID = 'my-js-live-enhancement-styles'; // ** P2P CDN 相关 (及新 CDN 优化常量) ** // const rBackupCdn = /(?:up|cn-)[\w-]+\.bilivideo\.com/g; // 旧的提取备用 CDN 的正则,不再主要依赖 const isP2PCDNOriginalMatcher = createUrlMatcher([ // 保留原始 P2P 判断,用于 P2P 功能本身 '.mcdn.bilivideo.cn', '.szbdyd.com' ]); const playUrlApi = 'api.bilibili.com/x/player/wbi/playurl'; // 获取播放地址的 API // 新增:CDN 优化相关常量 (移植自 1.txt) const VOD_ORIGIN_ALI = "ali"; const VOD_ORIGIN_COS = "cos"; const VOD_ORIGIN_HW = "hw"; const VOD_ORIGIN_08 = "08"; const VOD_ORIGIN_BD = "bd"; const VOD_MIRROR_ALI = "upos-sz-mirrorali.bilivideo.com"; const VOD_MIRROR_COS = "upos-sz-mirrorcos.bilivideo.com"; const VOD_MIRROR_HW = "upos-sz-mirrorhw.bilivideo.com"; const VOD_MIRROR_08 = "upos-sz-mirror08c.bilivideo.com"; const VOD_MIRROR_BD = "upos-sz-mirrorbd.bilivideo.com"; const VOD_MIRROR_AKAM_OVERSEAS = "upos-hz-mirrorakam.akamaized.net"; // 海外备选 const mirrorRegex = /https?:\/\/((upos-\S+-mirror\S+)|((upos|proxy)-tf-.*))\.(bilivideo|akamaized)\.(com|net)/; const mCdnTfRegex = /((([0-9]{1,3}\.){3}[0-9]{1,3})|(.*\.mcdn\.bilivideo\.(com|cn|net))):[0-9]{1,5}\/v1\/resource/; // 假设在中国大陆,1.txt 中 isLocatedInCn 的逻辑可以后续按需实现 const IS_LOCATED_IN_CN = true; let unknownOgsCache = new Set(); // --- 功能常量 End --- // 从GM_getValue获取保存的状态 let isAdRemovalEnabled = GM_getValue('bilibiliAdRemovalEnabled', false); let isSwiperRemovalEnabled = GM_getValue('bilibiliSwiperRemovalEnabled', false); let isNonVideoCardRemovalEnabled = GM_getValue('bilibiliNonVideoCardRemovalEnabled', false); let isCopySuffixRemovalEnabled = GM_getValue('bilibiliCopySuffixRemovalEnabled', false); let isTrackingLoggingDisabled = GM_getValue('bilibiliTrackingLoggingDisabled', false); let isLiveEnhancementEnabled = GM_getValue('bilibiliLiveEnhancementEnabled', false); let isP2pCdnDisabled = GM_getValue('bilibiliP2pCdnDisabled', false); let isHomePageOptimizationEnabled = GM_getValue('bilibiliHomePageOptimizationEnabled', false); let isAv1Disabled = GM_getValue('bilibiliAv1Disabled', false); let isForce4kEnabled = GM_getValue('bilibiliForce4kEnabled', false); // 保存原始函数引用 const originalFetch = unsafeWindow.fetch; const originalXhrOpen = unsafeWindow.XMLHttpRequest.prototype.open; const originalSendBeacon = unsafeWindow.navigator.sendBeacon; const originalIndexedDBOpen = unsafeWindow.indexedDB?.open; const originalLocalStorage = unsafeWindow.localStorage; const originalLsSetItem = unsafeWindow.localStorage?.setItem; const originalLsGetItem = unsafeWindow.localStorage?.getItem; const originalLsRemoveItem = unsafeWindow.localStorage?.removeItem; const originalLsClear = unsafeWindow.localStorage?.clear; const originalLsKey = unsafeWindow.localStorage?.key; const originalHistoryPushState = unsafeWindow.history.pushState; const originalHistoryReplaceState = unsafeWindow.history.replaceState; // 新增:保存 AV1 相关原始方法 const originalHTMLMediaElement = unsafeWindow.HTMLMediaElement; const originalMediaSource = unsafeWindow.MediaSource; const originalCanPlayType = originalHTMLMediaElement?.prototype?.canPlayType; const originalIsTypeSupported = originalMediaSource?.isTypeSupported; // 新增:保存 sessionStorage 相关原始方法 const originalSessionStorage = unsafeWindow.sessionStorage; const originalSessionStorageGetItem = originalSessionStorage?.getItem; // --- P2P CDN 状态变量 (调整) --- let prevLocationHrefForCdnCache = ''; // 用于CDN缓存的href比较 let intelligentCdnCache = []; // 新的智能 CDN 缓存池,存储优质 CDN (如 Mirror) 的 host const DEFAULT_MIRROR_CDN = VOD_MIRROR_COS; // 默认使用的 Mirror CDN // 保存 P2P/WebRTC 相关原始引用 const originalHtmlMediaSrcDescriptor = Object.getOwnPropertyDescriptor(unsafeWindow.HTMLMediaElement.prototype, 'src'); const originalRTCPeerConnection = unsafeWindow.RTCPeerConnection; const originalWebkitRTCPeerConnection = unsafeWindow.webkitRTCPeerConnection; const originalMozRTCPeerConnection = unsafeWindow.mozRTCPeerConnection; const originalRTCDataChannel = unsafeWindow.RTCDataChannel; const originalWebkitRTCDataChannel = unsafeWindow.webkitRTCDataChannel; const originalMozRTCDataChannel = unsafeWindow.mozRTCDataChannel; const originalRTCSessionDescription = unsafeWindow.RTCSessionDescription; const noopNeverResolvedPromise = ()=> new Promise(o$1); // --- 直播增强状态变量 --- // REMOVED // --- 新增:CDN 优化核心逻辑 (移植并适配自 1.txt) --- /** * 检查 URL 是否为 Bilibili Mirror CDN (包括 akamaized 和 tf) * @param {string} urlString * @returns {boolean} */ function isVodTypeMirror(urlString) { if (!urlString) return false; return mirrorRegex.test(urlString); } /** * 从 'os' 参数检测是否为海外 CDN * @param {string} os - 'os' URL 参数值 * @param {string} urlString - 完整 URL 字符串 * @returns {boolean} */ function detectIfOvFromOs(os, urlString) { if (!os) return false; // 检查 host 是否包含 'cn-hk-' (针对 bcache 的海外判断) const detectIfOvFromBCacheHost = (host) => host && host.includes("cn-hk-"); let host; try { host = new URL(urlString).hostname; } catch(e) { host = ''; } return os.includes("ov") || os === "akam" || (os === "bcache" && detectIfOvFromBCacheHost(host)); } /** * 根据 'og' 参数构建新的 Mirror CDN URL (JS版 buildNewBaseUrlFromOg) * @param {URL} uri - 原始 URL 对象 * @param {string} [forcedOgMirrorSuffix] - 可选,强制使用的 mirror 后缀 (如 'cos', 'ali') * @returns {string | null} - 新的 base URL 或 null */ function buildNewBaseUrlFromOgJS(uri, forcedOgMirrorSuffix = null) { let newBaseUrl = null; if (IS_LOCATED_IN_CN) { // 假设在中国 const og = forcedOgMirrorSuffix || uri.searchParams.get("og"); if (og) { let mirrorSuffix; switch (og) { case "hw": mirrorSuffix = "08c"; break; case "cos": mirrorSuffix = "cos"; break; case VOD_ORIGIN_ALI: mirrorSuffix = "ali"; break; case VOD_ORIGIN_BD: mirrorSuffix = "bd"; break; default: if (!unknownOgsCache.has(og)) { unknownOgsCache.add(og); console.warn(`Tampermonkey Script "优化Bilibili" [CDN Optimize]: 未知 OG 参数 "${og}",URL: ${uri.href}。尝试默认。`); if (unknownOgsCache.size > 512) unknownOgsCache.clear(); } if (og.startsWith("ali")) mirrorSuffix = "ali"; else if (og.startsWith("cos")) mirrorSuffix = "cos"; else if (og.startsWith("hw")) mirrorSuffix = "08c"; else if (og.startsWith("bd")) mirrorSuffix = "bd"; else mirrorSuffix = "ali"; // 默认回退 } newBaseUrl = `${uri.protocol}//upos-sz-mirror${mirrorSuffix}.bilivideo.com${uri.pathname}${uri.search}`; } } else { // 非中国大陆,使用 akamaized newBaseUrl = `${uri.protocol}//${VOD_MIRROR_AKAM_OVERSEAS}${uri.pathname}${uri.search}`; } // 1.txt 中的 HEAD 请求验证可以后续添加,如果需要 return newBaseUrl; } /** * 尝试替换海外 CDN (JS版 tryReplaceOverseaCDN) * @param {URL} uri - 原始 URL 对象 * @returns {string | null} - 新的 URL 或 null */ function tryReplaceOverseaCDNJS(uri) { // if (!Settings.ReplaceOverseaCdn()) return null; // 假设总是替换 const os = uri.searchParams.get("os"); if (!os) return null; let newAuthority = null; if (os.startsWith(VOD_ORIGIN_ALI)) newAuthority = VOD_MIRROR_ALI; else if (os.startsWith(VOD_ORIGIN_COS)) newAuthority = VOD_MIRROR_COS; else if (os.startsWith(VOD_ORIGIN_HW)) newAuthority = VOD_MIRROR_HW; else if (os.startsWith(VOD_ORIGIN_08)) newAuthority = VOD_MIRROR_08; else if (os.startsWith(VOD_ORIGIN_BD)) newAuthority = VOD_MIRROR_BD; else if (os.includes("ov")) newAuthority = VOD_MIRROR_ALI; // 默认海外用 ali mirror else { console.warn(`Tampermonkey Script "优化Bilibili" [CDN Optimize]: 替换海外 CDN 时遇到未知 os: ${os}`); } if (newAuthority) { return `${uri.protocol}//${newAuthority}${uri.pathname}${uri.search}`; } // 如果没有特定规则匹配,尝试基于 'og' 构建 return buildNewBaseUrlFromOgJS(uri); } /** * 代理 MCDN (PCDN) URL (JS版 proxyMCdn) * @param {string} urlString - 原始 MCDN URL * @param {string} [scheme="https"] - 协议 * @returns {string | null} - 代理后的 URL 或 null */ function proxyMCdnJS(urlString, scheme = "https") { if (IS_LOCATED_IN_CN && mCdnTfRegex.test(urlString)) { // 注意:原始代码中 proxy-tf-all-ws.bilivideo.com 似乎是内部或特定用途的, // 公网直接使用可能无效或被限制。这里需要确认一个可用的代理服务器或策略。 // 暂时返回一个标准的 mirror CDN 作为替代 PCDN 的方案。 // 或者,如果B站前端JS有已知的MCDN到CDN的转换逻辑,可以模拟。 // 作为演示,我们先尝试构建一个 mirror URL console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize]: 尝试代理 MCDN: ${urlString}。实际代理服务器需确认。`); try { const tempUri = new URL(urlString); return buildNewBaseUrlFromOgJS(tempUri, VOD_ORIGIN_COS.substring(0,3)); // 尝试用 cos mirror } catch(e) { return null; } // return `${scheme}://proxy-tf-all-ws.bilivideo.com/?url=${encodeURIComponent(urlString)}`; } return null; } /** * 主要的 CDN 替换函数 (JS版 tryReplaceCDN) * @param {string} baseUrlString - 原始基础 URL * @param {string[]} backupUrlStrings - 备用 URL 列表 * @param {string} [context=''] - 调用上下文日志 * @returns {string} - 优化后的 URL,如果无法优化则返回原始 baseUrlString */ function tryReplaceCDNJS(baseUrlString, backupUrlStrings = [], context = '') { if (!baseUrlString) return baseUrlString; let baseUri; try { baseUri = new URL(baseUrlString); } catch (e) { // console.warn(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: 无效的 baseUrl: ${baseUrlString}`, e); return baseUrlString; // 无效 URL 则返回原始 } const baseUriOs = baseUri.searchParams.get("os"); // 目前主要处理点播,直播 CDN (os 为空或特定格式) 暂不深入优化,维持P2P禁用即可 if (!baseUriOs) { // console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: URL ${baseUrlString} 无 "os" 参数,跳过深度优化。`); // 对于没有 'os' 参数的 P2P CDN,简单替换 if (isP2PCDNOriginalMatcher(baseUrlString)) { const defaultReplacement = `${baseUri.protocol}//${DEFAULT_MIRROR_CDN}${baseUri.pathname}${baseUri.search}`; console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: 无 "os" 的 P2P URL ${baseUrlString} 替换为 ${defaultReplacement}`); return defaultReplacement; } return baseUrlString; } let bestUrl = baseUrlString; // 默认最佳 URL 是原始 URL // 1. 优先从 backupUrls 中寻找 Mirror CDN const mirrorBackup = backupUrlStrings.find(isVodTypeMirror); if (mirrorBackup) { let mirrorBackupUri; try { mirrorBackupUri = new URL(mirrorBackup); } catch(e) { /* ignore */ } if (mirrorBackupUri) { const mirrorBackupOs = mirrorBackupUri.searchParams.get("os"); // 如果 Mirror Backup 是海外的,也尝试替换成国内 Mirror if (mirrorBackupOs && detectIfOvFromOs(mirrorBackupOs, mirrorBackup)) { const replacedMirror = tryReplaceOverseaCDNJS(mirrorBackupUri); if (replacedMirror) { bestUrl = replacedMirror; console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: 使用替换后的海外 Mirror Backup: ${bestUrl}`); } else { bestUrl = mirrorBackup; // 使用原始海外 Mirror console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: 使用海外 Mirror Backup: ${bestUrl}`); } } else { bestUrl = mirrorBackup; // 国内 Mirror Backup console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: 使用国内 Mirror Backup: ${bestUrl}`); } // 如果已经找到了最佳的 Mirror,可以提前返回 if (isVodTypeMirror(bestUrl) && !detectIfOvFromOs(new URL(bestUrl).searchParams.get("os"), bestUrl) ) { return bestUrl; } } } // 还原 bestUrl 为 baseUrlString,因为上面的 mirrorBackup 可能是海外的,需要进一步判断 bestUrl = baseUrlString; let currentBestUri = baseUri; // 当前最佳 URL 对应的 URI 对象 // 检查当前 bestUrl (可能是原始 baseUrl,也可能是上面选的 mirrorBackup) const currentBestUrlOs = currentBestUri.searchParams.get("os"); // 2. 处理 upos 和 bcache (非 PCDN) if ((currentBestUrlOs === "upos" || currentBestUrlOs === "bcache") && !mCdnTfRegex.test(bestUrl)) { if (isVodTypeMirror(bestUrl)) { // 如果已经是 mirror (比如从 backup 选的) // 如果是海外 mirror,尝试转国内 if (detectIfOvFromOs(currentBestUrlOs, bestUrl)) { const replaced = tryReplaceOverseaCDNJS(currentBestUri); if (replaced) { console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: upos/bcache 海外 Mirror ${bestUrl} 替换为 ${replaced}`); return replaced; } } return bestUrl; // 已经是国内 mirror,直接用 } // 不是 mirror,尝试从 og 构建 const builtFromOg = buildNewBaseUrlFromOgJS(currentBestUri); if (builtFromOg) { console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: upos/bcache ${bestUrl} 基于 OG 替换为 ${builtFromOg}`); return builtFromOg; } } // 3. 处理 MCDN (PCDN) else if (currentBestUrlOs === "mcdn" || mCdnTfRegex.test(bestUrl)) { // 如果上面已经选了 Mirror Backup 且是国内的,就用它 if (mirrorBackup && isVodTypeMirror(mirrorBackup) && !detectIfOvFromOs(new URL(mirrorBackup).searchParams.get("os"), mirrorBackup)) { console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: MCDN ${bestUrl} 使用优先的国内 Mirror Backup ${mirrorBackup}`); return mirrorBackup; } // 尝试从 backupUrls 中找 upos/bcache 并用 buildNewBaseUrlFromOgJS 转换 for (const backupStr of backupUrlStrings) { try { const backupUri = new URL(backupStr); const backupOs = backupUri.searchParams.get("os"); // 确保不是 /v1/resource (来自1.txt的注释) if ((backupOs === "upos" || backupOs === "bcache") && !backupUri.pathname.startsWith("/v1/resource")) { const built = buildNewBaseUrlFromOgJS(backupUri); if (built) { console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: MCDN ${bestUrl} 从 Backup ${backupStr} 构建得到 ${built}`); return built; } } } catch (e) { /* ignore invalid backup url */ } } // 尝试代理 MCDN (目前是返回一个mirror) const proxied = proxyMCdnJS(bestUrl); if (proxied) { console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: MCDN ${bestUrl} 代理/替换为 ${proxied}`); return proxied; } } // 4. 处理普通海外 CDN else if (detectIfOvFromOs(currentBestUrlOs, bestUrl)) { const replaced = tryReplaceOverseaCDNJS(currentBestUri); if (replaced) { console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: 海外 CDN ${bestUrl} 替换为 ${replaced}`); return replaced; } } // 如果所有策略都未替换,且原始 URL 是 P2P 且我们希望禁用 P2P,则给一个默认 Mirror if (isP2PCDNOriginalMatcher(baseUrlString) && isP2pCdnDisabled) { const defaultReplacement = `${baseUri.protocol}//${DEFAULT_MIRROR_CDN}${baseUri.pathname}${baseUri.search}`; console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: P2P URL ${baseUrlString} 未匹配特定规则,回退至默认 Mirror: ${defaultReplacement}`); return defaultReplacement; } // console.log(`Tampermonkey Script "优化Bilibili" [CDN Optimize ${context}]: URL ${baseUrlString} 无优化策略命中,返回原始。`); return baseUrlString; // 返回原始 URL 如果没有优化策略命中 } // --- 禁用追踪与日志核心逻辑 --- function disableTrackingAndLogging() { console.log('Tampermonkey Script "优化Bilibili": 尝试应用禁用追踪与日志设置:', isTrackingLoggingDisabled); // 调试信息 if (isTrackingLoggingDisabled) { // 1. 覆盖全局对象和函数 defineReadonlyProperty(unsafeWindow.navigator, 'sendBeacon', r); // 使用返回 true 的版本,避免报错,但无效果 const SentryHub = createMockClass('SentryHub'); const fakeSentry = { SDK_NAME: 'sentry.javascript.browser', SDK_VERSION: '0.0.1', BrowserClient: createMockClass('Sentry.BrowserClient'), Hub: SentryHub, Integrations: { Vue: createMockClass('Sentry.Integrations.Vue'), GlobalHandlers: createMockClass('Sentry.Integrations.GlobalHandlers'), InboundFilters: createMockClass('Sentry.Integrations.InboundFilters') }, init: o$1, configureScope: o$1, getCurrentHub: ()=>new SentryHub(), setContext: o$1, setExtra: o$1, setExtras: o$1, setTag: o$1, setTags: o$1, setUser: o$1, wrap: o$1 }; defineReadonlyProperty(unsafeWindow, 'Sentry', fakeSentry); defineReadonlyProperty(unsafeWindow, 'MReporter', createMockClass('MReporter')); defineReadonlyProperty(unsafeWindow, 'ReporterPb', createMockClass('ReporterPb')); defineReadonlyProperty(unsafeWindow, '__biliUserFp__', { init: o$1, queryUserLog: () => [] }); defineReadonlyProperty(unsafeWindow, '__USER_FP_CONFIG__', undefined); defineReadonlyProperty(unsafeWindow, '__MIRROR_CONFIG__', undefined); defineReadonlyProperty(unsafeWindow, 'heimdallTrack', o$1); // 屏蔽首页心跳上报 // 2. 拦截 fetch (移至 updateNetworkInterceptors 和 masterFetchOverride) // 3. 拦截 XMLHttpRequest (移至 updateNetworkInterceptors 和 masterXhrOpenOverride) // 4. 拦截 IndexedDB if (originalIndexedDBOpen) { unsafeWindow.indexedDB.open = function(name, version) { if (TRACKING_IDB_NAMES.includes(name)) { console.log('Tampermonkey Script "优化Bilibili": 阻止 IndexedDB 打开:', name); // 返回一个立即失败的请求 const dummyRequest = {}; setTimeout(() => { const event = new Event('error', { bubbles: true }); Object.defineProperty(event, 'target', { value: { error: new DOMException('Blocked by userscript', 'AbortError') } }); if (typeof dummyRequest.onerror === 'function') { dummyRequest.onerror(event); } }, 0); return dummyRequest; // 返回一个空对象模拟 IDBOpenDBRequest } return originalIndexedDBOpen.apply(this, arguments); }; // 尝试删除已存在的追踪数据库 TRACKING_IDB_NAMES.forEach(name => { try { const req = unsafeWindow.indexedDB.deleteDatabase(name); req.onsuccess = () => console.log(`Tampermonkey Script "优化Bilibili": IndexedDB ${name} 已删除 (或不存在).`); req.onerror = (e) => console.warn(`Tampermonkey Script "优化Bilibili": 删除 IndexedDB ${name} 失败:`, e); } catch (e) { console.warn(`Tampermonkey Script "优化Bilibili": 删除 IndexedDB ${name} 时出错:`, e); } }); } // 5. 拦截 localStorage (只读,防止写入追踪数据,不影响读取正常数据) if (originalLsSetItem) { unsafeWindow.localStorage.setItem = function(key, value) { if (shouldRemoveLsKey(key)) { console.log('Tampermonkey Script "优化Bilibili": 阻止 localStorage 写入:', key); return; // 阻止写入 } return originalLsSetItem.apply(this, arguments); }; } if (originalLsRemoveItem) { unsafeWindow.localStorage.removeItem = function(key) { if (shouldRemoveLsKey(key)) { console.log('Tampermonkey Script "优化Bilibili": 阻止 localStorage 删除 (但也模拟删除):', key); return; // 假装删除,但不真删,防止脚本逻辑出错 } return originalLsRemoveItem.apply(this, arguments); }; } // 清理已存在的追踪 localStorage try { for (let i = originalLocalStorage.length - 1; i >= 0; i--) { const key = originalLsKey.call(originalLocalStorage, i); if (shouldRemoveLsKey(key)) { console.log('Tampermonkey Script "优化Bilibili": 清理 localStorage:', key); originalLsRemoveItem.call(originalLocalStorage, key); } } } catch (e) { console.warn('Tampermonkey Script "优化Bilibili": 清理 localStorage 时出错:', e); } // 6. 清理 URL 参数 unsafeWindow.history.pushState = function(state, unused, url) { return originalHistoryPushState.call(this, state, unused, removeUselessUrlParams(url)); }; unsafeWindow.history.replaceState = function(state, unused, url) { return originalHistoryReplaceState.call(this, state, unused, removeUselessUrlParams(url)); }; // 初始清理一次当前 URL const currentCleanedUrl = removeUselessUrlParams(window.location.href); if (window.location.href !== currentCleanedUrl) { originalHistoryReplaceState.call(window.history, window.history.state, '', currentCleanedUrl); console.log('Tampermonkey Script "优化Bilibili": 初始清理 URL 参数'); } } else { // 恢复原始函数 (如果可能且安全) // 注意:恢复 prototype 上的修改可能影响其他脚本,通常需要刷新页面 // unsafeWindow.fetch = originalFetch; // 由 updateNetworkInterceptors 处理 // unsafeWindow.XMLHttpRequest.prototype.open = originalXhrOpen; // 由 updateNetworkInterceptors 处理 unsafeWindow.navigator.sendBeacon = originalSendBeacon; if (originalIndexedDBOpen) unsafeWindow.indexedDB.open = originalIndexedDBOpen; if (originalLsSetItem) unsafeWindow.localStorage.setItem = originalLsSetItem; if (originalLsRemoveItem) unsafeWindow.localStorage.removeItem = originalLsRemoveItem; unsafeWindow.history.pushState = originalHistoryPushState; unsafeWindow.history.replaceState = originalHistoryReplaceState; // Mock 的全局变量无法安全地"恢复",因为原始对象可能已被修改或不存在 console.log('Tampermonkey Script "优化Bilibili": 尝试恢复原始追踪功能 (部分功能可能需要刷新页面生效)。'); } // 应用更改后更新网络拦截器 updateNetworkInterceptors(); } // --- 禁用 P2P CDN 和 WebRTC 核心逻辑 --- // 旧的 getCDNDomain 和 replaceP2P 不再是 CDN 优化的主要逻辑,但 replaceP2P 可能仍用于 HTMLMediaElement.src 的简单P2P替换 // getCDNDomain 不再需要主动从 head 提取, intelligentCdnCache 将从 playUrlApi 更新 function getFallbackCdnDomain() { if (intelligentCdnCache.length > 0) { // 优先用缓存中的 Mirror CDN return intelligentCdnCache[Math.floor(Math.random() * intelligentCdnCache.length)]; } // console.warn('Tampermonkey Script "优化Bilibili" [CDN Fallback]: intelligentCdnCache 为空, 使用硬编码默认值.'); return DEFAULT_MIRROR_CDN; // 默认备用 CDN } // replaceP2P 函数现在主要用于非常直接的 P2P 地址替换,例如在 HTMLMediaElement.src setter 中 // 更复杂的 CDN 选择逻辑在 tryReplaceCDNJS 中 function simpleReplaceP2PUrl(url, meta = '') { try { let urlString = ''; let originalUrl = url; // 保存原始输入以在返回时保持类型 let parsedUrl; if (typeof url === 'string') { urlString = url; // 增加对mcdn.bilivideo的直接处理 if (urlString.includes('.mcdn.bilivideo')) { return forceMcdnReplace(urlString); } // 原有P2P检测逻辑 if (!isP2PCDNOriginalMatcher(urlString)) return url; if (urlString.startsWith('//')) urlString = (unsafeWindow.location.protocol || 'https:') + urlString; try { parsedUrl = new URL(urlString); } catch { return originalUrl; } } else if (url instanceof URL) { parsedUrl = url; urlString = url.href; // 增加对mcdn.bilivideo的直接处理 if (urlString.includes('.mcdn.bilivideo')) { const replaced = forceMcdnReplace(urlString); if (replaced !== urlString) { try { return new URL(replaced); } catch { return originalUrl; } } } if (!isP2PCDNOriginalMatcher(urlString)) return url; } else { return originalUrl; } const hostname = parsedUrl.hostname; let replaced = false; if (hostname.endsWith('.mcdn.bilivideo.cn') || hostname.endsWith('.szbdyd.com')) { // 对于这两种已知的 P2P CDN,直接替换为智能缓存或默认的 Mirror CDN // .szbdyd.com 的 xy_usource 逻辑可以被更通用的 Mirror 替换覆盖 const cdn = getFallbackCdnDomain(); // 获取一个备选的 Mirror CDN host parsedUrl.hostname = cdn; parsedUrl.protocol = 'https:'; // 强制 HTTPS parsedUrl.port = ''; // 清除端口号,HTTPS 默认443 // 移除可能导致问题的特定 P2P 查询参数 (如果已知) // parsedUrl.searchParams.delete('xy_usource'); console.log(`Tampermonkey Script "优化Bilibili" [Simple P2P Replace - ${meta}]: URL 替换: ${hostname} -> ${cdn}`); replaced = true; } return replaced ? (typeof originalUrl === 'string' ? parsedUrl.href : parsedUrl) : originalUrl; } catch (e) { console.error(`Tampermonkey Script "优化Bilibili" [Simple P2P Replace - ${meta}]: 替换 P2P URL (${url}) 时出错:`, e); return url instanceof URL ? url.href : url; } } // 应用/移除 P2P 和 WebRTC 禁用逻辑 function disableP2pCdnAndWebRTC(disable) { console.log('Tampermonkey Script "优化Bilibili": 应用 P2P/WebRTC 禁用设置:', disable); if (disable) { // 1. Mock P2P 相关对象 defineReadonlyProperty(unsafeWindow, 'PCDNLoader', class MockPCDNLoader {}); defineReadonlyProperty(unsafeWindow, 'BPP2PSDK', class MockBPP2PSDK { on = o$1; }); defineReadonlyProperty(unsafeWindow, 'SeederSDK', class MockSeederSDK {}); // 2. Mock WebRTC 相关对象 class MockDataChannel { close = o$1; send = o$1; addEventListener = o$1; removeEventListener = o$1; onbufferedamountlow = null; onclose = null; onerror = null; onmessage = null; toString() { return '[object RTCDataChannel]'; } } class MockRTCSessionDescription { type; sdp; constructor(init){ this.type = init?.type; this.sdp = init?.sdp || ''; } toJSON() { return { type: this.type, sdp: this.sdp }; } toString() { return '[object RTCSessionDescription]'; } } const mockedRtcSessionDescription = new MockRTCSessionDescription({ type: 'offer', sdp: '' }); class MockRTCPeerConnection { close = o$1; createOffer = noopNeverResolvedPromise; setLocalDescription = o$1; setRemoteDescription = o$1; addEventListener = o$1; removeEventListener = o$1; addIceCandidate = o$1; setConfiguration = o$1; get localDescription() { return mockedRtcSessionDescription; } createAnswer = noopNeverResolvedPromise; onicecandidate = null; createDataChannel = ()=> new MockDataChannel(); toString() { return '[object RTCPeerConnection]'; } } if (originalRTCPeerConnection) defineReadonlyProperty(unsafeWindow, 'RTCPeerConnection', MockRTCPeerConnection); if (originalWebkitRTCPeerConnection) defineReadonlyProperty(unsafeWindow, 'webkitRTCPeerConnection', MockRTCPeerConnection); if (originalMozRTCPeerConnection) defineReadonlyProperty(unsafeWindow, 'mozRTCPeerConnection', MockRTCPeerConnection); if (originalRTCDataChannel) defineReadonlyProperty(unsafeWindow, 'RTCDataChannel', MockDataChannel); if (originalWebkitRTCDataChannel) defineReadonlyProperty(unsafeWindow, 'webkitRTCDataChannel', MockDataChannel); if (originalMozRTCDataChannel) defineReadonlyProperty(unsafeWindow, 'mozRTCDataChannel', MockDataChannel); if (originalRTCSessionDescription) defineReadonlyProperty(unsafeWindow, 'RTCSessionDescription', MockRTCSessionDescription); // 3. Patch HTMLMediaElement.prototype.src if (originalHtmlMediaSrcDescriptor) { Object.defineProperty(unsafeWindow.HTMLMediaElement.prototype, 'src', { ...originalHtmlMediaSrcDescriptor, set (value) { let newValue = value; try { // 当 P2P 禁用时,对于 src 的设置,我们只简单替换已知的 P2P URL // 复杂的 CDN 选择在 XHR/Fetch 层面处理 PlayURL 响应 if (typeof value === 'string' && isP2PCDNOriginalMatcher(value)) { newValue = simpleReplaceP2PUrl(value, 'HTMLMediaElement.src'); } else { // 如果不是 P2P URL,但 P2P 禁用开启,理论上它应该是已经优化过的 URL // 或者,如果它是未经优化的非P2P URL,我们在这里没有足够上下文(如backupUrls)做完整 tryReplaceCDNJS // 维持原样,依赖网络拦截的优化。 } } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [P2P Disable]: 处理 HTMLMediaElement.src setter 时出错', e, { value }); } originalHtmlMediaSrcDescriptor.set?.call(this, newValue); } }); } } else { // 恢复原始对象/方法 (如果可能且安全) // ... (恢复逻辑保持不变) ... if (originalHtmlMediaSrcDescriptor) Object.defineProperty(unsafeWindow.HTMLMediaElement.prototype, 'src', originalHtmlMediaSrcDescriptor); if (originalRTCPeerConnection) unsafeWindow.RTCPeerConnection = originalRTCPeerConnection; if (originalWebkitRTCPeerConnection) unsafeWindow.webkitRTCPeerConnection = originalWebkitRTCPeerConnection; if (originalMozRTCPeerConnection) unsafeWindow.mozRTCPeerConnection = originalMozRTCPeerConnection; if (originalRTCDataChannel) unsafeWindow.RTCDataChannel = originalRTCDataChannel; if (originalWebkitRTCDataChannel) unsafeWindow.webkitRTCDataChannel = originalWebkitRTCDataChannel; if (originalMozRTCDataChannel) unsafeWindow.mozRTCDataChannel = originalMozRTCDataChannel; if (originalRTCSessionDescription) unsafeWindow.RTCSessionDescription = originalRTCSessionDescription; console.log('Tampermonkey Script "优化Bilibili": 尝试恢复 P2P/WebRTC 功能 (可能需要刷新页面生效)。'); } // 应用更改后更新网络拦截器 updateNetworkInterceptors(); } // --- 直播增强核心逻辑 --- // ... (保持不变, 因为已移除了画质强制部分) ... function applyLiveEnhancements(enabled) { console.log('Tampermonkey Script "优化Bilibili": 应用直播增强设置:', enabled); const styleElement = document.getElementById(LIVE_ENHANCEMENT_STYLE_ID); if (enabled && window.location.hostname === 'live.bilibili.com') { // 只在直播页启用 if (!styleElement) { const newStyle = document.createElement('style'); newStyle.id = LIVE_ENHANCEMENT_STYLE_ID; newStyle.textContent = ` /* 修复播放器遮挡 */ div[data-cy=EvaRenderer_LayerWrapper]:has(.player) { z-index: 999999 !important; } /* 隐藏欢迎横幅和房间状态图标 */ #welcome-area-bottom-vm, .web-player-icon-roomStatus { display: none !important; } `; (document.head || document.documentElement).appendChild(newStyle); } } else { if (styleElement) { styleElement.remove(); } } updateNetworkInterceptors(); } // --- URL 参数清理 --- // ... (保持不变) ... function removeUselessUrlParams(url) { if (!url) return url; try { let urlObj; if (typeof url === 'string') { try { urlObj = new URL(url); } catch (e) { if (url.startsWith('/')) { urlObj = new URL(url, window.location.origin); } else { console.warn('Tampermonkey Script "优化Bilibili": 无法解析 URL:', url, e); return url; } } } else if (url instanceof URL) { urlObj = url; } else { console.warn('Tampermonkey Script "优化Bilibili": 无效的 URL 类型:', url); return url instanceof URL ? url.href : url; // 返回原始输入 } if (!urlObj.search) return urlObj.href; // 没有参数,直接返回 const params = urlObj.searchParams; const keysToRemove = []; for (const key of params.keys()) { for (const pattern of USELESS_URL_PARAMS) { if (typeof pattern === 'string') { if (key === pattern) { keysToRemove.push(key); break; } } else if (pattern instanceof RegExp) { if (pattern.test(key)) { keysToRemove.push(key); break; } } } } if (keysToRemove.length > 0) { keysToRemove.forEach(key => params.delete(key)); return urlObj.href; } else { return urlObj.href; // 没有需要移除的参数 } } catch (e) { console.error('Tampermonkey Script "优化Bilibili": 清理 URL 参数时出错:', e, 'URL:', url); return url instanceof URL ? url.href : url; // 出错时返回原始 URL } } // --- 网络请求拦截 (重点修改) --- // ** Master Fetch Override ** async function masterFetchOverride(...fetchArgsInput) { let fetchArgs = [...fetchArgsInput]; // 可变副本 // 1. CDN 优化 (Before Fetch, 主要针对直接请求媒体片段的P2P URL) if (isP2pCdnDisabled) { try { let input = fetchArgs[0]; let originalInputUrl = getUrlFromRequest(input); // 新增:最高优先级处理 mcdn URL if (originalInputUrl && originalInputUrl.includes('.mcdn.bilivideo')) { const replacedUrl = forceMcdnReplace(originalInputUrl); if (replacedUrl !== originalInputUrl) { if (typeof input === 'string' || input instanceof URL) { fetchArgs[0] = replacedUrl; } else if (input instanceof Request) { fetchArgs[0] = new Request(replacedUrl, { method: input.method, headers: input.headers, body: input.body, mode: input.mode, credentials: input.credentials, cache: input.cache, redirect: input.redirect, referrer: input.referrer, integrity: input.integrity, }); } console.log(`Tampermonkey Script "优化Bilibili" [Fetch MCDN]: 强力替换成功: ${originalInputUrl} -> ${replacedUrl}`); } } // 原有P2P处理逻辑继续... if (originalInputUrl && isP2PCDNOriginalMatcher(originalInputUrl)) { // ... 原有代码 ... } } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [Fetch CDN]: 处理 Fetch URL 时出错', e); } } // 2. 禁用追踪 (Before Fetch) if (isTrackingLoggingDisabled) { const urlForTracking = getUrlFromRequest(fetchArgs[0]); if (typeof urlForTracking === 'string' && shouldBlockUrl(urlForTracking)) { console.log('Tampermonkey Script "优化Bilibili" [Tracking Disable]: 阻止 Fetch 请求:', urlForTracking); return Promise.resolve(new Response(null, { status: 204, statusText: 'No Content (Blocked by My.js)' })); } } // 3. 执行 Fetch try { const response = await originalFetch.apply(this, fetchArgs); // 4. PlayURL API 响应处理 (如果播放器用 fetch 请求 PlayURL API) // 通常 PlayURL API 是 XHR,但以防万一 const responseUrl = response.url; if (isP2pCdnDisabled && responseUrl && responseUrl.includes(playUrlApi) && response.ok) { console.log('Tampermonkey Script "优化Bilibili" [Fetch CDN]: 检测到 PlayURL API 响应 (via Fetch)'); try { const responseCloneForParsing = response.clone(); // 克隆响应以读取内容 const jsonData = await responseCloneForParsing.json(); const originalJsonString = JSON.stringify(jsonData); // 保存原始JSON字符串用于比较 function processStreamData(streamData) { if (Array.isArray(streamData)) { streamData.forEach(stream => { if (stream && (stream.baseUrl || stream.base_url || stream.url)) { const currentBaseUrl = stream.baseUrl || stream.base_url || stream.url; const currentBackupUrls = stream.backupUrl || stream.backup_url || []; const newUrl = tryReplaceCDNJS(currentBaseUrl, currentBackupUrls, 'Fetch PlayURL API'); if (newUrl !== currentBaseUrl) { if (stream.baseUrl) stream.baseUrl = newUrl; if (stream.base_url) stream.base_url = newUrl; if (stream.url) stream.url = newUrl; } // 更新 intelligentCdnCache if (newUrl && isVodTypeMirror(newUrl)) intelligentCdnCache.push(new URL(newUrl).hostname); currentBackupUrls.forEach(bu => { if (bu && isVodTypeMirror(bu)) intelligentCdnCache.push(new URL(bu).hostname); }); } }); } } if (jsonData?.data?.dash) { processStreamData(jsonData.data.dash.video); processStreamData(jsonData.data.dash.audio); } if (jsonData?.data?.durl) { // 处理 durl 格式 processStreamData(jsonData.data.durl); } // 去重 intelligentCdnCache if (intelligentCdnCache.length > 0) { intelligentCdnCache = [...new Set(intelligentCdnCache)]; // console.log('Tampermonkey Script "优化Bilibili" [Fetch CDN]: intelligentCdnCache 更新:', intelligentCdnCache); } const modifiedJsonString = JSON.stringify(jsonData); if (modifiedJsonString !== originalJsonString) { console.log('Tampermonkey Script "优化Bilibili" [Fetch CDN]: PlayURL API (Fetch) 响应已修改。'); // 返回修改后的响应。需要创建一个新的 Response 对象,因为原始 response body 已经读过(或即将被读) return new Response(modifiedJsonString, { status: response.status, statusText: response.statusText, headers: response.headers, // 保留原始头 }); } } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [Fetch CDN]: 解析或修改 PlayURL (Fetch) 响应时出错:', e); } } return response; // 返回最终响应 } catch (error) { console.error('Tampermonkey Script "优化Bilibili": Fetch 执行出错:', error, 'URL:', getUrlFromRequest(fetchArgs[0])); throw error; // 重新抛出错误 } } // ** Master XHR Open & Response Override ** function masterXhrOpenOverride(...args) { let xhrArgs = [...args]; let blockXhr = false; let url = xhrArgs[1]; // URL 可能为 string 或 URL object const originalUrlString = typeof url === 'string' ? url : (url?.toString() || ''); // 新增:最高优先级处理 mcdn URL if (isP2pCdnDisabled && typeof url === 'string' && url.includes('.mcdn.bilivideo')) { const replacedUrl = forceMcdnReplace(url); if (replacedUrl !== url) { xhrArgs[1] = replacedUrl; url = replacedUrl; console.log(`Tampermonkey Script "优化Bilibili" [XHR MCDN]: 强力替换成功: ${url}`); } } const isPlayUrlApiRequest = originalUrlString.includes(playUrlApi); const xhrInstance = this; // 保存 XHR 实例的引用 let originalUserOnReadyStateChange = xhrInstance.onreadystatechange; if (isP2pCdnDisabled && isPlayUrlApiRequest) { // console.log(`Tampermonkey Script "优化Bilibili" [XHR CDN]: Attaching onreadystatechange for PlayURL API: ${originalUrlString}`); xhrInstance.onreadystatechange = function (event) { if (typeof originalUserOnReadyStateChange === 'function') { try { originalUserOnReadyStateChange.apply(xhrInstance, arguments); } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [XHR CDN]: Error in originalUserOnReadyStateChange for PlayURL API', e); } } if (xhrInstance.readyState === 4 && xhrInstance.status === 200 && !xhrInstance._cdnProcessed) { xhrInstance._cdnProcessed = true; // 确保只处理一次 // console.log('Tampermonkey Script "优化Bilibili" [XHR CDN]: PlayURL API (XHR) readyState 4, status 200. Processing...'); try { let responseTextValue = xhrInstance.responseText; // 获取此时真实的 responseText if (responseTextValue) { const jsonData = JSON.parse(responseTextValue); const originalJsonResponseText = JSON.stringify(jsonData); function processStreamData(streamData, context) { if (Array.isArray(streamData)) { streamData.forEach(stream => { if (stream && (stream.baseUrl || stream.base_url || stream.url)) { const currentBaseUrl = stream.baseUrl || stream.base_url || stream.url; const currentBackupUrls = Array.isArray(stream.backupUrl) ? stream.backupUrl : Array.isArray(stream.backup_url) ? stream.backup_url : []; const newUrl = tryReplaceCDNJS(currentBaseUrl, currentBackupUrls, context); if (newUrl !== currentBaseUrl) { if (stream.baseUrl) stream.baseUrl = newUrl; if (stream.base_url) stream.base_url = newUrl; if (stream.url) stream.url = newUrl; } try { if (newUrl && isVodTypeMirror(newUrl)) intelligentCdnCache.push(new URL(newUrl).hostname); currentBackupUrls.forEach(bu => { if (bu && isVodTypeMirror(bu)) intelligentCdnCache.push(new URL(bu).hostname); }); } catch (e) { /* ignore */ } } }); } } if (jsonData?.data?.dash) { processStreamData(jsonData.data.dash.video, 'XHR PlayURL API - Video'); processStreamData(jsonData.data.dash.audio, 'XHR PlayURL API - Audio'); } if (jsonData?.data?.durl) { processStreamData(jsonData.data.durl, 'XHR PlayURL API - Durl'); } if (intelligentCdnCache.length > 0) { intelligentCdnCache = [...new Set(intelligentCdnCache)]; if (prevLocationHrefForCdnCache !== unsafeWindow.location.href) { prevLocationHrefForCdnCache = unsafeWindow.location.href; } } const modifiedJsonString = JSON.stringify(jsonData); if (modifiedJsonString !== originalJsonResponseText) { // console.log('Tampermonkey Script "优化Bilibili" [XHR CDN]: PlayURL API (XHR) response JSON modified.'); xhrInstance._finalResponseText = modifiedJsonString; xhrInstance._finalResponseJson = jsonData; Object.defineProperty(xhrInstance, 'responseText', { get: function() { return this._finalResponseText; }, configurable: true }); Object.defineProperty(xhrInstance, 'response', { get: function() { if (this.responseType === 'json' || (!this.responseType && typeof this._finalResponseJson !== 'undefined')) { return this._finalResponseJson; } return this._finalResponseText; }, configurable: true }); console.log('Tampermonkey Script "优化Bilibili" [XHR CDN]: responseText/response getters redefined for PlayURL API.'); } else { // console.log('Tampermonkey Script "优化Bilibili" [XHR CDN]: PlayURL API (XHR) response JSON not modified.'); } } } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [XHR CDN]: Error processing PlayURL (XHR) response:', e, { responseText: xhrInstance.responseText_}); } } }; } // --- Before Open Phase (CDN 和 Tracking) --- if (isP2pCdnDisabled && !isPlayUrlApiRequest && typeof url === 'string' && isP2PCDNOriginalMatcher(url)) { try { const replacedUrl = tryReplaceCDNJS(url, [], 'XHR Open P2P'); if (replacedUrl !== url) { xhrArgs[1] = replacedUrl; } } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [XHR CDN]: Error processing XHR Open URL for P2P', e, { xhrUrl: url }); } url = xhrArgs[1]; } // 在上面代码后添加:直接处理任何包含mcdn的URL,确保无漏网之鱼 if (isP2pCdnDisabled && !isPlayUrlApiRequest && typeof url === 'string' && url.includes('.mcdn.bilivideo')) { console.log(`Tampermonkey Script "优化Bilibili" [XHR Extra]: 检测到直接的mcdn请求: ${url}`); try { // 尝试使用默认Mirror替换 const cdnHost = getFallbackCdnDomain(); const mcdnPattern = /(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i; const replacedUrl = url.replace(mcdnPattern, `$1${cdnHost}$4`); if (replacedUrl !== url) { console.log(`Tampermonkey Script "优化Bilibili" [XHR Extra]: 已替换MCDN: ${url} -> ${replacedUrl}`); xhrArgs[1] = replacedUrl; } } catch (e) { console.error('Tampermonkey Script "优化Bilibili" [XHR Extra]: 处理MCDN URL出错', e); } url = xhrArgs[1]; } if (isTrackingLoggingDisabled) { let urlForTracking = typeof url === 'string' ? url : (url?.toString() || ''); if (shouldBlockUrl(urlForTracking)) { blockXhr = true; } } if (blockXhr) { Object.defineProperty(this, 'send', { value: o$1, writable: false, configurable: true }); Object.defineProperty(this, 'setRequestHeader', { value: o$1, writable: false, configurable: true }); try { originalXhrOpen.call(this, xhrArgs[0], 'about:blank', ...(xhrArgs.slice(2))); } catch (e) { console.warn("Error blocking XHR open:", e); } return; } else { return originalXhrOpen.apply(this, xhrArgs); } } // --- 更新网络拦截器状态 --- function updateNetworkInterceptors() { // 确定是否需要拦截 fetch (追踪、P2P CDN优化) const needsFetchOverride = isTrackingLoggingDisabled || isP2pCdnDisabled; // 确定是否需要拦截 XHR (追踪、P2P CDN优化 - 因为需要处理响应) const needsXhrOverride = isTrackingLoggingDisabled || isP2pCdnDisabled; if (needsFetchOverride && unsafeWindow.fetch !== masterFetchOverride) { console.log('Tampermonkey Script "优化Bilibili": 应用 Fetch 拦截器'); unsafeWindow.fetch = masterFetchOverride; } else if (!needsFetchOverride && unsafeWindow.fetch === masterFetchOverride) { console.log('Tampermonkey Script "优化Bilibili": 恢复原始 Fetch'); unsafeWindow.fetch = originalFetch; } if (needsXhrOverride && unsafeWindow.XMLHttpRequest.prototype.open !== masterXhrOpenOverride) { console.log('Tampermonkey Script "优化Bilibili": 应用 XHR Open 拦截器'); unsafeWindow.XMLHttpRequest.prototype.open = masterXhrOpenOverride; } else if (!needsXhrOverride && unsafeWindow.XMLHttpRequest.prototype.open === masterXhrOpenOverride) { console.log('Tampermonkey Script "优化Bilibili": 恢复原始 XHR Open'); unsafeWindow.XMLHttpRequest.prototype.open = originalXhrOpen; } } // ---- 初始执行 功能应用 和 网络拦截更新 ---- disableTrackingAndLogging(); // 应用追踪设置 (除网络拦截) disableP2pCdnAndWebRTC(isP2pCdnDisabled); // 应用 P2P/WebRTC 设置 (除网络拦截), 这会调用 updateNetworkInterceptors applyLiveEnhancements(isLiveEnhancementEnabled); // 应用直播设置 (除网络拦截), 这会调用 updateNetworkInterceptors applyAv1Disable(isAv1Disabled); // 新增:应用 AV1 禁用设置 applyForce4k(isForce4kEnabled); // 新增:应用强制4K设置 // updateNetworkInterceptors(); // 在上述函数内部各自调用,确保顺序正确 // 添加:强制立即注入加速函数,特别是XMLHttpRequest拦截器 // 不等待页面加载或其他依赖,确保最早拦截到视频请求 (function forceEarlyInit() { // 立即注入视频元素拦截 interceptVideoElements(); // 立即给document添加早期监听,确保在页面加载过程中就能捕获视频元素 if (document.readyState === 'loading' || document.readyState === 'interactive') { // 在DOM尚未完全加载时,对每个新添加的节点立即进行检查 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeName === 'VIDEO' || node.nodeName === 'AUDIO') { // 即刻添加监听器 node.addEventListener('loadstart', () => { if (node.src && node.src.includes('.mcdn.bilivideo')) { const newSrc = forceMcdnReplace(node.src); if (newSrc !== node.src) { console.log(`Tampermonkey Script "优化Bilibili" [早期DOM监听]: 替换src: ${node.src} -> ${newSrc}`); node.src = newSrc; } } }, true); // 立即检查当前src if (node.src && node.src.includes('.mcdn.bilivideo')) { const newSrc = forceMcdnReplace(node.src); if (newSrc !== node.src) { console.log(`Tampermonkey Script "优化Bilibili" [早期DOM监听]: 直接替换src: ${node.src} -> ${newSrc}`); node.src = newSrc; } } } }); }); }); observer.observe(document, { childList: true, subtree: true }); console.log('Tampermonkey Script "优化Bilibili": 早期DOM监听已启动'); } // 强制重新应用XHR和fetch拦截 unsafeWindow.XMLHttpRequest.prototype.open = masterXhrOpenOverride; unsafeWindow.fetch = masterFetchOverride; // 清空CDN缓存,确保总是重新替换 prevLocationHrefForCdnCache = ''; // 设置默认Mirror,确保即使找不到合适的镜像也能替换 if (intelligentCdnCache.length === 0) { intelligentCdnCache = [ VOD_MIRROR_COS, // 主要CDN VOD_MIRROR_ALI, // 备用CDN1 VOD_MIRROR_08 // 备用CDN2 ]; } // 应用URL参数修改器,处理视频直链 const urlParamHandler = function(details) { const url = details.url || ''; if (url.includes('.mcdn.bilivideo')) { const newUrl = forceMcdnReplace(url); if (newUrl !== url) { console.log(`Tampermonkey Script "优化Bilibili" [URL参数处理]: ${url} -> ${newUrl}`); return { redirectUrl: newUrl }; } } }; // 监听video和iframe元素,这是从b2.js的经验中获取的 const waitForElm = (selector) => new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.body, { childList: true, subtree: true }); }); // 等待视频元素并监听 waitForElm('video').then(videoEl => { console.log('Tampermonkey Script "优化Bilibili": 检测到视频元素,添加监听'); videoEl.addEventListener('canplay', () => { if (videoEl.src && videoEl.src.includes('.mcdn.bilivideo')) { const newSrc = forceMcdnReplace(videoEl.src); if (newSrc !== videoEl.src) { console.log(`Tampermonkey Script "优化Bilibili" [视频canplay]: 替换src: ${videoEl.src} -> ${newSrc}`); videoEl.src = newSrc; } } }); }); console.log('Tampermonkey Script "优化Bilibili": 强制提前初始化网络拦截器已完成'); })(); // --- UI 和页面逻辑 --- // ... (UI 和页面特定逻辑保持不变) ... function initSettingsUI() { if (document.readyState === 'loading' || document.readyState === 'interactive') { // 在 interactive 状态也可能需要等待 document.addEventListener('DOMContentLoaded', createSettingsButton); } else { createSettingsButton(); // 如果 'complete' 则直接创建 } } function createSettingsButton() { // 检查是否已存在,避免重复插入 if (document.getElementById('bilibiliSettingsButton')) return; // 查找目标容器 - 延迟查找,确保 DOM 加载 const paletteButtonWrap = document.querySelector('.palette-button-wrap'); if (!paletteButtonWrap) { // console.warn('Tampermonkey Script "优化Bilibili": 未找到 .palette-button-wrap 容器,稍后重试。'); // 使用 setTimeout 稍微延迟重试,给页面更多加载时间 setTimeout(createSettingsButton, 500); // 500ms 后重试 return; } console.log('Tampermonkey Script "优化Bilibili": 找到 .palette-button-wrap 容器。'); // 创建设置按钮 const settingsButton = document.createElement('div'); settingsButton.id = 'bilibiliSettingsButton'; settingsButton.className = 'settings-button'; settingsButton.innerHTML = ` `; settingsButton.style.cssText = ` width: 40px; height: 40px; background: #fff; border-radius: 6px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #18191c; box-shadow: 0 2px 4px rgba(0,0,0,.1); transition: all .2s; position: relative; /* 改回 relative,让其在文档流中 */ z-index: 10; /* 确保在按钮容器内可见 */ pointer-events: auto; margin-bottom: 12px; /* 与其他按钮保持一致的间距 */ `; // 添加悬停效果 settingsButton.addEventListener('mouseover', function() { this.style.color = '#00aeec'; }); settingsButton.addEventListener('mouseout', function() { this.style.color = '#18191c'; }); // 创建设置面板容器 const toggleContainer = document.createElement('div'); toggleContainer.id = 'settingsToggleContainer'; toggleContainer.style.cssText = ` position: fixed; z-index: 100000; background-color: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); display: none; min-width: 120px; `; // 添加设置面板内容 (增加新选项) toggleContainer.innerHTML = ` `; // 添加点击事件 (在捕获阶段监听) settingsButton.addEventListener('click', function(e) { console.log('Tampermonkey Script "优化Bilibili": 设置按钮被点击!'); // 更新日志 e.preventDefault(); e.stopPropagation(); // 阻止事件继续传播 const container = document.getElementById('settingsToggleContainer'); if (container) { const isVisible = container.style.display === 'block'; if (isVisible) { container.style.display = 'none'; } else { // 计算面板位置 const btnRect = settingsButton.getBoundingClientRect(); const panelWidthEstimate = 160; // 调整宽度以适应 "视频播放增强 (CDN优化)" const gap = 10; const panelHeightEstimate = 300; // 增加高度以适应新选项和更长的文本 let top = btnRect.top - panelHeightEstimate - gap; top = Math.max(10, top); // 微调:将面板稍微向上移动一点 top -= 10; // 将面板向上移动的距离减少 const horizontalOffset = 20; // 将面板向右移动20px let left = btnRect.left + (btnRect.width / 2) - (panelWidthEstimate / 2) + horizontalOffset; left = Math.max(10, left); left = Math.min(left, window.innerWidth - panelWidthEstimate - 10); container.style.top = `${top}px`; container.style.left = `${left}px`; container.style.display = 'block'; } } }, true); // 捕获阶段 // 将设置按钮插入到目标容器开头,将面板添加到页面 // paletteButtonWrap.insertBefore(settingsButton, paletteButtonWrap.firstChild); paletteButtonWrap.prepend(settingsButton); // 使用 prepend 插入到最前面 console.log('Tampermonkey Script "优化Bilibili": 设置按钮已添加到 .palette-button-wrap.'); // 更新日志 document.body.appendChild(toggleContainer); // 添加点击外部关闭面板的功能 document.addEventListener('click', function(e) { const container = document.getElementById('settingsToggleContainer'); const btn = document.getElementById('bilibiliSettingsButton'); // 确保点击的目标不是按钮本身或面板内部 if (container && container.style.display === 'block' && (!container.contains(e.target)) && (!btn || !btn.contains(e.target))) { container.style.display = 'none'; } }); // 添加复选框的事件监听器 toggleContainer.addEventListener('change', function(e) { if (e.target.type === 'checkbox') { let needsReload = false; // 标记是否需要提示刷新 switch(e.target.id) { case 'adRemovalToggle': isAdRemovalEnabled = e.target.checked; GM_setValue('bilibiliAdRemovalEnabled', isAdRemovalEnabled); // 主页广告移除是动态的,通常不需要刷新 if (window.location.pathname === '/') applyHomepageOptimizations(); // 尝试动态应用 break; case 'swiperRemovalToggle': isSwiperRemovalEnabled = e.target.checked; GM_setValue('bilibiliSwiperRemovalEnabled', isSwiperRemovalEnabled); needsReload = true; // 移除轮播图最好刷新 if (window.location.pathname === '/') applyHomepageOptimizations(); // 尝试动态应用 break; case 'nonVideoCardRemovalToggle': isNonVideoCardRemovalEnabled = e.target.checked; GM_setValue('bilibiliNonVideoCardRemovalEnabled', isNonVideoCardRemovalEnabled); // 非视频卡片移除是动态的,通常不需要刷新 if (window.location.pathname === '/') applyHomepageOptimizations(); // 尝试动态应用 break; case 'copySuffixRemovalToggle': isCopySuffixRemovalEnabled = e.target.checked; GM_setValue('bilibiliCopySuffixRemovalEnabled', isCopySuffixRemovalEnabled); // 复制后缀是事件监听,需要刷新才能完全应用或取消 needsReload = true; if (window.location.pathname.startsWith('/read/')) applyArticlePageOptimizations(); // 动态应用 break; case 'trackingLoggingToggle': isTrackingLoggingDisabled = e.target.checked; GM_setValue('bilibiliTrackingLoggingDisabled', isTrackingLoggingDisabled); disableTrackingAndLogging(); // 立即尝试应用 needsReload = true; // 禁用追踪涉及底层修改,强烈建议刷新 break; case 'homePageOptimizationToggle': isHomePageOptimizationEnabled = e.target.checked; GM_setValue('bilibiliHomePageOptimizationEnabled', isHomePageOptimizationEnabled); // 主页优化功能保持为空,仅保存状态 // 根据当前页面应用相应的优化 if (window.location.pathname === '/') { applyHomepageOptimizations(); // 主页优化 } else if (window.location.hostname === 't.bilibili.com') { applyStoryPageOptimizations(); // 动态页优化 } // 移除黑白滤镜在所有页面都应用 removeBlackWhiteFilter(); break; // 新增:处理新选项 case 'liveEnhancementToggle': isLiveEnhancementEnabled = e.target.checked; GM_setValue('bilibiliLiveEnhancementEnabled', isLiveEnhancementEnabled); // 直播增强可能需要刷新 applyLiveEnhancements(isLiveEnhancementEnabled); // 应用直播设置,并触发网络拦截更新 needsReload = true; break; case 'p2pCdnToggle': isP2pCdnDisabled = e.target.checked; GM_setValue('bilibiliP2pCdnDisabled', isP2pCdnDisabled); disableP2pCdnAndWebRTC(isP2pCdnDisabled); // 应用 P2P 设置,并触发网络拦截更新 // 禁用 P2P 涉及网络请求拦截,强烈建议刷新 needsReload = true; break; case 'av1DisableToggle': // 新增:处理禁用AV1 isAv1Disabled = e.target.checked; GM_setValue('bilibiliAv1Disabled', isAv1Disabled); applyAv1Disable(isAv1Disabled); // 应用 AV1 禁用设置 needsReload = true; // 通常编码切换需要刷新 break; case 'force4kToggle': // 新增:处理强制4K isForce4kEnabled = e.target.checked; GM_setValue('bilibiliForce4kEnabled', isForce4kEnabled); applyForce4k(isForce4kEnabled); // 应用强制4K设置 needsReload = true; // 通常分辨率/UA切换需要刷新 break; } if (needsReload) { console.log(`Tampermonkey Script "优化Bilibili": 设置项 "${e.target.id}" 已更改,建议刷新页面以完全生效。`); // 可以选择性地弹出提示 // GM.notification(`设置项 "${e.target.parentElement.textContent.trim()}" 已更改,建议刷新页面生效。`, 'Bilibili 优化提示'); } } }); } // --- 页面特定逻辑 --- // 主页功能 function applyHomepageOptimizations() { console.log('Tampermonkey Script "优化Bilibili": 应用主页优化逻辑'); // 添加CSS样式以立即隐藏非视频卡片和直播卡片 if (isNonVideoCardRemovalEnabled) { let styleElement = document.getElementById('non-video-card-removal-style'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = 'non-video-card-removal-style'; styleElement.textContent = ` /* 隐藏非视频卡片 */ .floor-single-card, .bili-topic-card, .video-page-special-card-small, .bili-live-card, .ad-floor-card, .activity-card, /* 直播卡片特定选择器 */ .floor-card.single-card[data-v-21f11628]:has(a[href*="live.bilibili.com"]), .floor-card.single-card:has(.badge svg[id*="live"]), .floor-card.single-card:has(.living), .floor-card.single-card:has([href*="live.bilibili.com"]), div[class*="liveCard"] { display: none !important; } `; document.head.appendChild(styleElement); console.log('Tampermonkey Script "优化Bilibili": 添加了非视频卡片和直播卡片移除样式'); } } // 应用主页优化功能(来自b3.js中的optimizeHomepage) if (isHomePageOptimizationEnabled) { let styleElement = document.getElementById('homepage-optimization-style'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = 'homepage-optimization-style'; styleElement.textContent = ` /* 首页广告去除和样式优化 */ .feed2 .feed-card:has(a[href*="cm.bilibili.com"]), .feed2 .feed-card:has(.bili-video-card:empty) { width: 1px !important; height: 1px !important; opacity: 0 !important; pointer-events: none !important; position: absolute !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border-width: 0 !important; } .feed2 .container > * { margin-top: 0 !important } `; document.head.appendChild(styleElement); console.log('Tampermonkey Script "优化Bilibili": 添加了首页优化样式'); } } function removeAds() { if (!isAdRemovalEnabled) return; // 删除 adcard-content 下的 btn-ad const adcardContents = document.querySelectorAll('.adcard-content'); adcardContents.forEach(adcard => { const btnAds = adcard.querySelectorAll('.btn-ad'); btnAds.forEach(btn => btn.remove()); }); // 处理被feed-card包裹的情况 const feedCards = document.querySelectorAll('.feed-card'); feedCards.forEach(card => { const isAd = card.querySelector('.bili-video-card.is-rcmd:not(.enable-no-interest)') || card.querySelector('.ad-report') || // 通用广告标记 card.querySelector('a[href*="cm.bilibili.com"]'); // 活动广告链接 if (isAd) { // console.log('Tampermonkey Script "优化Bilibili": 移除主页广告 Feed Card', card); card.remove(); } }); // 处理直接的bili-video-card (rcmd 但非无兴趣) const directVideoCards = document.querySelectorAll('.bili-video-card.is-rcmd:not(.enable-no-interest):not([data-ad-id])'); // 进一步排除 data-ad-id? directVideoCards.forEach(card => { // 确认父元素不是已经被移除的 feed-card if (card.parentElement && !card.parentElement.classList.contains('feed-card')) { // console.log('Tampermonkey Script "优化Bilibili": 移除主页广告 Direct Card', card); card.remove(); } }); // 删除推广按钮的文字 const flexibleRollBtns = document.querySelectorAll('.flexible-roll-btn'); flexibleRollBtns.forEach(btn => { const spans = btn.querySelectorAll('span'); spans.forEach(span => { if (span.textContent.includes('广告') || span.textContent.includes('推荐')) { // 更精确判断 // btn.remove(); // 或直接移除按钮 span.remove(); } }); }); // 删除特定推广按钮 const feedRollBtns = document.querySelectorAll('.feed-roll-btn'); feedRollBtns.forEach(btn => btn.remove()); // 移除顶部横幅广告 (可能选择器需要更新) const bannerAd = document.querySelector('.bili-banner .ad-report'); if (bannerAd && bannerAd.closest('.bili-banner')) { bannerAd.closest('.bili-banner').remove(); } } function removeSwiper() { if (!isSwiperRemovalEnabled) return; // 删除轮播图 const swiper = document.querySelector('.recommended-swipe.grid-anchor, .bili-banner.carousel-active'); // 尝试匹配两种轮播图选择器 if (swiper) { // console.log('Tampermonkey Script "优化Bilibili": 移除轮播图', swiper); swiper.remove(); } // 删除特定的CSS规则 (确保只在需要时添加) let styleElement = document.getElementById('remove-swiper-style'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = 'remove-swiper-style'; styleElement.textContent = ` /* 移除轮播图后调整布局,防止空白 (使用更通用的选择器) */ .feed2 .container > * { margin-top: 0 !important; } /* 以下旧规则可能已不再需要或需要调整 */ /* .recommended-container_floor-aside .container > *:nth-child(1) { margin-top: 0 !important; } */ /* .recommended-container_floor-aside .container>*:nth-of-type(n + 8) { margin-top: 0 !important; } */ /* .recommended-container_floor-aside .container.is-version8>*:nth-of-type(n + 13) { margin-top: 0 !important; } */ `; document.head.appendChild(styleElement); } } function removeNonVideoCards() { if (!isNonVideoCardRemovalEnabled) return; // 删除特定非视频卡片容器 const nonVideoCardSelectors = [ '.floor-single-card', // 单个卡片推广 '.bili-topic-card', // 话题卡片 '.video-page-special-card-small', // 小型特殊卡片 '.bili-live-card', // 直播卡片 '.ad-floor-card', // 明确的广告楼层 '.activity-card', // 活动卡片 // 添加其他非视频卡片的选择器 ]; nonVideoCardSelectors.forEach(selector => { const cards = document.querySelectorAll(selector); cards.forEach(card => { // console.log('Tampermonkey Script "优化Bilibili": 移除非视频卡片', selector, card); card.remove(); }); }); // 特别处理直播卡片 const liveCardSelectors = [ '.floor-card.single-card', // 找到所有单卡片 'div[class*="liveCard"]', // 任何包含liveCard的类名 ]; liveCardSelectors.forEach(selector => { const cards = document.querySelectorAll(selector); cards.forEach(card => { // 检查是否是直播卡片 const isLiveCard = card.querySelector('a[href*="live.bilibili.com"]') || // 链接包含直播域名 card.querySelector('.badge svg[id*="live"]') || // 有直播图标 card.querySelector('.living') || // 有"直播中"标记 card.textContent.includes('直播中'); // 文本包含"直播中" if (isLiveCard) { console.log('Tampermonkey Script "优化Bilibili": 移除主页直播卡片', card); card.remove(); } }); }); // 针对性处理用户提到的特定直播卡片结构 const specificLiveCards = document.querySelectorAll('div[data-v-21f11628].floor-card.single-card'); specificLiveCards.forEach(card => { if (card.innerHTML.includes('live.bilibili.com') || card.querySelector('a[href*="live.bilibili.com"]') || card.querySelector('svg[id="channel-icon-live"]')) { console.log('Tampermonkey Script "优化Bilibili": 移除特定结构直播卡片', card); card.remove(); } }); } // 初始执行 (如果设置启用) if (isAdRemovalEnabled) removeAds(); if (isSwiperRemovalEnabled) removeSwiper(); if (isNonVideoCardRemovalEnabled) removeNonVideoCards(); // 创建一个MutationObserver来监视DOM变化 const observer = new MutationObserver((mutations) => { let changed = false; mutations.forEach((mutation) => { // 优化:只在添加了节点时检查 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { // 更精确地检查添加的节点是否可能是广告或需要移除的卡片 for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.querySelector('.ad-report, a[href*="cm.bilibili.com"]') || // 广告相关 node.matches('.feed-card, .floor-single-card, .bili-live-card, .bili-topic-card, .video-page-special-card-small, .ad-floor-card, .activity-card') || // 卡片类型 node.classList?.contains('recommended-swipe') || node.classList?.contains('bili-banner') || // 轮播图 // 检查是否是直播卡片 node.querySelector('a[href*="live.bilibili.com"]') || node.querySelector('svg[id="channel-icon-live"]') || node.textContent.includes('直播中') || (node.hasAttribute('data-v-21f11628') && node.classList.contains('floor-card')) ) { changed = true; break; } } } } }); if (changed) { // console.log('Tampermonkey Script "优化Bilibili": 检测到主页 DOM 变化,重新应用优化'); if (isAdRemovalEnabled) removeAds(); if (isSwiperRemovalEnabled) removeSwiper(); // Swiper 可能需要重新移除 if (isNonVideoCardRemovalEnabled) removeNonVideoCards(); } }); // 配置观察选项 const config = { childList: true, subtree: true }; // 开始观察文档体 // 延迟启动 Observer,等待页面初步加载 setTimeout(() => { if (document.body) { observer.observe(document.body, config); console.log('Tampermonkey Script "优化Bilibili": 主页 MutationObserver 已启动'); } else { console.warn('Tampermonkey Script "优化Bilibili": document.body 不存在,无法启动主页 Observer'); } }, 1000); // 延迟 1 秒 } // 视频页面功能 function applyVideoPageOptimizations() { console.log('Tampermonkey Script "优化Bilibili": 应用视频页优化逻辑'); if (!isAdRemovalEnabled) return; // 创建并添加样式 const styleId = 'bilibili-video-ad-removal-styles'; if (!document.getElementById(styleId)) { const style = document.createElement('style'); style.id = styleId; style.textContent = ` /* 隐藏视频播放前广告和弹窗 */ .bilibili-player-video-ad, .bilibili-player-video-popup, .bilibili-player-video-upgrade-popup, .bilibili-player-community-intro { display: none !important; } /* 隐藏右侧推广广告 */ .ad-report, .ad-floor-cover, .slide-ad, .video-card-ad, .video-page-special-card-small, .video-page-game-card-small, .video-container .pop-live-small-mode { display: none !important; } /* 隐藏播放器内推广内容 */ .player-wrapper .video-player-recommend-ad { display: none !important; } /* 隐藏相关视频广告 */ .up-card-wrap:has(.ad-info), .card-box:has(.ad-info), .recommend-video-card:has(.info-box .ad-tag) { display: none !important; } /* 隐藏播放器底部推广 */ #biliMainFooter, #biliMainFooter .contact-help { display: none !important; } /* 隐藏右侧卡片广告 */ .right-container .vcd { display: none !important; } `; document.head.appendChild(style); } // 使用DOM移除无法通过CSS隐藏的广告 function removeVideoAds() { // 移除播放器预载广告 const playerAds = document.querySelectorAll('.bilibili-player-video-ad'); playerAds.forEach(ad => ad.remove()); // 移除相关视频中的广告 const relatedAds = document.querySelectorAll('.video-page-card-small .ad-tag'); relatedAds.forEach(ad => { const card = ad.closest('.video-page-card-small'); if (card) card.remove(); }); // 移除右侧广告卡片 const adCards = document.querySelectorAll('.ad-report, .slide-ad, .video-card-ad'); adCards.forEach(card => { const container = card.closest('.b-wrap, .card-list-item, .video-page-card-small'); if (container) container.remove(); }); // 移除视频下方推广 const bottomAds = document.querySelectorAll('.activity-m, .up-card:has(.ad-info)'); bottomAds.forEach(ad => ad.remove()); } // 初始执行一次 removeVideoAds(); // 创建MutationObserver来监视DOM变化 const observer = new MutationObserver((mutations) => { let changed = false; mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.querySelector('.ad-report, .ad-tag, .bilibili-player-video-ad') || node.matches('.video-card-ad, .slide-ad, .ad-report, .activity-m, .up-card')) { changed = true; break; } } } } }); if (changed) { // console.log('Tampermonkey Script "优化Bilibili": 检测到视频页 DOM 变化,重新应用广告移除'); removeVideoAds(); } }); // 启动观察器 setTimeout(() => { if (document.body) { observer.observe(document.body, { childList: true, subtree: true }); console.log('Tampermonkey Script "优化Bilibili": 视频页 MutationObserver 已启动'); } else { console.warn('Tampermonkey Script "优化Bilibili": document.body 不存在,无法启动视频页 Observer'); } }, 1000); // 新增: 特定于视频页面的CDN替换逻辑 if (isP2pCdnDisabled) { // 监听播放器初始化 function watchForPlayer() { // 检查播放器是否存在并替换视频源 const checkAndReplaceVideo = () => { const videoElements = document.querySelectorAll('video'); videoElements.forEach(video => { if (video.src && video.src.includes('.mcdn.bilivideo')) { const originalSrc = video.src; const newSrc = forceMcdnReplace(originalSrc); if (newSrc !== originalSrc) { console.log(`Tampermonkey Script "优化Bilibili" [视频页]: 替换视频src: ${originalSrc} -> ${newSrc}`); video.src = newSrc; } } }); }; // 立即检查一次 checkAndReplaceVideo(); // 每200ms检查一次,持续5秒 let attempts = 0; const interval = setInterval(() => { checkAndReplaceVideo(); attempts++; if (attempts >= 25) { // 5秒后停止 clearInterval(interval); } }, 200); } // 视频页面加载后检查 if (document.readyState === 'complete') { watchForPlayer(); } else { window.addEventListener('load', watchForPlayer); } // 监听历史状态变化 (SPA导航) const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function() { const result = originalPushState.apply(this, arguments); setTimeout(watchForPlayer, 100); // 略微延迟执行 return result; }; history.replaceState = function() { const result = originalReplaceState.apply(this, arguments); setTimeout(watchForPlayer, 100); return result; }; // 监听URL变化 window.addEventListener('popstate', () => { setTimeout(watchForPlayer, 100); }); } } // 文章页面功能 function applyArticlePageOptimizations() { console.log('Tampermonkey Script "优化Bilibili": 应用文章页优化逻辑'); let copyListener = null; // 用于存储事件监听器引用 // 移除或添加文本复制后缀的监听器 function toggleCopySuffixListener() { // 先移除旧的监听器(如果存在) if (copyListener) { window.removeEventListener('copy', copyListener, true); copyListener = null; console.log('Tampermonkey Script "优化Bilibili": 移除旧的 copy 监听器'); } if (isCopySuffixRemovalEnabled) { copyListener = function(e) { e.stopImmediatePropagation(); // 阻止同一元素上其他监听器以及事件传播 console.log('Tampermonkey Script "优化Bilibili": 阻止复制事件传播 (immediate)'); }; window.addEventListener('copy', copyListener, true); // 在捕获阶段阻止 console.log('Tampermonkey Script "优化Bilibili": 添加 copy 监听器 (阻止后缀)'); } else { console.log('Tampermonkey Script "优化Bilibili": 未添加 copy 监听器 (允许后缀)'); } } // 初始应用一次 // 需要等待 window load 吗?DOMContentLoaded 应该足够添加事件监听器 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', toggleCopySuffixListener); } else { toggleCopySuffixListener(); } // 监听设置变化并重新应用 removeCopySuffix // 通过 change 事件处理,无需 setInterval const settingsContainer = document.getElementById('settingsToggleContainer'); if (settingsContainer) { const toggleInput = settingsContainer.querySelector('#copySuffixRemovalToggle'); if (toggleInput) { toggleInput.addEventListener('change', (e) => { isCopySuffixRemovalEnabled = e.target.checked; // 确保状态同步 toggleCopySuffixListener(); // 重新应用监听器 }); } } } // --- 初始化 UI 和页面特定逻辑 --- initSettingsUI(); // 尝试初始化设置按钮 // 根据当前页面 URL 应用特定逻辑 // 使用更通用的匹配方式 const hostname = window.location.hostname; const pathname = window.location.pathname; if (hostname.endsWith('bilibili.com')) { // 在所有页面上都应用黑白滤镜移除 removeBlackWhiteFilter(); if (pathname === '/' && hostname === 'www.bilibili.com') { // 仅限主域名首页 // 等待 DOM 加载完毕再应用主页优化 if (document.readyState === 'loading' || document.readyState === 'interactive') { document.addEventListener('DOMContentLoaded', applyHomepageOptimizations); } else { applyHomepageOptimizations(); } } else if (pathname.startsWith('/read/')) { // 文章页优化可以在脚本加载后立即应用 applyArticlePageOptimizations(); } else if (hostname === 'live.bilibili.com') { // 直播页面的特定逻辑已在 applyLiveEnhancements 中处理样式 // applyLiveEnhancements 会在脚本开始时根据初始状态调用 console.log('Tampermonkey Script "优化Bilibili": 检测到直播页面'); } else if (pathname.startsWith('/video/')) { // 视频页面优化 if (document.readyState === 'loading' || document.readyState === 'interactive') { document.addEventListener('DOMContentLoaded', applyVideoPageOptimizations); } else { applyVideoPageOptimizations(); } console.log('Tampermonkey Script "优化Bilibili": 检测到视频页面'); } else if (hostname === 't.bilibili.com') { // 动态页面优化 if (document.readyState === 'loading' || document.readyState === 'interactive') { document.addEventListener('DOMContentLoaded', applyStoryPageOptimizations); } else { applyStoryPageOptimizations(); } console.log('Tampermonkey Script "优化Bilibili": 检测到动态页面'); } // 其他页面,如视频页、直播页、动态页等,只应用全局功能(如禁用追踪、设置按钮) } // 添加动态页面优化功能(来自b3.js中的optimizeStory) function applyStoryPageOptimizations() { console.log('Tampermonkey Script "优化Bilibili": 应用动态页面优化逻辑'); if (!isHomePageOptimizationEnabled) return; // 添加动态页宽屏模式样式 let styleElement = document.getElementById('story-optimization-style'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = 'story-optimization-style'; styleElement.textContent = ` /* 动态页面优化 */ html[wide] #app { display: flex; } html[wide] .bili-dyn-home--member { box-sizing: border-box; padding: 0 10px; width: 100%; flex: 1; } html[wide] .bili-dyn-content { width: initial; } html[wide] main { margin: 0 8px; flex: 1; overflow: hidden; width: initial; } #wide-mode-switch { margin-left: 0; margin-right: 20px; } .bili-dyn-list__item:has(.bili-dyn-card-goods), .bili-dyn-list__item:has(.bili-rich-text-module.goods) { display: none !important } `; document.head.appendChild(styleElement); console.log('Tampermonkey Script "优化Bilibili": 添加了动态页面优化样式'); } // 设置宽屏模式 if (!localStorage.WIDE_OPT_OUT) { document.documentElement.setAttribute('wide', 'wide'); } // 添加宽屏模式切换按钮 function addWideModeSwitchButton() { const tabContainer = document.querySelector('.bili-dyn-list-tabs__list'); if (!tabContainer || document.getElementById('wide-mode-switch')) return; const placeHolder = document.createElement('div'); placeHolder.style.flex = '1'; const switchButton = document.createElement('a'); switchButton.id = 'wide-mode-switch'; switchButton.className = 'bili-dyn-list-tabs__item'; switchButton.textContent = '宽屏模式'; switchButton.addEventListener('click', (e) => { e.preventDefault(); if (localStorage.WIDE_OPT_OUT) { localStorage.removeItem('WIDE_OPT_OUT'); document.documentElement.setAttribute('wide', 'wide'); } else { localStorage.setItem('WIDE_OPT_OUT', '1'); document.documentElement.removeAttribute('wide'); } }); tabContainer.appendChild(placeHolder); tabContainer.appendChild(switchButton); console.log('Tampermonkey Script "优化Bilibili": 添加了宽屏模式切换按钮'); } // 等待页面加载完成后添加按钮 if (document.readyState === 'complete') { addWideModeSwitchButton(); } else { window.addEventListener('load', addWideModeSwitchButton, { once: true }); } } // 添加移除黑白滤镜功能(来自b3.js中的removeBlackBackdropFilter) function removeBlackWhiteFilter() { if (!isHomePageOptimizationEnabled) return; console.log('Tampermonkey Script "优化Bilibili": 应用移除黑白滤镜'); let styleElement = document.getElementById('remove-black-white-filter-style'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = 'remove-black-white-filter-style'; styleElement.textContent = ` /* 去除叔叔去世时的全站黑白效果 */ html, body { -webkit-filter: none !important; filter: none !important; } `; document.head.appendChild(styleElement); console.log('Tampermonkey Script "优化Bilibili": 添加了移除黑白滤镜样式'); } } // --- AV1 禁用核心逻辑 --- function applyAv1Disable(disable) { console.log('Tampermonkey Script "优化Bilibili": 应用 AV1 禁用设置:', disable); // 调试信息:应用 AV1 禁用设置 if (disable) { if (originalHTMLMediaElement && originalCanPlayType) { originalHTMLMediaElement.prototype.canPlayType = function(type) { // 确保 type 是字符串再调用 includes if (type && typeof type === 'string' && type.includes('av01')) { console.log('Tampermonkey Script "优化Bilibili" [AV1 Disable]: HTMLMediaElement.canPlayType 已为类型拦截:', type); // 调试信息:HTMLMediaElement.canPlayType 拦截 return ''; // 返回空字符串表示不支持 } // 对于其他类型,调用原始方法 return originalCanPlayType.apply(this, arguments); }; } else { console.warn('Tampermonkey Script "优化Bilibili" [AV1 Disable]: 原始 HTMLMediaElement.prototype.canPlayType 未找到或不可用。'); // 警告:原始 canPlayType 不可用 } if (originalMediaSource && originalIsTypeSupported) { originalMediaSource.isTypeSupported = function(type) { // 确保 type 是字符串再调用 includes if (type && typeof type === 'string' && type.includes('av01')) { console.log('Tampermonkey Script "优化Bilibili" [AV1 Disable]: MediaSource.isTypeSupported 已为类型拦截:', type); // 调试信息:MediaSource.isTypeSupported 拦截 return false; // 返回 false 表示不支持 } // 对于其他类型,调用原始方法 return originalIsTypeSupported.apply(this, arguments); }; } else { console.warn('Tampermonkey Script "优化Bilibili" [AV1 Disable]: 原始 MediaSource.isTypeSupported 未找到或不可用。'); // 警告:原始 isTypeSupported 不可用 } if ((originalHTMLMediaElement && originalCanPlayType) || (originalMediaSource && originalIsTypeSupported)) { console.log('Tampermonkey Script "优化Bilibili": AV1 功能已尝试禁用 (通过拦截 canPlayType 和/或 isTypeSupported)。'); // 信息:AV1 已尝试禁用 } else { console.warn('Tampermonkey Script "优化Bilibili": 未能应用 AV1 禁用,相关 API 缺失。'); // 警告:未能应用 AV1 禁用 } } else { // 恢复原始函数 if (originalHTMLMediaElement && originalCanPlayType) { originalHTMLMediaElement.prototype.canPlayType = originalCanPlayType; } if (originalMediaSource && originalIsTypeSupported) { originalMediaSource.isTypeSupported = originalIsTypeSupported; } console.log('Tampermonkey Script "优化Bilibili": AV1 功能已恢复/启用 (如果之前被修改)。'); // 信息:AV1 已恢复/启用 } } // --- 强制启用 4K 核心逻辑 --- function applyForce4k(enable) { console.log('Tampermonkey 脚本 "优化Bilibili": 应用强制4K设置:', enable); // 日志:应用强制4K设置 if (enable) { // 1. 设置 localStorage 项 if (originalLsSetItem && unsafeWindow.localStorage) { originalLsSetItem.call(unsafeWindow.localStorage, 'bilibili_player_force_DolbyAtmos&8K&HDR', '1'); originalLsSetItem.call(unsafeWindow.localStorage, 'bilibili_player_force_hdr', '1'); console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: localStorage 标志已设置。'); } else { console.warn('Tampermonkey 脚本 "优化Bilibili" [强制4K]: localStorage API 或 setItem 不可用,无法设置标志。'); } // 2. Hook sessionStorage.getItem if (unsafeWindow.sessionStorage && originalSessionStorageGetItem) { unsafeWindow.sessionStorage.getItem = function(key) { if (key === 'enableHEVCError') { console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: sessionStorage.getItem(\'enableHEVCError\') 已拦截。'); return null; } return originalSessionStorageGetItem.apply(this, arguments); }; console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: sessionStorage.getItem 已覆写。'); } else { console.warn('Tampermonkey 脚本 "优化Bilibili" [强制4K]: sessionStorage API 或 getItem 不可用,无法拦截。'); } // 3. 覆盖 navigator.userAgent // My.js 中的 defineReadonlyProperty 将 configurable 设置为 false // 这意味着需要刷新才能真正恢复 UA。 if (unsafeWindow.navigator) { defineReadonlyProperty(unsafeWindow.navigator, 'userAgent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15'); console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: navigator.userAgent 已通过 defineReadonlyProperty 修改。'); } else { console.warn('Tampermonkey 脚本 "优化Bilibili" [强制4K]: unsafeWindow.navigator 不可用,无法修改 userAgent。'); } console.log('Tampermonkey 脚本 "优化Bilibili": 强制4K功能已启用 (User Agent 的修改需要刷新页面才能在所有网络请求中完全生效)。'); } else { // 恢复功能 // 1. 移除 localStorage 项 if (originalLsRemoveItem && unsafeWindow.localStorage) { originalLsRemoveItem.call(unsafeWindow.localStorage, 'bilibili_player_force_DolbyAtmos&8K&HDR'); originalLsRemoveItem.call(unsafeWindow.localStorage, 'bilibili_player_force_hdr'); console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: localStorage 标志已移除。'); } // 2. 恢复 sessionStorage.getItem if (unsafeWindow.sessionStorage && originalSessionStorageGetItem) { unsafeWindow.sessionStorage.getItem = originalSessionStorageGetItem; console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: sessionStorage.getItem 已恢复。'); } // 3. 恢复 navigator.userAgent // 由于 defineReadonlyProperty (configurable:false) 的使用,无法直接恢复。 // 用户需要刷新页面。'needsReload = true' 会提示用户。 if (unsafeWindow.navigator) { console.log('Tampermonkey 脚本 "优化Bilibili" [强制4K]: navigator.userAgent 的恢复需要刷新页面。'); } console.log('Tampermonkey 脚本 "优化Bilibili": 强制4K功能已禁用 (建议刷新页面以完全恢复 User Agent)。'); } } // 5. 添加额外的拦截层:拦截视频元素创建 (在脚本末尾初始化区域附近,大约在1860行左右) // 在 disableTrackingAndLogging(); 等初始化代码后添加 // 额外添加:拦截视频元素创建 function interceptVideoElements() { if (!isP2pCdnDisabled) return; // 使用 MutationObserver 监听视频元素 const videoObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { if (node.nodeName === 'VIDEO' || node.nodeName === 'AUDIO') { // 监听src属性变化 node.addEventListener('loadstart', function() { if (this.src && this.src.includes('.mcdn.bilivideo')) { const originalSrc = this.src; const newSrc = forceMcdnReplace(originalSrc); if (newSrc !== originalSrc) { console.log(`Tampermonkey Script "优化Bilibili" [DOM监听]: 替换视频src: ${originalSrc} -> ${newSrc}`); this.src = newSrc; } } }, true); // 立即检查当前src if (node.src && node.src.includes('.mcdn.bilivideo')) { const originalSrc = node.src; const newSrc = forceMcdnReplace(originalSrc); if (newSrc !== originalSrc) { console.log(`Tampermonkey Script "优化Bilibili" [DOM监听]: 替换视频src: ${originalSrc} -> ${newSrc}`); node.src = newSrc; } } } } } } }); // 开始观察 videoObserver.observe(document, { childList: true, subtree: true }); console.log('Tampermonkey Script "优化Bilibili": 视频元素监听器已启动'); // 额外处理已存在的视频元素 document.querySelectorAll('video, audio').forEach(el => { if (el.src && el.src.includes('.mcdn.bilivideo')) { const originalSrc = el.src; const newSrc = forceMcdnReplace(originalSrc); if (newSrc !== originalSrc) { console.log(`Tampermonkey Script "优化Bilibili" [DOM监听]: 替换已存在视频src: ${originalSrc} -> ${newSrc}`); el.src = newSrc; } } }); } // 在初始化区域调用 interceptVideoElements(); // 确保默认CDN缓存不为空 if (intelligentCdnCache.length === 0) { intelligentCdnCache = [DEFAULT_MIRROR_CDN]; console.log('Tampermonkey Script "优化Bilibili": 初始化默认CDN缓存:', intelligentCdnCache); } // 在脚本开始,添加顶部即时执行的函数,确保拦截器最早生效 (function earlyInit() { // 立即修补XMLHttpRequest,优先捕获所有网络请求 if (unsafeWindow.XMLHttpRequest) { const origXhrOpen = unsafeWindow.XMLHttpRequest.prototype.open; unsafeWindow.XMLHttpRequest.prototype.open = function(...args) { let url = args[1]; if (typeof url === 'string' && url.includes('.mcdn.bilivideo')) { // 立即替换mcdn URL const cdnHost = 'upos-sz-mirrorcos.bilivideo.com'; // 默认备选CDN args[1] = url.replace(/(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i, `$1${cdnHost}$4`); console.log(`[earlyInit] 替换XHR mcdn URL: ${url} -> ${args[1]}`); } return origXhrOpen.apply(this, args); }; } // 检查已有video元素并处理 function processVideoElements() { document.querySelectorAll('video, audio').forEach(el => { if (el.src && el.src.includes('.mcdn.bilivideo')) { const originalSrc = el.src; const cdnHost = 'upos-sz-mirrorcos.bilivideo.com'; const newSrc = originalSrc.replace(/(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i, `$1${cdnHost}$4`); if (newSrc !== originalSrc) { console.log(`[earlyInit] 替换现有视频元素src: ${originalSrc} -> ${newSrc}`); el.src = newSrc; } } // 监听src属性变化 const origSrcDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'src'); if (origSrcDescriptor && origSrcDescriptor.set) { Object.defineProperty(el, 'src', { set(value) { if (typeof value === 'string' && value.includes('.mcdn.bilivideo')) { const cdnHost = 'upos-sz-mirrorcos.bilivideo.com'; const newValue = value.replace(/(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i, `$1${cdnHost}$4`); console.log(`[earlyInit] 拦截设置视频src: ${value} -> ${newValue}`); origSrcDescriptor.set.call(this, newValue); } else { origSrcDescriptor.set.call(this, value); } }, get: origSrcDescriptor.get, configurable: true }); } }); } // 从b2.js借鉴的等待元素出现函数 const waitForElm = (selector) => new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.body || document.documentElement, { childList: true, subtree: true }); }); // 立即处理现有元素 if (document.readyState !== 'loading') { processVideoElements(); } else { document.addEventListener('DOMContentLoaded', processVideoElements); } // 监听video元素创建 const observer = new MutationObserver(mutations => { for (const mutation of mutations) { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { if (node.nodeName === 'VIDEO' || node.nodeName === 'AUDIO') { if (node.src && node.src.includes('.mcdn.bilivideo')) { const originalSrc = node.src; const cdnHost = 'upos-sz-mirrorcos.bilivideo.com'; const newSrc = originalSrc.replace(/(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i, `$1${cdnHost}$4`); if (newSrc !== originalSrc) { console.log(`[earlyInit] 替换新增视频src: ${originalSrc} -> ${newSrc}`); node.src = newSrc; } } } } } } }); // 从页面开始就监听整个DOM树 observer.observe(document.documentElement, { childList: true, subtree: true }); // 等待视频播放器加载并处理 waitForElm('video').then(videoEl => { console.log('[earlyInit] 找到视频元素,添加特殊处理'); // 直接修补视频元素 if (videoEl.src && videoEl.src.includes('.mcdn.bilivideo')) { const originalSrc = videoEl.src; const cdnHost = 'upos-sz-mirrorcos.bilivideo.com'; const newSrc = originalSrc.replace(/(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i, `$1${cdnHost}$4`); if (newSrc !== originalSrc) { console.log(`[earlyInit] 替换视频播放器src: ${originalSrc} -> ${newSrc}`); videoEl.src = newSrc; } } // 添加额外事件监听 videoEl.addEventListener('loadstart', () => { if (videoEl.src && videoEl.src.includes('.mcdn.bilivideo')) { const originalSrc = videoEl.src; const cdnHost = 'upos-sz-mirrorcos.bilivideo.com'; const newSrc = originalSrc.replace(/(https?:\/\/)([\w.-]+)(\.mcdn\.bilivideo\.[a-z]+)(.*)/i, `$1${cdnHost}$4`); if (newSrc !== originalSrc) { console.log(`[earlyInit] loadstart事件中替换src: ${originalSrc} -> ${newSrc}`); videoEl.src = newSrc; } } }); }); console.log('[earlyInit] 初始化完成,已建立全局拦截'); })(); // 在主要文件开头位置添加DOM元素替换逻辑 function injectVideoSrcOverrides() { // 为所有HTMLMediaElement添加src属性拦截 const origDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'src'); if (origDescriptor && origDescriptor.set) { Object.defineProperty(HTMLMediaElement.prototype, 'src', { set(value) { if (typeof value === 'string' && value.includes('.mcdn.bilivideo')) { const newValue = forceMcdnReplace(value); console.log(`[全局拦截] 视频src设置: ${value} -> ${newValue}`); return origDescriptor.set.call(this, newValue); } return origDescriptor.set.call(this, value); }, get: origDescriptor.get, configurable: true }); console.log('[全局拦截] HTMLMediaElement.src已重写'); } // 监听MediaSource创建 const origAddSourceBuffer = MediaSource.prototype.addSourceBuffer; MediaSource.prototype.addSourceBuffer = function(mimeType) { console.log(`[全局拦截] MediaSource.addSourceBuffer: ${mimeType}`); return origAddSourceBuffer.call(this, mimeType); }; } // 在适当位置调用 injectVideoSrcOverrides(); })();