Bilibili - 优化未登录(不可用)情况下的移动网页端

优化未登录(不可用)情况下的移动网页端的使用体验

目前为 2024-06-12 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Bilibili - 优化未登录(不可用)情况下的移动网页端
  3. // @namespace https://bilibili.com/
  4. // @version 0.1
  5. // @description 优化未登录(不可用)情况下的移动网页端的使用体验
  6. // @license GPL-3.0
  7. // @author DD1969
  8. // @match https://m.bilibili.com/*
  9. // @icon https://www.bilibili.com/favicon.ico
  10. // @grant none
  11. // @run-at document-end
  12. // ==/UserScript==
  13.  
  14. (async function() {
  15. 'use strict';
  16.  
  17. // prevent downloading apk or opening external app
  18. Object.defineProperty(document, 'hidden', { get: () => true });
  19. Object.defineProperty(Event.prototype, 'timeStamp', { get: () => NaN });
  20. const emptyFunction = () => {};
  21. setInterval(() => {
  22. if (window.PlayerAgent && window.PlayerAgent.openApp !== emptyFunction) {
  23. window.PlayerAgent.openApp = emptyFunction;
  24. }
  25. }, 500);
  26.  
  27. // add custom style
  28. const styleElement = document.createElement('style');
  29. styleElement.textContent = `
  30. #app .m-navbar .right .face,
  31. #app .m-navbar .right .m-nav-openapp,
  32. .home-float-openapp,
  33. .caution-dialog,
  34. .launch-app-btn.m-video-main-launchapp,
  35. [class*=float-openapp],
  36. #relateRecomMore,
  37. .card > .open-app,
  38. #app .mplayer-widescreen-callapp,
  39. #app .mplayer-fullscreen-call-app,
  40. .visible-open-app-btn,
  41. .share-video-info .title-wrapper .title a.label,
  42. .share-video-info .title-wrapper .icon-spread,
  43. .up .interact-wrapper,
  44. .bottom-tabs,
  45. .list-view__state,
  46. .mplayer-control-btn-speed .mplayer-control-dot,
  47. .mplayer-control-btn-quality .mplayer-control-dot,
  48. #bilibiliPlayer .mplayer-comment-text,
  49. .v-card-toapp .card .label,
  50. .play-page-gotop,
  51. .natural-main-video .main-cover .play-icon {
  52. display: none !important;
  53. }
  54.  
  55. .m-navbar .right .search {
  56. width: auto !important;
  57. height: 42px !important;
  58. margin-right: 0 !important;
  59. padding: 12px 0 8px 128px;
  60. }
  61.  
  62. .m-search-together .list {
  63. overflow-x: hidden;
  64. }
  65.  
  66. .fixed-module {
  67. position: relative !important;
  68. }
  69.  
  70. .natural-main-video {
  71. padding-bottom: 24px;
  72. flex-direction: column;
  73. }
  74.  
  75. .natural-main-video .main-cover {
  76. margin-bottom: 20px;
  77. width: 100% !important;
  78. }
  79.  
  80. .natural-main-video .main-info {
  81. margin: 0;
  82. width: 100%;
  83. }
  84.  
  85. .natural-main-video .main-info .btn.light {
  86. margin-top: 20px;
  87. width: 100%;
  88. height: 48px;
  89. display: flex;
  90. justify-content: center;
  91. align-items: center;
  92. font-size: 16px;
  93. }
  94.  
  95. .natural-module > .m-video-related {
  96. margin-top: 0 !important;
  97. padding-top: 16px;
  98. border-top: 1px dashed #AAAAAA;
  99. }
  100.  
  101. .bottom-tab > .m-video-related {
  102. padding-top: 16px;
  103. }
  104.  
  105. .v-dialog.natural-dialog:has(.to-see) {
  106. opacity: 0 !important;
  107. }
  108.  
  109. .m-video-player {
  110. position: relative !important;
  111. z-index: 999 !important;
  112. }
  113.  
  114. .share-video-info {
  115. margin-top: 56px !important;
  116. }
  117.  
  118. .share-video-info .title-name {
  119. margin-left: 0 !important;
  120. }
  121.  
  122. .share-video-info h1.title-text {
  123. font-weight: bold !important;
  124. font-size: 16px !important;
  125. }
  126.  
  127. .m-footer {
  128. margin-top: 40px;
  129. padding-top: 90px;
  130. border-top: 1px dashed #AAAAAA;
  131. }
  132.  
  133. .playback-rate-option-container {
  134. width: 240px;
  135. padding: 8px;
  136. display: flex;
  137. flex-direction: column;
  138. align-items: center;
  139. background-color: #FFFFFF;
  140. border-radius: 4px;
  141. user-select: none;
  142. }
  143.  
  144. .playback-rate-option {
  145. width: 100%;
  146. margin-top: 2px;
  147. padding: 8px 0;
  148. color: #FFFFFF;
  149. background-color: #00AEEC;
  150. border-top: 1px solid #EEEEEE;
  151. border-radius: 4px;
  152. text-align: center;
  153. }
  154. `;
  155. document.head.appendChild(styleElement);
  156.  
  157. // open home video in new tab
  158. setInterval(() => {
  159. const videoCards = Array.from(document.querySelectorAll('.m-home .card-box a.v-card:not(.modified)'));
  160. for (const card of videoCards) {
  161. card.classList.add('modified');
  162. card.setAttribute('target', '_blank');
  163. }
  164. }, 1000);
  165.  
  166. // open searched video in new tab
  167. setInterval(() => {
  168. const videoCards = Array.from(document.querySelectorAll('.video-list .card-box .v-card-single:not(.modified)'));
  169. for (const card of videoCards) {
  170. const maskElement = document.createElement('div');
  171. maskElement.style = `
  172. position: absolute;
  173. top: 0;
  174. left: 0;
  175. width: 100vw;
  176. height: 100%;
  177. background-color: #000000;
  178. opacity: 0;
  179. `;
  180. maskElement.addEventListener('click', (e) => {
  181. if (parseInt(card.dataset.aid) === 0) return;
  182. e.stopPropagation();
  183. window.open(`https://m.bilibili.com/video/${av2bv(card.dataset.aid)}`, '_blank');
  184. });
  185.  
  186. card.style.position = 'relative';
  187. card.classList.add('modified');
  188. card.appendChild(maskElement);
  189. }
  190. }, 1000);
  191.  
  192. // click cancel btn which appeared after clicking video card in search page
  193. setInterval(() => {
  194. const openAppDialogCancelBtn = document.querySelector('.v-dialog .open-app-dialog .open-app-dialog-btn.cancel');
  195. if (openAppDialogCancelBtn) openAppDialogCancelBtn.click();
  196. }, 100);
  197.  
  198. // click the "X" mark in the bottom dialog in video page
  199. const timer4CloseBtn = setInterval(() => {
  200. const openAppDialogCloseBtn = document.querySelector('.openapp-dialog .dialog-close');
  201. if (openAppDialogCloseBtn) {
  202. openAppDialogCloseBtn.click();
  203. clearInterval(timer4CloseBtn);
  204. }
  205. }, 100);
  206.  
  207. // add publish date
  208. setInterval(() => {
  209. const publishDate = document.querySelector('meta[itemprop=datePublished]');
  210. const authorElement = document.querySelector('.natural-module .main-info .author');
  211. if (publishDate && authorElement && !authorElement.textContent.includes(' @ ')) {
  212. authorElement.textContent += ` @ ${publishDate.getAttribute('content')}`;
  213. }
  214. }, 500);
  215.  
  216. // click the "play now" button on the top
  217. const timer4PlayBtn = setInterval(() => {
  218. const playNowBtn = document.querySelector('.natural-main-video .main-info .btn.light');
  219. if (playNowBtn) {
  220. playNowBtn.onclick = () => {
  221. const timer4ToSeeBtn = setInterval(() => {
  222. const toSeeBtn = document.querySelector('.to-see');
  223. if (toSeeBtn) {
  224. toSeeBtn.click();
  225. clearInterval(timer4ToSeeBtn);
  226. }
  227. }, 100);
  228. }
  229. clearInterval(timer4PlayBtn);
  230. }
  231. }, 100);
  232.  
  233. // open space by clicking author avatar or name
  234. setInterval(() => {
  235. const upElement = document.querySelector('.share-video-info .up .face');
  236. const upID = window?.__INITIAL_STATE__?.video?.upInfo?.card?.mid;
  237. if (upElement && upID) {
  238. if (upElement.classList.contains('modified')) return;
  239. upElement.classList.add('modified');
  240. upElement.onclick = () => window.open(`https://m.bilibili.com/space/${upID}`, '_blank');
  241. }
  242. }, 1000);
  243.  
  244. // open recommand video
  245. setInterval(() => {
  246. const videoCards = Array.from(document.querySelectorAll('.m-video-related .card-box .v-card-toapp:not(.modified)'));
  247. for (const card of videoCards) {
  248. card.classList.add('modified');
  249. card.querySelector('a').onclick = () => window.open(`https://m.bilibili.com/video/${av2bv(card.dataset.aid)}`, '_blank');
  250. }
  251. }, 1000);
  252.  
  253. // open video in space
  254. setInterval(() => {
  255. const cards = Array.from(document.querySelectorAll('.dynamic-list .list .cover .card-content .main > [regstring][type="8"]:not(.modified)'));
  256. for (const card of cards) {
  257. card.classList.add('modified');
  258. card.onclick = () => window.open(`https://m.bilibili.com/video/${av2bv(Number(card.id))}`, '_blank');
  259. }
  260. }, 1000);
  261.  
  262. // enable playback rate button
  263. const timer4PlaybackRateBtn = setInterval(() => {
  264. const playbackRateBtn = document.querySelector('.mplayer-control-bar .mplayer-control-btn-speed');
  265. if (playbackRateBtn) {
  266. playbackRateBtn.onclick = () => {
  267. const maskElement = document.createElement('div');
  268. maskElement.style = `
  269. position: fixed;
  270. top: 0;
  271. left: 0;
  272. width: 100%;
  273. height: 100%;
  274. display: flex;
  275. justify-content: center;
  276. align-items: center;
  277. background-color: rgba(0, 0, 0, 0.5);
  278. z-index: 999999;
  279. `;
  280.  
  281. maskElement.innerHTML = `
  282. <div class="playback-rate-option-container">
  283. <span style="margin-bottom: 6px; padding: 6px 0;">播放倍速</span>
  284. <span class="playback-rate-option" data-rate="5">5.0x</span>
  285. <span class="playback-rate-option" data-rate="3">3.0x</span>
  286. <span class="playback-rate-option" data-rate="2">2.0x</span>
  287. <span class="playback-rate-option" data-rate="1.5">1.5x</span>
  288. <span class="playback-rate-option" data-rate="1">1.0x</span>
  289. <span class="playback-rate-option" data-rate="0.5">0.5x</span>
  290. </div>
  291. `;
  292.  
  293. maskElement
  294. .querySelectorAll('.playback-rate-option')
  295. .forEach(optionElement => optionElement.addEventListener('click', function() {
  296. const videoElement = document.querySelector('#bilibiliPlayer video');
  297. if (videoElement) videoElement.playbackRate = parseFloat(this.dataset.rate);
  298. }));
  299. maskElement.onclick = () => maskElement.remove();
  300.  
  301. (document.querySelector('#bilibiliPlayer > .mplayer.mplayer-wide') || document.body).appendChild(maskElement);
  302. }
  303. clearInterval(timer4PlaybackRateBtn);
  304. }
  305. }, 100);
  306.  
  307. // av ID to bv ID
  308. function av2bv(avid) {
  309. const BYTES = ["B", "V", 1, "", "", "", "", "", "", "", "", ""];
  310. const BV_LEN = BYTES.length;
  311. const MAX_AID = 1n << 51n;
  312. const XOR_CODE = 23442827791579n;
  313. const BASE = 58n;
  314. const DIGIT_MAP = [0, 1, 2, 9, 7, 5, 6, 4, 8, 3, 10, 11];
  315. const ALPHABET = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'.split('');
  316.  
  317. if(typeof avid !== "bigint") avid = BigInt(avid);
  318. const bytes = Array.from(BYTES);
  319. let bv_idx = BV_LEN - 1;
  320. let tmp = (MAX_AID | avid) ^ XOR_CODE;
  321. while (tmp !== 0n) {
  322. let table_idx = tmp % BASE;
  323. bytes[DIGIT_MAP[Number(bv_idx)]] = ALPHABET[Number(table_idx)];
  324. tmp /= BASE;
  325. bv_idx -= 1;
  326. }
  327. return bytes.join("");
  328. }
  329.  
  330. })();

QingJ © 2025

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