Greasy Fork

Greasy Fork is available in English.

【哔哩哔哩】屏蔽视频PCDN地址

从官方CDN加载视频

目前为 2024-08-11 提交的版本。查看 最新版本

// ==UserScript==
// @name         【哔哩哔哩】屏蔽视频PCDN地址
// @namespace    https://github.com/AkagiYui/UserScript
// @supportURL   https://github.com/AkagiYui/UserScript/issues
// @homepage     https://github.com/AkagiYui
// @version      0.2.1
// @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
// ==/UserScript==

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ 636:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.useBooleanMenu = exports.useLogger = void 0;
const useLogger = (name) => {
    if (name) {
        return console.log.bind(console, "AkagiYui", `[${name}]`);
    }
    return console.log.bind(console, "AkagiYui");
};
exports.useLogger = useLogger;
const useBooleanMenu = (configs) => {
    // 获取配置
    const getConfig = (key) => {
        return GM_getValue(key, configs[key].defaultValue);
    };
    // 配置注册
    let menuIds = [];
    const registerMenuCommand = () => {
        menuIds.forEach((id) => {
            GM_unregisterMenuCommand(id);
        });
        menuIds = [];
        Object.entries(configs).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);
        });
    };
    registerMenuCommand();
    return { getConfig };
};
exports.useBooleanMenu = useBooleanMenu;


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
var exports = __webpack_exports__;
var __webpack_unused_export__;

__webpack_unused_export__ = ({ value: true });
const utils_1 = __webpack_require__(636);
const log = (0, utils_1.useLogger)("bilibili-ban-pcdn");
const { getConfig } = (0, utils_1.useBooleanMenu)({
    blockPlayError: {
        title: "屏蔽“播放遇到问题?”提示",
        defaultValue: false,
    },
});
// 真实页面
const pageWindow = unsafeWindow;
// 挑出有用的链接
const removePcdnUrl = (allUrls) => {
    const urls = allUrls.filter((url) => !url.includes("mcdn.bilivideo.cn"));
    const baseUrl = urls[0];
    const backupUrls = urls.slice(1);
    return { baseUrl, backupUrls };
};
// 处理资源数据
const cleanPlayInfo = (playInfo) => {
    log("处理前", JSON.parse(JSON.stringify(playInfo)));
    if (playInfo.data) {
        log("非番剧视频");
        cleanNonBangumiVideo(playInfo.data);
    }
    else if (playInfo.result) {
        log("番剧视频");
        cleanBangumiVideo(playInfo.result);
    }
    log("处理后", JSON.parse(JSON.stringify(playInfo)));
};
const cleanNonBangumiVideo = (data) => {
    if (data.dash) {
        cleanDash(data.dash);
    }
    if (data.durl) {
        log("试看视频");
        cleanDurl(data.durl);
    }
};
const cleanBangumiVideo = (result) => {
    if (!result.video_info) {
        log("番剧播放列表不存在,可能是没有大会员或未承包");
        return;
    }
    const videoInfo = result.video_info;
    if (videoInfo.dash) {
        cleanDash(videoInfo.dash);
    }
    else if (videoInfo.durl || videoInfo.durls) {
        log("试看番剧");
        if (videoInfo.durl) {
            cleanDurl(videoInfo.durl);
        }
        if (videoInfo.durls) {
            videoInfo.durls.forEach((durlGroup) => cleanDurl(durlGroup.durl));
        }
    }
    else {
        log("番剧播放列表不存在,可能是没有大会员或未承包");
    }
};
const cleanDash = (dash) => {
    const cleanMedia = (media) => {
        const { baseUrl, backupUrls } = removePcdnUrl([media.baseUrl, ...media.backupUrl]);
        media.baseUrl = media.base_url = baseUrl;
        media.backupUrl = media.backup_url = backupUrls;
    };
    dash.video.forEach(cleanMedia);
    dash.audio.forEach(cleanMedia);
};
const cleanDurl = (durls) => {
    durls.forEach((durl) => {
        const { baseUrl, backupUrls } = removePcdnUrl([durl.url, ...durl.backup_url]);
        durl.url = baseUrl;
        durl.backup_url = backupUrls;
    });
};
// 播放器初始化参数
let __playinfo__ = pageWindow.__playinfo__;
Object.defineProperty(pageWindow, "__playinfo__", {
    get: () => __playinfo__,
    set: (value) => {
        log("初始化参数", value);
        cleanPlayInfo(value);
        __playinfo__ = value;
    },
});
// 播放列表请求处理
const originalXHR = pageWindow.XMLHttpRequest;
const xhrOpen = originalXHR.prototype.open;
originalXHR.prototype.open = function (_, url) {
    if (url.includes("api.bilibili.com/x/player/wbi/playurl")) {
        // 包括单个视频的多个(画质数量*编码数量)的url
        const avid = url.match(/avid=(\d+)/)?.[1]; // 提取出url中的avid参数
        log("请求视频列表", `av${avid}`);
        const getter = Object.getOwnPropertyDescriptor(originalXHR.prototype, "responseText").get;
        Object.defineProperty(this, "responseText", {
            get: () => {
                let response = getter.call(this);
                let responseJson = JSON.parse(response);
                cleanPlayInfo(responseJson);
                let modifiedResponse = JSON.stringify(responseJson);
                return modifiedResponse;
            },
        });
    }
    if (url.includes("api.bilibili.com/pgc/player/web/v2/playurl")) {
        const season_id = url.match(/season_id=(\d+)/)?.[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(this, "responseText", {
            get: () => {
                let response = getter.call(this);
                let responseJson = JSON.parse(response);
                cleanPlayInfo(responseJson);
                let modifiedResponse = JSON.stringify(responseJson);
                return modifiedResponse;
            },
        });
    }
    return xhrOpen.apply(this, 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);
    };
}

})();

/******/ })()
;