qb-fix-bilibili

inQ_Beta wants to fix some of bilibili problem

目前为 2022-03-21 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name qb-fix-bilibili
  3. // @description inQ_Beta wants to fix some of bilibili problem
  4. // @namespace no1xsyzy
  5. // @match https://*.bilibili.com/*
  6. // @license Apache License, Version 2.0 (Apache-2.0); https://opensource.org/licenses/Apache-2.0
  7. // @version 0.0.6
  8. // @author inQ_Beta
  9. // @grant GM_addStyle
  10. // ==/UserScript==
  11. (function () {
  12. 'use strict';
  13.  
  14. const $ = (x) => document.querySelector(x);
  15. const $$ = (x) => Array.from(document.querySelectorAll(x));
  16.  
  17. function launchObserver({ parentNode, selector, failCallback = null, successCallback = null, stopWhenSuccess = true, config = {
  18. childList: true,
  19. subtree: true,
  20. }, }) {
  21. // if parent node does not exist, use body instead
  22. if (!parentNode) {
  23. parentNode = document.body;
  24. }
  25. const observeFunc = () => {
  26. const selected = document.querySelector(selector);
  27. if (!selected) {
  28. if (failCallback) {
  29. failCallback();
  30. }
  31. return;
  32. }
  33. if (stopWhenSuccess) {
  34. observer.disconnect();
  35. }
  36. if (successCallback) {
  37. successCallback(selected);
  38. }
  39. };
  40. const observer = new MutationObserver(observeFunc);
  41. observer.observe(parentNode, config);
  42. }
  43.  
  44. function 关注栏尺寸 () {
  45. GM_addStyle(`.section-content-cntr{height:calc(100vh - 250px)!important;}`);
  46.  
  47. launchObserver({
  48. selector: `.side-bar-popup-cntr.ts-dot-4`,
  49. successCallback: () => {
  50. const g = $`.side-bar-popup-cntr.ts-dot-4`;
  51. if (g.style.height !== '0px') {
  52. g.style.bottom = '75px';
  53. g.style.height = 'calc(100vh - 150px)';
  54. // g.style.height = "600px"
  55. }
  56. if ($(`.side-bar-popup-cntr.ts-dot-4 .ps`)) {
  57. setTimeout(() => $(`.side-bar-popup-cntr.ts-dot-4 .ps`).dispatchEvent(new Event('scroll')), 1000);
  58. }
  59. },
  60. stopWhenSuccess: false,
  61. config: {
  62. childList: true,
  63. subtree: true,
  64. attributes: true,
  65. },
  66. });
  67. }
  68.  
  69. const makeTitle$1 = () =>
  70. `${($`#area-tags header img+div` || $`#area-tags header h2`).innerText} - 分区列表 - 哔哩哔哩直播`;
  71. const parentNode$3 = $`#area-tags`;
  72. const selector$3 = `header`;
  73. function 分区标题 () {
  74. launchObserver({
  75. parentNode: parentNode$3,
  76. selector: selector$3,
  77. successCallback: () => {
  78. document.title = makeTitle$1();
  79. },
  80. stopWhenSuccess: false,
  81. });
  82.  
  83. document.title = makeTitle$1();
  84. }
  85.  
  86. const TTL = 10 * 60 * 1000;
  87. function timedLRU1(func) {
  88. const cache = new Map();
  89. let time = [];
  90. let timeout = null;
  91. const cleanup = () => {
  92. if (timeout !== null) {
  93. clearTimeout(timeout);
  94. }
  95. const ts = new Date().getTime();
  96. const idx = time.findIndex(([a, t]) => t + TTL > ts);
  97. const drop = time.splice(idx);
  98. for (const [a] of drop) {
  99. cache.delete(a);
  100. }
  101. timeout = setTimeout(cleanup, 60 * 1000);
  102. };
  103. return (a1) => {
  104. const got = cache.get(a1);
  105. if (got !== undefined) {
  106. const ts = new Date().getTime();
  107. time = [[a1, ts], ...time.filter(([a, t]) => a !== a1)];
  108. cleanup();
  109. return got;
  110. }
  111. const val = func(a1);
  112. const ts = new Date().getTime();
  113. time = [[a1, ts], ...time];
  114. cache.set(a1, val);
  115. return val;
  116. };
  117. }
  118.  
  119. const getCard = timedLRU1(async (uid) => {
  120. const json = await (await fetch(`https://api.bilibili.com/x/web-interface/card?mid=${uid}`, {
  121. // credentials: 'include',
  122. headers: {
  123. Accept: 'application/json',
  124. },
  125. method: 'GET',
  126. mode: 'cors',
  127. })).json();
  128. if (json.code === 0) {
  129. return json.data;
  130. }
  131. else {
  132. throw json.message;
  133. }
  134. });
  135. const getFansCount = async (uid) => {
  136. return (await getCard(uid)).card.fans;
  137. };
  138. const getSexTag = async (uid) => {
  139. const sex = (await getCard(uid)).card.sex;
  140. switch (sex) {
  141. case '男':
  142. return '♂';
  143. case '女':
  144. return '♀';
  145. default:
  146. return '〼';
  147. }
  148. };
  149. const getInfoByRoom = timedLRU1(async (roomid) => {
  150. const json = await (await fetch(`https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomid}`, {
  151. // credentials: 'include',
  152. headers: {
  153. Accept: 'application/json',
  154. },
  155. method: 'GET',
  156. mode: 'cors',
  157. })).json();
  158. if (json.code === 0) {
  159. return json.data;
  160. }
  161. else {
  162. throw json.message;
  163. }
  164. });
  165. const getRoomFollowers = async (roomid) => {
  166. return (await getInfoByRoom(roomid)).anchor_info.relation_info.attention;
  167. };
  168. const followersTextClass = (followers) => {
  169. if (followers > 1e6) {
  170. return [`${Math.round(followers / 1e5) / 10}m★`, 'followers-m'];
  171. }
  172. else if (followers > 1e3) {
  173. return [`${Math.round(followers / 1e2) / 10}k★`, 'followers-k'];
  174. }
  175. else {
  176. return [`${followers}★`, ''];
  177. }
  178. };
  179.  
  180. const parentNode$2 = $(`#area-tag-list`);
  181. const selector$2 = `a.Item_1EohdhbR`;
  182. GM_addStyle(`
  183. .processed::after {
  184. content: attr(data-followers);
  185. color: white;
  186. }
  187. .processed.followers-m::before{
  188. color: purple;
  189. }
  190. .processed.followers-k::before{
  191. color: red;
  192. }
  193. `);
  194. function 分区添加粉丝数 () {
  195. launchObserver({
  196. parentNode: parentNode$2,
  197. selector: selector$2,
  198. successCallback: () => {
  199. for (const a of $$(`a.Item_1EohdhbR`)) {
  200. (async () => {
  201. const nametag = a.querySelector(`.Item_QAOnosoB`);
  202. if (nametag.classList.contains('processed')) {
  203. return;
  204. }
  205. const followers = await getRoomFollowers(a.pathname.slice(1));
  206. let [txt, cls] = followersTextClass(followers);
  207. nametag.dataset.followers = txt;
  208. nametag.classList.add('processed');
  209. nametag.classList.add(cls);
  210. })();
  211. }
  212. },
  213. stopWhenSuccess: false,
  214. });
  215. }
  216.  
  217. function 分区 () {
  218. 关注栏尺寸();
  219. 分区标题();
  220. 分区添加粉丝数();
  221. }
  222.  
  223. function liveStatus() {
  224. switch ($`.live-status`.innerText) {
  225. case '直播':
  226. return '▶️'
  227. case '闲置':
  228. return '⏹️'
  229. case '轮播':
  230. return '🔁'
  231. default:
  232. return `【${$`.live-status`.innerText}】`
  233. }
  234. }
  235.  
  236. const liveTitle = () => $`.live-title`.innerText;
  237. const liveHost = () => $`.room-owner-username`.innerText;
  238. const makeTitle = () => `${liveStatus()} ${liveTitle()} - ${liveHost()} - 哔哩哔哩直播`;
  239. const parentNode$1 = $`#head-info-vm .left-header-area`;
  240. const selector$1 = `.live-title`;
  241.  
  242. function 直播间标题 () {
  243. launchObserver({
  244. parentNode: parentNode$1,
  245. selector: selector$1,
  246. successCallback: () => {
  247. document.title = makeTitle();
  248. },
  249. stopWhenSuccess: false,
  250. });
  251.  
  252. document.title = makeTitle();
  253. }
  254.  
  255. function 通用表情框尺寸修复 () {
  256. GM_addStyle(`
  257. #control-panel-ctnr-box > .border-box.top-left[style^="transform-origin: 249px "],
  258. #control-panel-ctnr-box > .border-box.top-left[style^="transform-origin: 251px "]
  259. {
  260. height: 700px
  261. }
  262. `);
  263. }
  264.  
  265. const parentNode = $(`#chat-items`);
  266. const selector = `.user-name`;
  267. GM_addStyle(`.infoline::before{
  268. content: attr(data-infoline);
  269. color: white;
  270. }
  271. .infoline.followers-m::before{
  272. color: purple;
  273. }
  274. .infoline.followers-k::before{
  275. color: red;
  276. }
  277. `);
  278. const append = async (un) => {
  279. un.classList.add('infoline');
  280. const uid = un.parentNode.dataset.uid;
  281. const fans = await getFansCount(uid);
  282. const [txt, cls] = followersTextClass(fans);
  283. const sextag = await getSexTag(uid);
  284. un.dataset.infoline = `${sextag} ${txt} `;
  285. un.classList.add(cls);
  286. };
  287. function 直播间留言者显示粉丝数 () {
  288. launchObserver({
  289. parentNode,
  290. selector,
  291. successCallback: () => {
  292. for (const un of $$(`#chat-items .user-name`)) {
  293. if (un.classList.contains('infoline')) {
  294. continue;
  295. }
  296. append(un);
  297. }
  298. },
  299. stopWhenSuccess: false,
  300. });
  301. }
  302.  
  303. function 动态井号标签 () {
  304. launchObserver({
  305. parentNode: document.body,
  306. selector: `a.dynamic-link-hover-bg`,
  307. successCallback: () => {
  308. for (let link of $$(`a.dynamic-link-hover-bg`)) {
  309. // link: HTMLAnchorElement
  310. if (/#.+#/.exec(link.innerHTML) && /https?:\/\/search.bilibili.com\/all\?.+/.exec(link.href)) {
  311. link.href = `https://t.bilibili.com/topic/name/${/#(.+)#/.exec(link.innerHTML)[1]}/feed`;
  312. }
  313. }
  314. },
  315. stopWhenSuccess: false,
  316. });
  317. }
  318.  
  319. function 直播间 () {
  320. 关注栏尺寸();
  321. 直播间标题();
  322. 直播间留言者显示粉丝数();
  323. 通用表情框尺寸修复();
  324. 动态井号标签();
  325. }
  326.  
  327. function 直播主页 () {
  328. 关注栏尺寸();
  329. }
  330.  
  331. function 其他页面 () {
  332. 动态井号标签();
  333. }
  334.  
  335. if (location.pathname === '/') {
  336. 直播主页();
  337. } else if (location.pathname === '/p/eden/area-tags') {
  338. 分区();
  339. } else if (/^\/\d+$/.exec(location.pathname)) {
  340. 直播间();
  341. } else {
  342. 其他页面();
  343. }
  344.  
  345. })();