Make BiliBili Great Again

useful tweaks for bilibili.com

目前为 2023-11-22 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Make BiliBili Great Again
  3. // @namespace https://www.kookxiang.com/
  4. // @version 1.5.4
  5. // @description useful tweaks for bilibili.com
  6. // @author kookxiang
  7. // @match https://*.bilibili.com/*
  8. // @run-at document-body
  9. // @grant unsafeWindow
  10. // @grant GM_addStyle
  11. // @grant GM_notification
  12. // ==/UserScript==
  13.  
  14. // 去掉叔叔去世时的全站黑白效果
  15. GM_addStyle("html, body { -webkit-filter: none !important; filter: none !important; }");
  16.  
  17. // 没用的 URL 参数
  18. const uselessUrlParams = [
  19. 'buvid',
  20. 'is_story_h5',
  21. 'launch_id',
  22. 'live_from',
  23. 'mid',
  24. 'session_id',
  25. 'timestamp',
  26. 'up_id',
  27. 'vd_source',
  28. /^share/,
  29. /^spm/,
  30. ];
  31.  
  32. // Block WebRTC,CNM 陈睿你就缺这点棺材钱?
  33. try {
  34. Object.defineProperty(unsafeWindow, 'webkitRTCPeerConnection', { value: undefined, enumerable: false, writable: false });
  35. } catch (e) { }
  36.  
  37. // 移除鸿蒙字体,系统自带它不香吗?
  38. window.addEventListener('load', function () {
  39. document.body.classList.remove('harmony-font');
  40. })
  41.  
  42. // 首页优化
  43. if (location.host === "www.bilibili.com") {
  44. GM_addStyle('.feed2 .container > *:has(a[href*="cm.bilibili.com"]) { display: none } .feed2 .container > * { margin-top: 0 !important }');
  45. }
  46.  
  47. // 动态页面优化
  48. if (location.host === "t.bilibili.com") {
  49. GM_addStyle("html[wide] #app { display: flex; } html[wide] .bili-dyn-home--member { box-sizing: border-box;padding: 0 10px;width: 100%;flex: 1; } html[wide] .bili-dyn-content { width: initial; } html[wide] main { margin: 0 8px;flex: 1;overflow: hidden;width: initial; } #wide-mode-switch { margin-left: 0;margin-right: 20px; }");
  50. if (!localStorage.WIDE_OPT_OUT) {
  51. document.documentElement.setAttribute('wide', 'wide');
  52. }
  53. window.addEventListener('load', function () {
  54. const tabContainer = document.querySelector('.bili-dyn-list-tabs__list');
  55. const placeHolder = document.createElement('div');
  56. placeHolder.style.flex = 1;
  57. const switchButton = document.createElement('a');
  58. switchButton.id = 'wide-mode-switch';
  59. switchButton.className = 'bili-dyn-list-tabs__item';
  60. switchButton.textContent = '宽屏模式';
  61. switchButton.addEventListener('click', function (e) {
  62. e.preventDefault();
  63. if (localStorage.WIDE_OPT_OUT) {
  64. localStorage.removeItem('WIDE_OPT_OUT');
  65. document.documentElement.setAttribute('wide', 'wide');
  66. } else {
  67. localStorage.setItem('WIDE_OPT_OUT', '1');
  68. document.documentElement.removeAttribute('wide');
  69. }
  70. })
  71. tabContainer.appendChild(placeHolder);
  72. tabContainer.appendChild(switchButton);
  73. })
  74. }
  75.  
  76. // 去广告
  77. GM_addStyle('.ad-report, a[href*="cm.bilibili.com"] { display: none !important; }');
  78. if (unsafeWindow.__INITIAL_STATE__?.adData) {
  79. for (const key in unsafeWindow.__INITIAL_STATE__.adData) {
  80. if (!Array.isArray(unsafeWindow.__INITIAL_STATE__.adData[key])) continue;
  81. for (const item of unsafeWindow.__INITIAL_STATE__.adData[key]) {
  82. item.name = 'B 站未来有可能会倒闭,但绝不会变质';
  83. item.pic = 'https://static.hdslb.com/images/transparent.gif';
  84. item.url = 'https://space.bilibili.com/208259';
  85. }
  86. }
  87. }
  88.  
  89. // 去充电列表(叔叔的跳过按钮越做越小了,就尼玛离谱)
  90. if (unsafeWindow.__INITIAL_STATE__?.elecFullInfo) {
  91. delete unsafeWindow.__INITIAL_STATE__.elecFullInfo;
  92. }
  93.  
  94. // 修复文章区复制
  95. if (location.href.startsWith('https://www.bilibili.com/read/cv')) {
  96. unsafeWindow.original.reprint = "1";
  97. document.querySelector('.article-holder').classList.remove("unable-reprint");
  98. document.querySelector('.article-holder').addEventListener('copy', e => e.stopImmediatePropagation(), true);
  99. }
  100.  
  101. // 去 P2P CDN
  102. Object.defineProperty(unsafeWindow, 'PCDNLoader', { value: class { }, enumerable: false, writable: false });
  103. Object.defineProperty(unsafeWindow, 'BPP2PSDK', { value: class { }, enumerable: false, writable: false });
  104. Object.defineProperty(unsafeWindow, 'SeederSDK', { value: class { }, enumerable: false, writable: false });
  105. if (location.href.startsWith('https://www.bilibili.com/video/') || location.href.startsWith('https://www.bilibili.com/bangumi/play/')) {
  106. let cdnDomain;
  107.  
  108. function replaceP2PUrl(url) {
  109. cdnDomain ||= document.head.innerHTML.match(/up[\w-]+\.bilivideo\.com/)?.[0];
  110.  
  111. try {
  112. const urlObj = new URL(url);
  113. const hostName = urlObj.hostname;
  114. if (urlObj.hostname.endsWith(".mcdn.bilivideo.cn")) {
  115. urlObj.host = cdnDomain || 'upos-sz-mirrorcoso1.bilivideo.com';
  116. urlObj.port = 443;
  117. console.warn(`更换视频源: ${hostName} -> ${urlObj.host}`);
  118. return urlObj.toString();
  119. } else if (urlObj.hostname.endsWith(".szbdyd.com")) {
  120. urlObj.host = urlObj.searchParams.get('xy_usource');
  121. urlObj.port = 443;
  122. console.warn(`更换视频源: ${hostName} -> ${urlObj.host}`);
  123. return urlObj.toString();
  124. }
  125. return url;
  126. } catch (e) {
  127. return url;
  128. }
  129. }
  130.  
  131. function replaceP2PUrlDeep(obj) {
  132. for (const key in obj) {
  133. if (typeof obj[key] === 'string') {
  134. obj[key] = replaceP2PUrl(obj[key]);
  135. } else if (typeof obj[key] === 'array' || typeof obj[key] === 'object') {
  136. replaceP2PUrlDeep(obj[key]);
  137. }
  138. }
  139. }
  140.  
  141. replaceP2PUrlDeep(unsafeWindow.__playinfo__);
  142.  
  143. (function (open) {
  144. unsafeWindow.XMLHttpRequest.prototype.open = function () {
  145. try {
  146. arguments[1] = replaceP2PUrl(arguments[1]);
  147. } finally {
  148. return open.apply(this, arguments);
  149. }
  150. }
  151. })(unsafeWindow.XMLHttpRequest.prototype.open);
  152. }
  153.  
  154. // 真·原画直播
  155. if (location.href.startsWith('https://live.bilibili.com/')) {
  156. let forceHighestQuality = true;
  157. let recentErrors = 0;
  158. setInterval(() => recentErrors > 0 ? recentErrors / 2 : null, 10000);
  159.  
  160. const oldFetch = unsafeWindow.fetch;
  161. unsafeWindow.fetch = function (url) {
  162. try {
  163. const mcdnRegexp = /[xy0-9]+\.mcdn\.bilivideo\.cn:\d+/
  164. const qualityRegexp = /(live-bvc\/\d+\/live_\d+_\d+)_\w+/;
  165. if (mcdnRegexp.test(arguments[0]) && unsafeWindow.disableMcdn) {
  166. return Promise.reject();
  167. }
  168. if (qualityRegexp.test(arguments[0]) && forceHighestQuality) {
  169. arguments[0] = arguments[0]
  170. .replace(qualityRegexp, '$1')
  171. .replace(/(\d+)_(mini|pro)hevc/g, '$1');
  172. }
  173. const promise = oldFetch.apply(this, arguments);
  174. promise.then(response => {
  175. if (!url.match(/\.(m3u8|m4s)/)) return;
  176. if ([403, 404].includes(response.status)) recentErrors++;
  177. if (recentErrors >= 5 && forceHighestQuality) {
  178. forceHighestQuality = false;
  179. GM_notification({ title: '最高清晰度可能不可用', text: '已为您自动切换至播放器上选择的清晰度.', timeout: 3000, silent: true });
  180. }
  181. });
  182. return promise;
  183. } catch (e) { }
  184. return oldFetch.apply(this, arguments);
  185. }
  186. }
  187.  
  188. // 视频裁切
  189. if (location.href.startsWith('https://www.bilibili.com/video/')) {
  190. GM_addStyle("body[video-fit] #bilibili-player video { object-fit: cover; } .bpx-player-ctrl-setting-fit-mode { display: flex;width: 100%;height: 32px;line-height: 32px; } .bpx-player-ctrl-setting-box .bui-panel-wrap, .bpx-player-ctrl-setting-box .bui-panel-item { min-height: 172px !important; }");
  191. let timer;
  192. function toggleMode(enabled) {
  193. if (enabled) {
  194. document.body.setAttribute('video-fit', '');
  195. } else {
  196. document.body.removeAttribute('video-fit');
  197. }
  198. }
  199. function injectButton() {
  200. if (!document.querySelector('.bpx-player-ctrl-setting-menu-left')) {
  201. return;
  202. }
  203. clearInterval(timer);
  204. const parent = document.querySelector('.bpx-player-ctrl-setting-menu-left');
  205. const item = document.createElement('div');
  206. item.className = 'bpx-player-ctrl-setting-fit-mode bui bui-switch';
  207. item.innerHTML = '<input class="bui-switch-input" type="checkbox"><label class="bui-switch-label"><span class="bui-switch-name">裁切模式</span><span class="bui-switch-body"><span class="bui-switch-dot"><span></span></span></span></label>';
  208. parent.insertBefore(item, document.querySelector('.bpx-player-ctrl-setting-more'));
  209. document.querySelector('.bpx-player-ctrl-setting-fit-mode input').addEventListener('change', e => toggleMode(e.target.checked));
  210. document.querySelector('.bpx-player-ctrl-setting-box .bui-panel-item').style.height = '';
  211. }
  212. timer = setInterval(injectButton, 200);
  213. }
  214.  
  215. // 去除地址栏多余参数
  216. unsafeWindow.history.replaceState(undefined, undefined, removeTracking(location.href));
  217. const pushState = unsafeWindow.history.pushState;
  218. unsafeWindow.history.pushState = function (state, unused, url) {
  219. return pushState.apply(this, [state, unused, removeTracking(url)]);
  220. }
  221. const replaceState = unsafeWindow.history.replaceState;
  222. unsafeWindow.history.replaceState = function (state, unused, url) {
  223. return replaceState.apply(this, [state, unused, removeTracking(url)]);
  224. }
  225.  
  226. function removeTracking(url) {
  227. if (!url) return url;
  228. try {
  229. const [base, search] = url.split('?');
  230. if (!search) return url;
  231. const searchParams = new URLSearchParams('?' + search);
  232. const keys = Array.from(searchParams.keys());
  233. for (const key of keys) {
  234. uselessUrlParams.forEach(item => {
  235. if (typeof item === 'string') {
  236. if (item === key) searchParams.delete(key);
  237. } else if (item instanceof RegExp) {
  238. if (item.test(key)) searchParams.delete(key);
  239. }
  240. });
  241. }
  242. if (location.pathname === base && !searchParams.size) return;
  243. return [base, searchParams.toString()].filter(Boolean).join('?');
  244. } catch (e) {
  245. return url;
  246. }
  247. }
  248.  
  249. // 去掉 B 站的傻逼上报
  250. !function () {
  251. const oldFetch = unsafeWindow.fetch;
  252. unsafeWindow.fetch = function (url) {
  253. if (typeof url === 'string' && url.includes('data.bilibili.com'))
  254. return Promise.resolve(new Response());
  255. return oldFetch.apply(this, arguments);
  256. }
  257. const oldSend = unsafeWindow.XMLHttpRequest.prototype.open;
  258. unsafeWindow.XMLHttpRequest.prototype.open = function (method, url) {
  259. if (typeof url === 'string' && url.includes('data.bilibili.com')) {
  260. this.send = function () { };
  261. return;
  262. }
  263. return oldSend.apply(this, arguments);
  264. }
  265. unsafeWindow.navigator.sendBeacon = () => Promise.resolve();
  266.  
  267. unsafeWindow.MReporter = {
  268. appear() { },
  269. click() { },
  270. tech() { },
  271. pv() { },
  272. import: { auto() { } },
  273. }
  274. const sentryHub = class { bindClient() { } }
  275. const fakeSentry = {
  276. SDK_NAME: 'sentry.javascript.browser',
  277. SDK_VERSION: '0.0.0',
  278. BrowserClient: class { },
  279. Hub: sentryHub,
  280. Integrations: { Vue: class { } },
  281. init() { },
  282. configureScope() { },
  283. getCurrentHub: () => new sentryHub(),
  284. setContext() { },
  285. setExtra() { },
  286. setExtras() { },
  287. setTag() { },
  288. setTags() { },
  289. setUser() { },
  290. wrap() { },
  291. }
  292. if (!unsafeWindow.Sentry || unsafeWindow.Sentry.SDK_VERSION !== fakeSentry.SDK_VERSION) {
  293. if (unsafeWindow.Sentry) { delete unsafeWindow.Sentry }
  294. Object.defineProperty(unsafeWindow, 'Sentry', { value: fakeSentry, enumerable: false, writable: false });
  295. }
  296. Object.defineProperty(unsafeWindow, 'ReporterPb', { value: class { click() { } custom() { } exposure() { } report() { } tech() { } pv() { } }, enumerable: false, writable: false });
  297. }()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址