Greasy Fork

Greasy Fork is available in English.

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

从官方CDN加载视频

当前为 2024-08-11 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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

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