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.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
// ==/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);
    };
}

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