【小破站必备2022】 哔哩哔哩(bilibili|B站)自动增强--功能快捷键,视频智能解析,每日任务等

🔥🔥🔥推荐! 浸入式虚拟会员体验,功能智能自动化,让你的 B站 比别人的更强。自动跳转多 P 视频(UP 上传视频)上次观看进度,快捷键增强,每日任务(签到&分享),会员番剧无感解析,视频已看标签等等,具体看脚本介绍~

  1. // ==UserScript==
  2. // @name 【小破站必备2022】 哔哩哔哩(bilibili|B站)自动增强--功能快捷键,视频智能解析,每日任务等
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.23
  5. // @icon https://cdn.jsdelivr.net/gh/Anjude/pubsrc@img/1.png
  6. // @description 🔥🔥🔥推荐! 浸入式虚拟会员体验,功能智能自动化,让你的 B站 比别人的更强。自动跳转多 P 视频(UP 上传视频)上次观看进度,快捷键增强,每日任务(签到&分享),会员番剧无感解析,视频已看标签等等,具体看脚本介绍~
  7. // @author anjude
  8. // @match https://*.bilibili.com/*
  9. // @grant GM_openInTab
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @grant GM_deleteValue
  13. // @grant GM_addStyle
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_xmlhttpRequest
  17. // @original-script https://github.com/Anjude/tampermonkey
  18. // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js
  19. // @require https://greasyfork.org/scripts/412159-mydrag/code/MyDrag.js?version=858320
  20. // ==/UserScript==
  21.  
  22. (function () {
  23. "use strict";
  24. // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js
  25. // 检查版本
  26. const RELEASE_VERSION = "0.0.23";
  27. let ENV = "RELEASE";
  28. // ENV = 'DEBUG'
  29. const updateVersion =
  30. ENV === "DEBUG" || RELEASE_VERSION !== GM_getValue("RELEASE_VERSION");
  31. updateVersion && GM_setValue("RELEASE_VERSION", RELEASE_VERSION);
  32. startHttpProxy();
  33. /**
  34. * 默认设置
  35. * `${e.altKey}${e.ctrlKey}${e.shiftKey}${pressKey}`
  36. */
  37. let defaultBili2sConf = {
  38. shortcutMap: {
  39. upToTop: "000U", // 回到顶部
  40. takeNote: "000N", // 打开视频笔记
  41. lightOff: "000L", // 开关宽屏模式
  42. notePicShot: "101P", // 笔记-视频截图
  43. noteTimePoint: "101T", // 笔记-时间标记
  44. changeParseApi: "100V", // 解锁视频
  45. showMenu: "100M", // 打开菜单
  46. },
  47. videoRecordMap: {}, // 视频记录
  48. multiUnceasing: true, // 多集自动连播
  49. singleUncreasing: false, // 单集自动连播
  50. autoUnlockVideo: false, // 是否自动解锁视频
  51. shareDate: "2022/1/1",
  52. lastClearup: new Date().toLocaleString(),
  53. parseApiIndex: 0, // 解析接口选择
  54. pretendVip: false,
  55. installTime: null,
  56. };
  57.  
  58. // 网站配置
  59. const siteConfig = {
  60. delay2s: 2000,
  61. scrollBtnList: [
  62. "div.item.back-top", // 首页
  63. "button.primary-btn.top-btn", // 新版首页
  64. "div.item.backup", // up视频,
  65. "div.tool-item.backup.iconfont.icon-up", //
  66. "#app > div.to-top", // up主所有视频
  67. "#cheese_guide > div > div", // 课堂
  68. ],
  69. noteBtnList: [
  70. "div.note-btn", // 普通up视频
  71. "span.note-btn", // 课堂视频
  72. ],
  73. notePanelList: [
  74. "div.resizable-component.bili-note", // 普通up视频
  75. ],
  76. lightOffBtn: [
  77. "div.squirtle-single-setting-other-choice.squirtle-lightoff",
  78. "div.bilibili-player-fl.bilibili-player-video-btn-setting-right-others-content-lightoff.bui.bui-checkbox.bui-dark > input",
  79. ],
  80. wideScreenBtn: [
  81. "div.squirtle-widescreen-wrap.squirtle-block-wrap > div", // bangumi 视频
  82. "div.bilibili-player-video-btn.bilibili-player-video-btn-widescreen", // up 视频
  83. ],
  84. videoSettingBtn: [
  85. "div.bilibili-player-video-btn.bilibili-player-video-btn-setting",
  86. ],
  87. picBtnList: ["span.ql-capture-btn"],
  88. pointBtnList: ["span.ql-tag-btn"],
  89. multiPageBox: ["#multi_page > div.cur-list"],
  90. chapListItem: ["div.cur-list > ul > li.on"],
  91. trendBtnList: [
  92. // "div.box-bottom > div > div:nth-child(1)",
  93. "div.share-btns > div:nth-child(1)",
  94. "div.share-info > div > div > span",
  95. ],
  96. shareBtnList: ["div.share-info"],
  97. unceasingBtnList: ["span.switch-button"],
  98. searchResBox: [
  99. "#video-list > ul",
  100. "div.video-list", // 搜索页
  101. "div.video.search-all-list > div", // 搜索页
  102. "div.mixin-list > ul.video-list", // 番剧
  103. "div.flow-loader > ul",
  104. "div.rcmd-box", // 首页推荐
  105. "div.section.video > div.content", // UP主页
  106. "#submit-video-list > ul.list-list", // UP主页,更多视频
  107. "#submit-video-list > ul.cube-list", // UP主页,更多视频
  108. "#page-series-detail > div > div > div > ul" // 专栏视频
  109. // '#reco_list > div.rec-list', // 相关视频
  110. ],
  111. vipIcon: "bili-avatar-icon--big-vip",
  112. vipSpan: [
  113. "div.avatar-container > div > div > span",
  114. "div.big-avatar-container--default > a > div > span",
  115. "a.header-entry-avatar > div > span",
  116. ],
  117. vipLabel: "div.h-vipType",
  118. playerBox: ["#player_module","#bilibili-player"],
  119. videoBox: ["video"],
  120. vipAdClose: ["div.twp-mask > div > i"],
  121. parseApiList: [
  122. // 解析链接均收集自网络,经过简单测试
  123. // https://jx.bozrc.com:4433/player/?url=
  124. // {
  125. // url: "https://vip.parwix.com:4433/player/?url=",
  126. // name: "Parwix解析系统",
  127. // },
  128. { url: "https://jx.bozrc.com:4433/player/?url=", name: "夜幕解析" },
  129. { url: "https://z1.m1907.cn/?jx=", name: "m1907" },
  130. { url: "https://yparse.jn1.cc/index.php?url=", name: "云解析" },
  131. // { url: "https://www.yemu.xyz/?url=", name: "夜幕解析" },
  132. // { url: "https://vip.bljiex.cc/?v=", name: "BL解析" },
  133. // { url: "https://vip.mmkv.cn/tv.php?url=", name: "mmkv" },
  134. // { url: "https://vip5.jiexi.one/?url=", name: "爱爱蓝光解析" },
  135. // { url: 'https://www.1717yun.com/jx/ty.php?url=', name: '1717云解析' },
  136. // { url: 'https://jx.rdhk.net/?v=', name: '4080视频解析' },
  137. // { url: 'https://go.yh0523.cn/y.cy?url=', name: '盘古云解析' },
  138. // { url: 'https://17kyun.com/api.php?url=', name: '17kyun' },
  139. // { url: 'https://lecurl.cn/?url=', name: 'dplayer - by-le' },
  140. ],
  141. bangumiLi: ["li.ep-item.cursor.badge.visited","div.numberListItem_number_list_item__wszA4.numberListItem_select__ar1X5"],
  142. watchroomvip: ["#paybar_module > div.vip"],
  143. shortcutList: {
  144. upToTop: "回到顶部",
  145. takeNote: "打开/关闭笔记",
  146. changeParseApi: "切换视频解析接口",
  147. showMenu: "打开菜单",
  148. notePicShot: "笔记-视频截图",
  149. noteTimePoint: "笔记-时间标志",
  150. lightOff: "开关宽屏模式",
  151. }, // shortcut list
  152. scSetting: "",
  153. };
  154.  
  155. let bili2sConf = GM_getValue("bili2sConf") || defaultBili2sConf;
  156.  
  157. if (updateVersion) {
  158. let shortcutMap = Object.assign({}, defaultBili2sConf.shortcutMap);
  159. bili2sConf = Object.assign(defaultBili2sConf, bili2sConf);
  160. bili2sConf.shortcutMap = Object.assign(shortcutMap, bili2sConf.shortcutMap);
  161. console.log(
  162. shortcutMap,
  163. defaultBili2sConf.shortcutMap,
  164. bili2sConf.shortcutMap
  165. );
  166. GM_setValue("bili2sConf", bili2sConf);
  167. Toast("脚本已更新");
  168. }
  169.  
  170. if (!bili2sConf.installTime) {
  171. bili2sConf.installTime = new Date().toLocaleString();
  172. GM_setValue("bili2sConf", bili2sConf);
  173. alert("首次使用,前往微信小程序,随时反馈!");
  174. window.GM_openInTab(
  175. "https://cdn.jsdelivr.net/gh/Anjude/pubsrc@img/TamperMonkey-InfoFlow.jpg",
  176. { active: true, insert: true, setParent: true }
  177. );
  178. }
  179.  
  180. const delayExecute = (execution, delayMs) => {
  181. setTimeout(() => {
  182. execution();
  183. }, delayMs || siteConfig.delay2s);
  184. };
  185.  
  186. const getElement = (list) => {
  187. if (typeof list === "string") return document.querySelector(list);
  188. let btn = document.querySelector(list[0]);
  189. list.forEach((e) => {
  190. btn = document.querySelector(e) || btn;
  191. });
  192. return btn;
  193. };
  194.  
  195. const getBvid = (href) => {
  196. let res = /video\/([0-9|a-z|A-Z]*)/gi.exec(href || document.location.href);
  197. return res === null ? false : res[1];
  198. };
  199.  
  200. // 改编自 github 网友贡献的代码,详情请参见 github 的提交记录
  201. const LightOff = () => {
  202. let settingBtn = getElement(siteConfig.videoSettingBtn);
  203. settingBtn?.dispatchEvent(new MouseEvent("mouseover"));
  204. settingBtn?.dispatchEvent(new MouseEvent("mouseout"));
  205.  
  206. let wideScreenBtn = getElement(siteConfig.wideScreenBtn);
  207. let lightOffBtn = getElement(siteConfig.lightOffBtn);
  208. let scrollDistance = window.location.href.match("bangumi") ? 50 : 100;
  209.  
  210. wideScreenBtn.click();
  211. lightOffBtn.click();
  212. window.scrollTo(0, scrollDistance);
  213. };
  214.  
  215. const UpToTop = () => {
  216. // 回到顶部
  217. let scrollBtn = getElement(siteConfig.scrollBtnList);
  218. if (scrollBtn) scrollBtn.click();
  219. };
  220.  
  221. const TakeNote = () => {
  222. let noteBtn = getElement(siteConfig.noteBtnList);
  223. let nodePanel = getElement(siteConfig.notePanelList);
  224. let res =
  225. nodePanel ||
  226. (() => {
  227. noteBtn.click();
  228. return false;
  229. })();
  230. if (!res) return;
  231.  
  232. nodePanel.style.display = nodePanel.style.display === "none" ? "" : "none";
  233. };
  234.  
  235. const NotePicShot = () => {
  236. let picBtn = getElement(siteConfig.picBtnList);
  237. picBtn.click();
  238. };
  239.  
  240. const NoteTimePoint = () => {
  241. let pointBtn = getElement(siteConfig.pointBtnList);
  242. pointBtn.click();
  243. };
  244.  
  245. const keyCtrl = () => { };
  246.  
  247. const blockKey = (e) => {
  248. let isBlock = false;
  249.  
  250. // do sth if isBlock should be true
  251.  
  252. return isBlock;
  253. };
  254.  
  255. const setShortcut = (command) => {
  256. let commandString = getShortCut(command);
  257. let scSetting = siteConfig.scSetting;
  258. let innerTextList = document
  259. .querySelector(`#${scSetting}`)
  260. .innerHTML.split(":");
  261. document.querySelector(`#${scSetting}`).innerHTML =
  262. innerTextList[0] + ": " + commandString;
  263. siteConfig.scm[scSetting] = command;
  264. };
  265.  
  266. let focus = false; // 输入中
  267. $(document).ready(() => {
  268. $(document).delegate("input, textarea", "focus", () => {
  269. focus = true;
  270. });
  271. $(document).delegate("input, textarea", "blur", () => {
  272. focus = false;
  273. });
  274. $(document).keydown((e) => {
  275. // 如果正在打字或者特殊情况,屏蔽快捷键
  276. if (!e.altKey && !e.shiftKey && !e.ctrlKey && (focus || blockKey(e))) {
  277. return;
  278. }
  279. const k = (key) => (key ? 1 : 0);
  280. let pressKey = String.fromCharCode(e.keyCode);
  281. let command = `${k(e.altKey)}${k(e.ctrlKey)}${k(e.shiftKey)}${pressKey}`;
  282. let keyMap = bili2sConf.shortcutMap;
  283.  
  284. // console.log('键盘:', command, siteConfig.scSetting)
  285. if (siteConfig.scSetting) {
  286. return setShortcut(command);
  287. }
  288. switch (command) {
  289. case keyMap.upToTop:
  290. return UpToTop();
  291. case keyMap.lightOff:
  292. return LightOff();
  293. case keyMap.takeNote:
  294. return TakeNote();
  295. case keyMap.changeParseApi:
  296. return ChangeParseApi();
  297. case keyMap.showMenu:
  298. return (document.querySelector("#sc-box").style.display = "");
  299. case keyMap.notePicShot:
  300. return NotePicShot();
  301. case keyMap.noteTimePoint:
  302. return NoteTimePoint();
  303. default:
  304. keyCtrl(command); // 一些不常用的小操作,集中一个函数处理
  305. }
  306. });
  307. });
  308.  
  309. const chapListener = (res) => {
  310. let listItem = getElement(siteConfig.chapListItem)?.innerHTML;
  311. if (!listItem) {
  312. console.log("非多集视频");
  313. return;
  314. }
  315. let regxList = /video\/([0-9a-zA-Z]*)\?p=(\d+).*title=.(.*?).><div/i.exec(
  316. listItem
  317. );
  318. let bvid = regxList[1];
  319. bili2sConf.videoRecordMap[bvid] = Object.assign(
  320. bili2sConf.videoRecordMap[bvid] || {},
  321. {
  322. p: regxList[2],
  323. title: regxList[3],
  324. updateTime: new Date().toLocaleString(),
  325. }
  326. );
  327. GM_setValue("bili2sConf", bili2sConf);
  328. console.log("[B站小助手]: 记录成功!");
  329. };
  330.  
  331. const multiPageJump = async () => {
  332. let bvid = getBvid();
  333. let videoHis = bili2sConf.videoRecordMap[bvid];
  334. videoHis &&
  335. (() => {
  336. let hrefRegexp = new RegExp(`${bvid}\\?p=\\d+`, "i");
  337. if (hrefRegexp.test(window.location.href)) {
  338. return;
  339. }
  340. let curChapLi = document.querySelector(
  341. `div.cur-list > ul > li:nth-child(${videoHis.p}) > a > div`
  342. );
  343. if (!curChapLi) {
  344. return delayExecute(multiPageJump);
  345. }
  346. curChapLi.click();
  347. Toast(`小助手: 跳转上次观看 P${videoHis.p}`);
  348. })();
  349. };
  350.  
  351. const setVideoRecord = () => {
  352. let bvid = getBvid();
  353. let videoRecord = bili2sConf.videoRecordMap[bvid] || {
  354. docTitle: document.title,
  355. p: 1,
  356. };
  357. videoRecord.updateTime = new Date().toLocaleString();
  358. bili2sConf.videoRecordMap[bvid] = Object.assign(
  359. bili2sConf.videoRecordMap[bvid] || {},
  360. videoRecord
  361. );
  362. // console.log(bili2sConf.videoRecordMap[bvid], videoRecord)
  363. GM_setValue("bili2sConf", bili2sConf);
  364. };
  365.  
  366. const dealUnceasing = (isMultiPage) => {
  367. // 处理连播
  368. let switchCase = isMultiPage ? "multiUnceasing" : "singleUncreasing";
  369. let unceasingBtn = getElement(siteConfig.unceasingBtnList);
  370. if (!unceasingBtn) {
  371. return delayExecute(dealUnceasing);
  372. }
  373. let curUnceasing = /switch-button on/.test(
  374. unceasingBtn.getAttribute("class")
  375. );
  376. curUnceasing === bili2sConf[switchCase] || unceasingBtn.click();
  377. unceasingBtn.addEventListener("click", (e) => {
  378. // 过滤脚本模拟点击
  379. if (e.isTrusted) {
  380. bili2sConf[switchCase] = !/switch-button on/.test(
  381. unceasingBtn.getAttribute("class")
  382. );
  383. GM_setValue("bili2sConf", bili2sConf);
  384. }
  385. });
  386. };
  387.  
  388. const closeBtn = ()=>{
  389. let closeBtn = $("#app-container > div > div > div.close");
  390. if (closeBtn){
  391. closeBtn.click();
  392. return true;
  393. }
  394. };
  395.  
  396. const doShare = () => {
  397. console.log("[B站小助手]: 开始分享!");
  398. // let shareBtn = getElement(siteConfig.shareBtnList);
  399.  
  400. // console.log(111, shareBtn);
  401. // shareBtn?.dispatchEvent(new MouseEvent("mouseover"));
  402.  
  403. let trendBtn = getElement(siteConfig.trendBtnList);
  404. if (!trendBtn) {
  405. return delayExecute(doShare);
  406. }
  407. trendBtn.click();
  408. let tryTimes = 0;
  409. let interval = setInterval(() =>{
  410. // console.log(tryTimes);
  411. if (closeBtn() || tryTimes > 30){
  412. clearInterval(interval);
  413. console.log("[B站小助手]: 分享完成!");
  414. return;
  415. }
  416. tryTimes++;
  417. },10);
  418. // shareBtn?.dispatchEvent(new MouseEvent("mouseout"));
  419. bili2sConf.shareDate = new Date().toLocaleDateString();
  420. GM_setValue("bili2sConf", bili2sConf);
  421. Toast("小助手: 今日分享任务达成");
  422. };
  423.  
  424. const dealRead = (res) => {
  425. siteConfig.searchResBox.forEach((boxPath) => {
  426. let searchResBox = getElement(boxPath);
  427. console.log(searchResBox, boxPath)
  428. searchResBox &&
  429. searchResBox.childNodes.forEach((e) => {
  430. if (!e.innerHTML) return;
  431. e.style.position = "relative";
  432. let bvid = getBvid(e.innerHTML);
  433. if (!bvid) return;
  434. let addDiv = document.createElement("div");
  435. addDiv.className = "video-view";
  436. if (bili2sConf.videoRecordMap[bvid]) {
  437. addDiv.innerHTML = "已看";
  438. addDiv.style.opacity = 0.9;
  439. addDiv.style.color = "red";
  440. } else {
  441. // addDiv.innerHTML = "未看";
  442. }
  443. e.prepend(addDiv);
  444. });
  445. });
  446. };
  447.  
  448. const ChangeParseApi = () => {
  449. let curIndex = bili2sConf.parseApiIndex;
  450. bili2sConf.parseApiIndex = (curIndex + 1) % siteConfig.parseApiList.length;
  451. UnlockBangumi(bili2sConf.parseApiIndex, false, true);
  452. GM_setValue("bili2sConf", bili2sConf);
  453. Toast(
  454. `B站小助手: 解析接口${bili2sConf.parseApiIndex + 1} ${siteConfig.parseApiList[bili2sConf.parseApiIndex].name
  455. }`
  456. );
  457. };
  458.  
  459. const UnlockBangumi = (parseApiIndex = 0, setAutoUnlock, forceUnlock) => {
  460. console.log("11");
  461. if (setAutoUnlock) {
  462. let set = !bili2sConf.autoUnlockVideo;
  463. bili2sConf.autoUnlockVideo = set;
  464. GM_setValue("bili2sConf", bili2sConf);
  465. Toast(`B站小助手:${set ? "开启" : "关闭"}自动解锁!`);
  466. }
  467. parseApiIndex %= siteConfig.parseApiList.length;
  468. let videoInfo = getElement(siteConfig.bangumiLi)?.innerHTML;
  469. let href = window.location.href
  470. // 观看室独立逻辑
  471. if (/bilibili.com\/watchroom/.test(href)) {
  472. href = document.referrer
  473. videoInfo = getElement(siteConfig.watchroomvip)?.innerHTML
  474. }
  475. if (!forceUnlock) {
  476. if (!bili2sConf.autoUnlockVideo || (videoInfo && !/(会员|付费|受限)/.test(videoInfo)) || !videoInfo) {
  477. return $("#anjude-iframe").length && location.reload();
  478. }
  479. }
  480.  
  481. let parseApi = siteConfig.parseApiList[parseApiIndex];
  482. let newPlayer = document.createElement("iframe");
  483. newPlayer.id = "anjude-iframe";
  484. newPlayer.height = "100%";
  485. newPlayer.width = "100%";
  486. newPlayer.src = parseApi.url + href;
  487. newPlayer.setAttribute("allow", "autoplay");
  488. newPlayer.setAttribute("frameborder", "no");
  489. newPlayer.setAttribute("border", "0");
  490. newPlayer.setAttribute("allowfullscreen", "true");
  491. newPlayer.setAttribute("webkitallowfullscreen", "webkitallowfullscreen");
  492.  
  493. let playerBox = getElement(siteConfig.playerBox);
  494. let videoBox = getElement(siteConfig.videoBox);
  495.  
  496. if (videoBox) {
  497. videoBox.muted = true;
  498. videoBox.pause();
  499. }
  500.  
  501. playerBox.innerHTML = "";
  502. playerBox.append(newPlayer);
  503.  
  504. let monitorTimes = 0;
  505. let vipAdMonitor = setInterval(() => {
  506. monitorTimes++;
  507. let closeAd = getElement(siteConfig.vipAdClose);
  508. if (closeAd || monitorTimes >= (5 * 60 * 1000) / 200) {
  509. closeAd.click();
  510. clearInterval(vipAdMonitor);
  511. }
  512. }, 200);
  513. Toast(`B站小助手: 解析完成`, 500);
  514. };
  515.  
  516. const pretendVip = () => {
  517. siteConfig.vipSpan.forEach((e) => {
  518. let vipSpan = getElement(e);
  519. vipSpan && vipSpan.classList.add(siteConfig.vipIcon);
  520. });
  521. let vipLabel = getElement(siteConfig.vipLabel);
  522. if (vipLabel) {
  523. let newClass = vipLabel.getAttribute("class").replace("disable", "");
  524. vipLabel.setAttribute("class", newClass);
  525. }
  526. };
  527.  
  528. const executeByUri = (responseURL, result) => {
  529. // console.log(responseURL);
  530. (/x\/web-interface\/archive\/desc2/.test(responseURL)) && chapListener(result);
  531. (/x\/web-interface\/search/.test(responseURL) ||
  532. /x\/web-interface\/index\/top\/rcmd/.test(responseURL) ||
  533. /x\/series\/archives/.test(responseURL) ||
  534. /x\/space\/arc/.test(responseURL)) &&
  535. dealRead(result);
  536. (/pgc\/view\/web\/section\/order/.test(responseURL) ||
  537. /pgc\/view\/web\/season/.test(responseURL) ||
  538. /pgc\/season\/episode\/web\/info/.test(responseURL)) &&
  539. UnlockBangumi(bili2sConf.parseApiIndex);
  540. };
  541.  
  542. const runScript = () => {
  543. let date = new Date().toLocaleDateString();
  544. let href = window.location.href;
  545. let isMultiPage = getElement(siteConfig.multiPageBox);
  546. if (isMultiPage) {
  547. multiPageJump();
  548. }
  549. if (/\/video\//.test(href)) {
  550. setVideoRecord();
  551. dealUnceasing(isMultiPage);
  552. dealRead();
  553. date === bili2sConf.shareDate || doShare();
  554. }
  555. if (/bilibili.com\/bangumi/.test(href)) {
  556. addParseBtn();
  557. }
  558. if (/bilibili.com\/watchroom/.test(href)) {
  559. UnlockBangumi(bili2sConf.parseApiIndex)
  560. }
  561. if (/search.bilibili.com/.test(href)) {
  562. dealRead();
  563. }
  564. bili2sConf.pretendVip && pretendVip();
  565. };
  566.  
  567. // 执行脚本
  568. try {
  569. console.log('[B站小助手]:', bili2sConf)
  570. GM_addStyle(getCss());
  571. setCommand();
  572. setTimeout(() => {
  573. runScript();
  574. }, siteConfig.delay2s);
  575. clearupStore();
  576. } catch (err) {
  577. console.log("[B站小助手]:", err.name, err.message);
  578. if (confirm(`【B站小助手】: 请截图(到 我的 - 客服 处)反馈 ${err}`)) {
  579. window.GM_openInTab(
  580. "https://cdn.jsdelivr.net/gh/Anjude/pubsrc@img/TamperMonkey-InfoFlow.jpg",
  581. { active: true, insert: true, setParent: true }
  582. );
  583. }
  584. }
  585.  
  586. function resetScript() {
  587. GM_deleteValue("bili2sConf");
  588. }
  589.  
  590. function helper() {
  591. let str = ``;
  592. let list = str.match(/https?:\/\/[0-9a-zA-Z./?_-]*=/gi);
  593. list.forEach((e, i) => {
  594. setTimeout(() => {
  595. console.log(i, i === list.length - 1);
  596. window.open(
  597. `${e}https://www.bilibili.com/bangumi/play/ep457778?spm_id_from=333.999.0.0`,
  598. "target"
  599. );
  600. }, i * 15000);
  601. });
  602. }
  603.  
  604. function clearupStore() {
  605. const getDayDiff = (d) => {
  606. return (new Date() - new Date(d)) / (1000 * 60 * 60 * 24);
  607. };
  608. let dayDiff = getDayDiff(bili2sConf.lastClearup);
  609. if (dayDiff < 30) return; // 每月清理一次数据
  610. console.log("[B站小助手]:开始清理!");
  611.  
  612. let recordMapKeys = Object.keys(bili2sConf.videoRecordMap);
  613. recordMapKeys.forEach((e) => {
  614. let updateTime = bili2sConf.videoRecordMap[e].updateTime;
  615. if (getDayDiff(updateTime) > 365 * 2) {
  616. delete bili2sConf.videoRecordMap[e];
  617. }
  618. });
  619. bili2sConf.lastClearup = new Date().toLocaleString();
  620. GM_setValue("bili2sConf", bili2sConf);
  621. }
  622.  
  623. function startHttpProxy() {
  624. XMLHttpRequest.prototype.send = new Proxy(XMLHttpRequest.prototype.send, {
  625. apply: (target, thisArg, args) => {
  626. thisArg.addEventListener("load", (event) => {
  627. try {
  628. // console.log(111, event.target.responseURL)
  629. let { responseText, responseURL } = event.target;
  630. if (/^{.*}$/.test(responseText)) {
  631. const result = JSON.parse(responseText);
  632. executeByUri(responseURL, result);
  633. }
  634. } catch (err) { }
  635. });
  636. return target.apply(thisArg, args);
  637. },
  638. });
  639. }
  640.  
  641. function addParseBtn() {
  642. let ele = $(`
  643. <div id="anjude-parse" class="mobile-info toolbar_watch_info__KslMm toolbar_item_info__xpKhw">
  644. <i class="iconfont icon-play"></i>
  645. <span>解析</span>
  646. </div>
  647. `);
  648. $("div.toolbar").append(ele);
  649. document
  650. .querySelector("#anjude-parse")
  651. .addEventListener("click", ChangeParseApi);
  652. }
  653.  
  654. function Toast(message = "已完成", time = 2000) {
  655. /*设置信息框停留的默认时间*/
  656. let el = document.createElement("div");
  657. el.setAttribute("class", "web-toast");
  658. el.innerHTML = message;
  659. document.body.appendChild(el);
  660. el.classList.add("fadeIn");
  661. setTimeout(() => {
  662. el.classList.remove("fadeIn");
  663. el.classList.add("fadeOut");
  664. /*监听动画结束,移除提示信息元素*/
  665. el.addEventListener("animationend", () => {
  666. document.body.removeChild(el);
  667. });
  668. el.addEventListener("webkitAnimationEnd", () => {
  669. document.body.removeChild(el);
  670. });
  671. }, time);
  672. }
  673.  
  674. function getShortCut(command) {
  675. // console.log(command);
  676. let res = "";
  677. if (parseInt(command[0])) res += "Alt+";
  678. if (parseInt(command[1])) res += "Ctrl+";
  679. if (parseInt(command[2])) res += "Shift+";
  680. res += command[3];
  681. return res;
  682. }
  683.  
  684. function clearCommandStatus(SL) {
  685. SL.forEach((e) => {
  686. document.querySelector(`#${e}`).style.color = "";
  687. });
  688. }
  689.  
  690. function setCommand() {
  691. initSettingPanel();
  692. GM_registerMenuCommand("设置脚本", () => {
  693. document.querySelector("#sc-box").style.display = "";
  694. });
  695. GM_registerMenuCommand("重置脚本", () => {
  696. if (confirm("重置后观看记录、快捷键修改等数据将清空!")) {
  697. resetScript();
  698. }
  699. });
  700. }
  701.  
  702. function initSettingPanel() {
  703. let SCL = siteConfig.shortcutList;
  704. siteConfig.scm = bili2sConf.shortcutMap;
  705. let scItem = "";
  706. Object.keys(SCL).forEach((e) => {
  707. scItem += `<div id="${e}">${SCL[e]}快捷键: ${getShortCut(
  708. siteConfig.scm[e]
  709. )}</div>`;
  710. });
  711. let boxHtml = $(`
  712. <div id="sc-box" style="display:none;width:300px;">
  713. <div id="sc-title" style="width: 100%;height: 20px;
  714. text-align: center;font-size: 16px;padding: 20px;">
  715. 快捷键设置(点击选中设置)
  716. </div>
  717. <div style="display:flex; font-size: 15px;flex-direction: column;">
  718. <label>假装是大会员 <input type="checkbox" id="pretend-vip" ${bili2sConf.pretendVip ? "checked" : ""
  719. } /></label>
  720. <label>自动解锁会员视频 <input type="checkbox" id="auto-unlockvideo" ${bili2sConf.autoUnlockVideo ? "checked" : ""
  721. } /></label>
  722. </div>
  723. <div style="font-size: 15px;">
  724. ${scItem}
  725. </div>
  726. <div style="justify-content:center; display: flex; padding: 10px;">
  727. <button id="anjude-scok-btn" style="color: white; font-size:16px; border-radius: 2px;
  728. background: green;padding: 3px;">设置完成</button>
  729. </div>
  730. <a style="font-size: 12px; color: blue;" target="_blank" href="https://greasyfork.org/zh-CN/scripts/437941/feedback">好用的话,去给个好评咯~</a>
  731. <a id="badguy" style="font-size: 12px; color: red;margin-left: 10px;">烂脚本,我要差评!</a>
  732. <img id="miniprogram" style="display: none;" src="https://cdn.jsdelivr.net/gh/Anjude/pubsrc@img/TamperMonkey-InfoFlow.jpg">
  733. </div>
  734. `);
  735. $(document.body).append(boxHtml);
  736. try {
  737. new MyDrag($("#sc-box")[0], { handle: $("#sc-title")[0] });
  738. } catch (err) {
  739. console.log(err);
  740. return
  741. }
  742. Object.keys(SCL).forEach((v) => {
  743. document.querySelector(`#${v}`).addEventListener("click", function (e) {
  744. siteConfig.scSetting = this.id;
  745. clearCommandStatus(Object.keys(SCL));
  746. this.style.color = "green";
  747. });
  748. });
  749. document
  750. .querySelector("#anjude-scok-btn")
  751. .addEventListener("click", function (e) {
  752. // 设置快捷键,缓存数据
  753. siteConfig.scSetting = "";
  754. bili2sConf.shortcutMap = siteConfig.scm;
  755. GM_setValue("bili2sConf", bili2sConf);
  756. document.querySelector("#sc-box").style.display = "none";
  757. });
  758. document
  759. .querySelector("#auto-unlockvideo")
  760. .addEventListener("click", function (e) {
  761. UnlockBangumi(bili2sConf.parseApiIndex, true);
  762. });
  763. document
  764. .querySelector("#pretend-vip")
  765. .addEventListener("click", function (e) {
  766. bili2sConf.pretendVip = !bili2sConf.pretendVip;
  767. GM_setValue("bili2sConf", bili2sConf);
  768. Toast("小助手: 刷新页面后生效");
  769. });
  770. document.querySelector("#badguy").addEventListener("click", function (e) {
  771. let cur = document.querySelector("#miniprogram").style.display;
  772. document.querySelector("#miniprogram").style.display = cur ? "" : "none";
  773. });
  774. updateVersion && (document.querySelector("#sc-box").style.display = "");
  775. }
  776.  
  777. function getCss() {
  778. return `
  779. #anjude-parse{
  780. color: orange;
  781. margin-left: 20px;
  782. }
  783. a{text-decoration:none;}
  784. #pretend-vip,
  785. #auto-unlockvideo{
  786. background-color: initial;
  787. cursor: default;
  788. appearance: checkbox;
  789. box-sizing: border-box;
  790. padding: initial;
  791. border: initial;
  792. }
  793. #sc-box{
  794. padding: 10px;border-radius: 5px;
  795. background: #F6F6F6;border: #44b549 2px solid;
  796. }
  797. .video-view{
  798. display:inline-block;
  799. position:absolute;
  800. left:0px; top:0px;
  801. background:#FFF; color:#666;
  802. opacity: 0.8; padding:1px 5px;
  803. z-index:999;
  804. }
  805. @keyframes fadeIn {
  806. 0% {opacity: 0}
  807. 100% {opacity: 1}
  808. }
  809. @keyframes fadeOut {
  810. 0% {opacity: 1}
  811. 100% {opacity: 0}
  812. }
  813. .web-toast{
  814. position: fixed;
  815. background: rgba(0, 0, 0, 0.7);
  816. color: #fff;
  817. font-size: 14px;
  818. line-height: 1;
  819. padding:10px;
  820. border-radius: 3px;
  821. left: 50%;
  822. top: 50%;
  823. transform: translate(-50%,-50%);
  824. z-index: 9999;
  825. white-space: nowrap;
  826. }
  827. .fadeOut{
  828. animation: fadeOut .5s;
  829. }
  830. .fadeIn{
  831. animation:fadeIn .5s;
  832. }
  833. `;
  834. }
  835. })();