// ==UserScript== // @name 【哔哩哔哩】屏蔽视频PCDN地址 // @namespace https://github.com/AkagiYui/UserScript // @supportURL https://github.com/AkagiYui/UserScript/issues // @homepage https://github.com/AkagiYui // @version 0.2.0 // @description 从官方CDN加载视频 // @author AkagiYui // @license MIT // @icon https://static.hdslb.com/images/favicon.ico // @match https://www.bilibili.com/video/* // @match https://www.bilibili.com/list/* // @match https://www.bilibili.com/bangumi/play/* // @grant unsafeWindow // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @run-at document-start // @downloadURL none // ==/UserScript== /******/ (() => { // webpackBootstrap /******/ "use strict"; var __webpack_exports__ = {}; const log = console.log.bind(console, "AkagiYui"); // 获取配置 function getConfig(key) { return GM_getValue(key, booleanConfigs[key].defaultValue); } // 配置注册 let menuIds = []; function registerMenuCommand() { menuIds.forEach((id) => { GM_unregisterMenuCommand(id); }); menuIds = []; Object.entries(booleanConfigs).forEach(([key, config]) => { let commandName = getConfig(key) ? "✅" : "❌"; commandName += ` ${config.title}`; let id = GM_registerMenuCommand(commandName, () => { let newValue = !getConfig(key); let valueToSet = config.callback ? config.callback(newValue) : newValue; GM_setValue(key, valueToSet); registerMenuCommand(); }); menuIds.push(id); }); } // 配置项 const booleanConfigs = { blockPlayError: { title: "屏蔽“播放遇到问题?”提示", defaultValue: false, }, }; registerMenuCommand(); const pageWindow = unsafeWindow; // 过滤掉pcdn的url const filterBackupUrls = (mediaArray) => { mediaArray.forEach((media) => { const filteredUrls = media.backupUrl.filter((url) => !url.includes("mcdn.bilivideo.cn")); if (!media.baseUrl.includes("mcdn.bilivideo.cn")) { filteredUrls.unshift(media.baseUrl); } if (filteredUrls.length > 0) { const baseUrl = filteredUrls[0]; const backupUrl = filteredUrls.slice(1); media.baseUrl = baseUrl; media.base_url = baseUrl; media.backupUrl = backupUrl; media.backup_url = backupUrl; } else { log("没有可用的非pcdn url"); } }); }; const replaceUrl = (data) => { let dash = data; log("处理前", JSON.parse(JSON.stringify(dash))); const video = dash.video; const audio = dash.audio; filterBackupUrls(video); filterBackupUrls(audio); log("处理后", JSON.parse(JSON.stringify(dash))); return dash; }; // 播放器初始化参数 let __playinfo__ = pageWindow.__playinfo__; Object.defineProperty(pageWindow, "__playinfo__", { get: () => __playinfo__, set: (value) => { log("播放器初始化参数", value); if (value.data) { log("非番剧"); value.data.dash = replaceUrl(value.data.dash); } if (value.result) { log("番剧"); value.result.video_info.dash = replaceUrl(value.result.video_info.dash); } __playinfo__ = value; }, }); // 播放列表请求处理 const originalXHR = pageWindow.XMLHttpRequest; const xhrOpen = originalXHR.prototype.open; originalXHR.prototype.open = function (_, url) { var _a, _b; const xhr = this; if (url.includes("api.bilibili.com/x/player/wbi/playurl")) { // 包括单个视频的多个(画质数量*编码数量)的url const avid = (_a = url.match(/avid=(\d+)/)) === null || _a === void 0 ? void 0 : _a[1]; // 提取出url中的avid参数 log("请求视频列表", `av${avid}`); const getter = Object.getOwnPropertyDescriptor(originalXHR.prototype, "responseText").get; Object.defineProperty(xhr, "responseText", { get: () => { let response = getter.call(xhr); let responseJson = JSON.parse(response); responseJson.data.dash = replaceUrl(responseJson.data.dash); let modifiedResponse = JSON.stringify(responseJson); return modifiedResponse; }, }); } if (url.includes("api.bilibili.com/pgc/player/web/v2/playurl")) { const season_id = (_b = url.match(/season_id=(\d+)/)) === null || _b === void 0 ? void 0 : _b[1]; // 提取出url中的season_id参数 const ep_id = url.match(/ep_id=(\d+)/); // 提取出url中的ep_id参数 log("请求番剧列表", `ss${season_id}`, ep_id ? `ep${ep_id[1]}` : "ep_id not found"); const getter = Object.getOwnPropertyDescriptor(originalXHR.prototype, "responseText").get; Object.defineProperty(xhr, "responseText", { get: () => { let response = getter.call(xhr); let responseJson = JSON.parse(response); responseJson.result.video_info.dash = replaceUrl(responseJson.result.video_info.dash); let modifiedResponse = JSON.stringify(responseJson); return modifiedResponse; }, }); } return xhrOpen.apply(xhr, arguments); }; // 屏蔽“播放遇到问题?”提示 if (getConfig("blockPlayError")) { const originalDefineProperty = pageWindow.Object.defineProperty; pageWindow.Object.defineProperty = function (target, propertyKey, descriptor) { if (propertyKey === "videoHasBuffered") { originalDefineProperty(target, "showLoadTimeoutFeedback", { get: () => () => { }, set: () => { pageWindow.Object.defineProperty = originalDefineProperty; }, }); } return originalDefineProperty(target, propertyKey, descriptor); }; } /******/ })() ;