Bilibili 纯净

纯净 & 原生

  1. // ==UserScript==
  2. // @name Bilibili Pure
  3. // @name:zh-CN Bilibili 纯净
  4. // @description Pure & Native
  5. // @description:zh-CN 纯净 & 原生
  6. // @namespace https://greasyfork.org/users/197529
  7. // @version 0.5.1
  8. // @author kkocdko
  9. // @license Unlicense
  10. // @match *://*.bilibili.com/robots.txt
  11. // @match *://*.bilibili.com/video/*
  12. // ==/UserScript==
  13. "use strict";
  14.  
  15. if (location.host === "m.bilibili.com")
  16. throw alert("Bilibili Pure dosen't supports m.bilibili.com domain.");
  17.  
  18. if (location.pathname !== "/robots.txt") {
  19. localStorage.bpTitle = __INITIAL_STATE__.videoData.title;
  20. localStorage.bpBvid = __INITIAL_STATE__.bvid;
  21. localStorage.bpDash = JSON.stringify(__playinfo__.data.dash);
  22. location = "https://www.bilibili.com/robots.txt#" + __INITIAL_STATE__.bvid;
  23. throw "jump to clean page";
  24. }
  25.  
  26. document.head.insertAdjacentHTML(
  27. "beforeend",
  28. `
  29. <meta name="viewport" content="width=device-width">
  30. <!-- <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> -->
  31. <style>
  32. body { display: flex; justify-content: center; align-items: center; margin: 0; height: 100vh; overflow: hidden; }
  33. body > :not(video) { display: none; }
  34. video { max-width: 100%; max-height: 100%; outline: none; }
  35. </style>
  36. `
  37. );
  38.  
  39. // TODO: show parts and serials list
  40. (async () => {
  41. if (!localStorage.bpTitle) return;
  42. document.title = localStorage.bpTitle;
  43. localStorage.bpTitle = undefined;
  44. const bvid = localStorage.bpBvid;
  45. localStorage.bpBvid = undefined;
  46. history.pushState(null, null, `https://www.bilibili.com/video/${bvid}`);
  47. const dash = JSON.parse(localStorage.bpDash);
  48. localStorage.bpDash = undefined;
  49.  
  50. const min = (arr, f = (e) => e) => {
  51. let ret = null;
  52. for (const e of arr) if (ret === null || f(e) < f(ret)) ret = e;
  53. return ret;
  54. }; // TODO: HD
  55. const aInfo = min(dash.audio, (e) => e.bandwidth);
  56. const vInfo = min(dash.video, (e) => (e.codecid === 7 ? -e.width : 9e9));
  57.  
  58. const audio = document.createElement("audio");
  59. audio.src = aInfo.baseUrl;
  60. const video = document.createElement("video");
  61. video.src = vInfo.baseUrl;
  62. video.controls = true;
  63. video.onplay = () => {
  64. audio.play();
  65. };
  66. video.onpause = () => {
  67. audio.pause();
  68. };
  69. video.onratechange = () => {
  70. audio.playbackRate = video.playbackRate;
  71. };
  72. video.onseeking = () => {
  73. audio.pause();
  74. };
  75. let lastLostSync = 0;
  76. const sync = () => {
  77. const diff = Math.abs(audio.currentTime - video.currentTime); // unit: seconds
  78. if (diff <= 0.2) return;
  79. const now = Date.now();
  80. let reason = "";
  81. if (diff > 0.5) reason = "big_drop";
  82. if (now - lastLostSync < 750) reason = "double_miss";
  83. if (reason) {
  84. console.log(`[bp] sync, reason = ${reason}, diff = ${diff}`);
  85. audio.currentTime = video.currentTime;
  86. }
  87. lastLostSync = now;
  88. };
  89. video.onseeked = () => {
  90. sync();
  91. audio.play();
  92. };
  93. setInterval(sync, 500);
  94. // TODO: fix, chrome will auto pause slient video if you switch the tab
  95. document.body.innerHTML = "";
  96. document.body.appendChild(video);
  97. })();