Instantly share code, notes, and snippets.
Last active
May 10, 2025 07:39
-
Star
47
(47)
You must be signed in to star a gist -
Fork
14
(14)
You must be signed in to fork a gist
-
Save lyc8503/614f9d62c1b00175867fe0efc93c374f to your computer and use it in GitHub Desktop.
已被 bilibili 制裁并无法修复,仅供技术学习,后续不会更新
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Bilibili share | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description Bilibili share | |
// @author You | |
// @match *://www.bilibili.com/bangumi/play/* | |
// @match *://www.bilibili.com/video/* | |
// @connect api.bilibili.com | |
// @icon https://www.bilibili.com/favicon.ico | |
// @grant GM_xmlhttpRequest | |
// @grant GM_getValue | |
// @grant GM_setValue | |
// @grant GM_registerMenuCommand | |
// @run-at document-start | |
// ==/UserScript== | |
// 尽早将 __playinfo__ 覆盖, 使 B 站重新请求 playurl API, 不要使用随 HTML 返回的内容 | |
Object.defineProperty(unsafeWindow, '__playinfo__', { | |
get: () => undefined | |
}); | |
Object.defineProperty(unsafeWindow, 'playurlSSRData', { | |
get: () => undefined | |
}); | |
let dataProxy = undefined | |
Object.defineProperty(unsafeWindow, '__NEXT_DATA__', { | |
get: () => dataProxy, | |
set: (d) => { | |
dataProxy = d | |
if (dataProxy.props?.pageProps?.dehydratedState?.queries) { | |
dataProxy.props.pageProps.dehydratedState.queries = [] | |
console.log("queries in __NEXT_DATA__ removed") | |
} | |
console.log(d) | |
} | |
}); | |
(function() { | |
'use strict' | |
// 虽然未观察到相关的风控行为, 但推荐同时安装 Adblock 类插件屏蔽跟踪器 | |
const vip_cookie = GM_getValue("vip_cookie") | |
console.log("Use Cookie: " + vip_cookie) | |
const promptCookieInput = () => { | |
const cookie = prompt("请输入一个大会员账号的 Cookie\n(只需要 SESSDATA 部分, 直接复制全部 Cookie 也行)\n" + | |
"该 Cookie 只会用于解析视频的播放地址\n目前已经支持普通视频+番剧\n" + | |
"初次设置后可以在油猴插件选项中设置新的 Cookie\n(一般 Cookie 有效期比较久)") | |
if (cookie) { | |
GM_setValue("vip_cookie", cookie) | |
} | |
location.reload() | |
} | |
if (!vip_cookie) { | |
promptCookieInput() | |
return; | |
} | |
GM_registerMenuCommand("重设大会员 Cookie", promptCookieInput) | |
if (location.href.includes("bangumi/play")) { | |
// 隐藏 playerPop 遮盖 | |
setInterval(() => { | |
const node = document.querySelector('div[class^="playerPop"]'); | |
if (node && node.style.display == '') { | |
node.style.display = 'none' | |
console.log("Patched playerPop display to none") | |
} | |
}, 2000) | |
// 番剧页面多次切换有 bug, 刷新一下清除状态 | |
window.addEventListener('locationchange', function (event) { | |
// 同一页面不再刷新 | |
if (event.detail?.origin && event.detail?.origin.match(/bangumi\/play\/ep\d+/)?.[0] !== location.href.match(/bangumi\/play\/ep\d+/)?.[0]) { | |
console.log("Refresh page") | |
location.reload() | |
} | |
}); | |
} | |
// 替换 XHR, Hook 相关请求 | |
const oriGetAllResponseHeaders = XMLHttpRequest.prototype.getAllResponseHeaders | |
XMLHttpRequest.prototype.getAllResponseHeaders = function(args) { | |
return this._headers === undefined ? oriGetAllResponseHeaders.apply(this, args): this._headers | |
} | |
const oriSend = XMLHttpRequest.prototype.send | |
XMLHttpRequest.prototype.send = function() { | |
if (this._url && this._url.includes("playurl") && this._url.startsWith("https://api.bilibili.com")) { | |
// 获取播放地址 API, 需要使用大会员 Cookie 进行请求 | |
console.log('Hook: playurl request') | |
console.log(this) | |
GM_xmlhttpRequest({ | |
method: this._method, | |
url: this._url, | |
anonymous: true, // 不携带原有 Cookie | |
headers: { | |
"Cookie": vip_cookie, // 使用大会员 Cookie | |
"Referer": location.href | |
}, | |
onload: (args) => { | |
console.log("Hooked response: ") | |
console.log(args) | |
// 解锁只有大会员才能选择的清晰度选项 | |
console.log("Patched response text: " + args.response.replaceAll('"need_login":true', '"need_login":false').replaceAll('"need_vip":true', '"need_vip":false')) | |
this._headers = args.responseHeaders | |
Object.defineProperty(this, 'readyState', { | |
get: () => args.readyState | |
}) | |
Object.defineProperty(this, 'status', { | |
get: () => args.status | |
}) | |
Object.defineProperty(this, 'statusText', { | |
get: () => args.statusText | |
}) | |
Object.defineProperty(this, 'response', { | |
get: () => args.response.replaceAll('"need_login":true', '"need_login":false').replaceAll('"need_vip":true', '"need_vip":false') | |
}) | |
Object.defineProperty(this, 'responseText', { | |
get: () => args.responseText.replaceAll('"need_login":true', '"need_login":false').replaceAll('"need_vip":true', '"need_vip":false') | |
}) | |
console.log(this) | |
this.onloadend?.(args); // 新版页面 | |
this.onreadystatechange?.(args); // 旧版页面 | |
} | |
}) | |
} else if (this._url && this._url.includes("x/player/wbi/v2") && this._url.startsWith("https://api.bilibili.com")) { | |
// 获取当前用户 vip 状态的接口, 替换 response 中的 status, 否则普通视频页面不能切换清晰度 | |
console.log('Hook: wbi request') | |
console.log(this) | |
const oriLoadEnd = this.onloadend; | |
this.onloadend = (args) => { | |
console.log('Hooked response:') | |
let response = JSON.parse(this.response); | |
let responseText = JSON.parse(this.responseText); | |
response.data.vip.status = 1; | |
responseText.data.vip.status = 1; | |
response = JSON.stringify(response) | |
responseText = JSON.stringify(responseText) | |
console.log(response) | |
Object.defineProperty(this, 'response', { | |
get: () => response | |
}) | |
Object.defineProperty(this, 'responseText', { | |
get: () => responseText | |
}) | |
console.log(this) | |
oriLoadEnd.apply(this, args) | |
} | |
oriSend.apply(this, arguments); | |
} else { | |
oriSend.apply(this, arguments); | |
} | |
}; | |
const oriOpen = XMLHttpRequest.prototype.open | |
XMLHttpRequest.prototype.open = function() { | |
this._method = arguments[0] | |
this._url = arguments[1].startsWith("//") ? ("https:" + arguments[1]) : arguments[1] | |
oriOpen.apply(this, arguments) | |
}; | |
})(); | |
(() => { | |
let oldPushState = history.pushState; | |
history.pushState = function pushState() { | |
let origin = location.href; | |
let ret = oldPushState.apply(this, arguments); | |
window.dispatchEvent(new CustomEvent('pushstate', { detail: { origin: origin } })); | |
window.dispatchEvent(new CustomEvent('locationchange', { detail: { origin: origin } })); | |
return ret; | |
}; | |
let oldReplaceState = history.replaceState; | |
history.replaceState = function replaceState() { | |
let origin = location.href; | |
let ret = oldReplaceState.apply(this, arguments); | |
window.dispatchEvent(new CustomEvent('replacestate', { detail: { origin: origin } })); | |
window.dispatchEvent(new CustomEvent('locationchange', { detail: { origin: origin } })); | |
return ret; | |
}; | |
window.addEventListener('popstate', () => { | |
window.dispatchEvent(new CustomEvent('locationchange')); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment