您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增强视频播放观感, 提升学习效率
// ==UserScript== // @name 哔哩哔哩(bilibili | B站) 视频播放速度调整器 // @namespace http://tampermonkey.net/ // @version 0.16 // @description 增强视频播放观感, 提升学习效率 // @author WindOfCast // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js // @include *://www.bilibili.com/video/* // @include *//www.bilibili.com/medialist/play/watchlater/* // @include *//www.bilibili.com/list/watchlater* // @include *//www.bilibili.com/list/* // @icon data:image/png;base64,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 // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_notification // @homepage https://gitee.com/windofcast/ // @website https://space.bilibili.com/367207503 // @license AGPL License // ==/UserScript== /* * @since 0.5 添加快捷键控制 * - 添加快捷键控制框架 * - 快捷键_removeCurrentVideo * - 快捷键_showShortcutKeyMenu * @since 0.6 添加添加快捷键校验 * - 添加快捷键校验 * - 设置快捷键成功与否都有提示通知 * @since 0.7 更新 B站 稍后再看 播放页 * - 添加新的 B站 url 请求地址 *\//www.bilibili.com/list/watchlater* * - 替换之前速度菜单列表点击目标 * @since 0.8 watchlater 中添加播放历史记录 与 添加删除已播放快捷键 与 自动点赞 * - 添加播放历史记录 * - 修改快捷键配置格式 * - 添加删除已播放快捷键 (替换 删除当前视频) * - 自动点赞 * @since 0.9 修复 自动点赞 bug 评论全点? 并降低重试频率 * @since 0.10 修复 自动点赞 bug 修改为至多两次 * @since 0.11 修复 自动点赞 bug 匹配 video like * @since 0.12 修复 自动点赞 bug 修改延时点赞逻辑 * @since 0.13 修复 删除已播放快捷键 bug 多版本多播放页 * @since 0.14 修复 播放历史记录 中获取当前bvid * @since 0.15 修复 播放历史记录 中加载videoInfo 中历史 preVideoInfo 非空判断 * * */ (function () { 'use strict'; function commonFunc() { this.video = () => { return document.querySelector("video") } this.GMGetValue = (name, value = null) => { let storageValue = value; if (typeof GM_getValue === 'function') { storageValue = GM_getValue(name, value) } else { let o = window.localStorage.getItem(name); if (o != null) { storageValue = o; } } return storageValue; } this.GMSetValue = (name, value) => { if (typeof GM_setValue === 'function') { GM_setValue(name, value) } else { window.localStorage.setItem(name, value) } } this.GMAddStyle = (css) => { let ms = document.createElement("style"); ms.textContent = css; let doc = document.head || document.documentElement; doc.append(ms) } this.addCommonHtmlCss = () => { let commonCss = ` input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none !important; margin: 0; } input[type="number"] { -moz-appearance: textfield; } input[type='text'] { outline: none; border: 1px solid #a9a9a9; }`; this.GMAddStyle(commonCss); } this.appendHtml = (htmlText) => { let html = $(htmlText); $(document.body).append(html); } this.randomNumber = () => { return Math.ceil(Math.random() * 100000000); } this.createElementDiv = () => { return document.createElement('div'); } this.getShortcutKey = (cmd) => { let shortcutKeyReg = /^(?<group>[01]{3})(?<key>.+)$/g; // 100F let {group, key} = shortcutKeyReg.exec(cmd).groups; let mod = parseInt(group, 2); return Modifier.toString(mod) + key; } this.notification = (messageObj) => { GM_notification({ title: messageObj.title, text: messageObj.message, timeout: messageObj.timeout || 3000, }); } this.moduleInfo = (title, iptType, ...iptAttrs) => { return {title: title, iptType: iptType, iptAttrs: iptAttrs}; } this.moduleInfoAttr = (iptAttrs) => { let result = ""; if (iptAttrs === undefined || iptAttrs.length === 0) { return result; } iptAttrs.forEach((item) => { if (item.indexOf(":") > -1) { let kvs = item.split(":"); result += ` ${kvs[0]}="${kvs[1]}"`; } else { result += " " + item; } }) return result; } this.moduleAutoLike = (isReady = false) => { if (!this.autoLikeFlag || isReady) { console.log("autoLike") this.autoLikeFlag = true; setTimeout(() => { let btnVideoLike = $("div[class*=video-like],span[class^=like]"); if (!btnVideoLike.hasClass("on")) { btnVideoLike.click(); } this.autoLikeFlag = false; }, 5000); } } this.loadVideoInfo = () => { let currentVideoInfo; let isVUE; try { isVUE = window.parent.unsafeWindow.__VUE__; } catch (e) { isVUE = false; } if (isVUE) { currentVideoInfo = this.__loadVueVideoInfo(); } else { currentVideoInfo = this.__loadDefaultVideoInfo(); } // todo 更新数据 let preVideoInfo = currentConfig.historyVideoInfo; if (preVideoInfo != null) { Object.keys(currentVideoInfo).forEach((cviKey) => { let currentVideoItem = currentVideoInfo[cviKey], preVideoItem = preVideoInfo[cviKey]; if (preVideoItem === undefined) { return; } let newObj = { index: currentVideoItem.index, bv_id: cviKey, title: currentVideoItem.title } delete preVideoItem.index; delete preVideoItem.bv_id; let pKeys = Object.keys(preVideoItem); if (pKeys.length > 0) { pKeys.forEach((pk) => { if (currentVideoItem[pk] === undefined) { newObj[pk] = preVideoItem[pk]; } }) } currentVideoInfo[cviKey] = newObj; }) } let videoInfo = $.extend(true, {}, currentVideoInfo); currentConfig.historyVideoInfo = videoInfo; return videoInfo; } this.__loadVueVideoInfo = () => { let currentVideoInfo = {}; $(".action-list-item").each((i, e) => { let info = e["__vue__"]["info"]; currentVideoInfo[info.bv_id] = this.__createVideoInfo(i, info.bv_id, info.title); }) return currentVideoInfo; } this.__loadDefaultVideoInfo = () => { let currentVideoInfo = {}; $(".player-auxiliary-playlist-item").each((i, e) => { let bv_id = $(e).attr("data-bvid"); let title = $(e).find(".player-auxiliary-playlist-item-title").attr("title"); currentVideoInfo[bv_id] = this.__createVideoInfo(i, bv_id, title); }) return currentVideoInfo; } this.__createVideoInfo = (index, bv_id, title) => { return {index: index, bv_id: bv_id, title: title} }; this.KeyMap = { 8: "BackSpace", 27: "Esc", 32: "Spacebar", 33: "Page Up", 34: "Page Down", 35: "End", 36: "Home", 37: "Left Arrow", 38: "Up Arrow", 39: "Right Arrow", 40: "Dw Arrow", 45: "Insert", 46: "Delete", 48: "0", 49: "1", 50: "2", 51: "3", 52: "4", 53: "5", 54: "6", 55: "7", 56: "8", 57: "9", 65: "A", 66: "B", 67: "C", 68: "D", 69: "E", 70: "F", 71: "G", 72: "H", 73: "I", 74: "J", 75: "K", 76: "L", 77: "M", 78: "N", 79: "O", 80: "P", 81: "Q", 82: "R", 83: "S", 84: "T", 85: "U", 86: "V", 87: "W", 88: "X", 89: "Y", 90: "Z", 112: "F1", 113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6", 118: "F7", 119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12", 186: ";", 187: "等号", 188: "逗号", 189: "减号", 190: ".>", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "引号", } this.moduleList = { // removeCurrentVideo: this.moduleInfo("移除当前视频/关闭当前视频单页快捷键:", "text", "readonly"), showModuleMenu: this.moduleInfo("设置快捷键菜单快捷键:", "text", "readonly"), removeHistoryVideo: this.moduleInfo("移除已播放视频快捷键:", "text", "readonly"), autoLike: this.moduleInfo("自动点赞", "checkbox"), }; this.commSettings = ""; this.autoLikeFlag = false; } const commonFuncObj = new commonFunc(); commonFuncObj.addCommonHtmlCss(); // alt ctrl shift const defaultBiliBiliConfig = { shortcutKeyMap: { /** * @deprecated */ removeCurrentVideo: "110F", showModuleMenu: "011M", removeHistoryVideo: "110H" }, moduleMap: { autoLike: false, } } let currentConfig = commonFuncObj.GMGetValue("bilibili_current_config") || defaultBiliBiliConfig; class Modifier { static isAlt(mod) { return (mod & Modifier.ALT) !== 0; } static isCtrl(mod) { return (mod & Modifier.CTRL) !== 0; } static isShift(mod) { return (mod & Modifier.SHIFT) !== 0; } static toString(mod) { let sb = ""; let len; if (Modifier.isAlt(mod)) sb += "Alt + "; if (Modifier.isCtrl(mod)) sb += "Ctrl + "; if (Modifier.isShift(mod)) sb += "Shift + "; // if ((len = sb.length) > 0) { // return sb.substring(0, len - 1); // } // return ""; return sb; } static get ALT() { return 0x4; } static get CTRL() { return 0x2; } static get SHIFT() { return 0x1; } } /** * * @href: https://www.cnblogs.com/jianglijs/p/14682059.html * @author: 天长地久-无为 */ class Enumerable { static closeEnum() { const enumKeys = [] const enumValues = [] // Traverse the enum entries for (const [key, value] of Object.entries(this)) { enumKeys.push(key) value.enumKey = key value.enumOrdinal = enumValues.length enumValues.push(value) } // Important: only add more static properties *after* processing the enum entries this.enumKeys = enumKeys this.enumValues = enumValues // TODO: prevent instantiation now. Freeze `this`? } /** Use case: parsing enum values */ static enumValueOf(str) { const index = this.enumKeys.indexOf(str) if (index >= 0) { return this.enumValues[index] } return undefined } static [Symbol.iterator]() { return this.enumValues[Symbol.iterator]() } toString() { return this.constructor.name + '.' + this.enumKey } } const popup = (function () { class Popup { constructor() { this.mask = commonFuncObj.createElementDiv(); this.content = commonFuncObj.createElementDiv(); this.setStyle(this.mask, { 'width': '100%', 'height': '100%', 'background-color': 'rgba(0, 0, 0, 0.65)', 'position': 'fixed', 'left': '0px', 'top': '0px', 'bottom': '0px', 'right': '0px', 'z-index': '99999999', }); this.setStyle(this.content, { 'max-width': '550px', 'width': '100%', 'max-height': '550px', 'background-color': 'rgb(255, 255, 255)', 'box-shadow': 'rgb(153, 153, 153) 0px 0px 2px', 'position': 'absolute', 'left': '50%', 'top': '50%', 'transform': 'translate(-50%, -50%)', 'border-radius': '3px', }); this.mask.appendChild(this.content); } middleBox(param) { this.content.innerText = ''; // 预清除原内容 let titleStr = '标题'; let obj = {}; // 参数类型检验 if (obj.toString.call(param) === '[object String]') { titleStr = param; } else if (obj.toString.call(param) === '[object Object]') { titleStr = param.title; } // 显示遮罩 document.body.appendChild(this.mask); // 创建 title 组件 this.title = commonFuncObj.createElementDiv(); this.setStyle(this.title, { "width": '100%', "height": '40px', "line-height": '40px', "box-sizing": 'border-box', "background-color": "rgb(255, 77, 64)", "color": 'rgb(255, 255, 255)', "text-align": 'center', "font-weight": "700", "font-size": "16px", }); this.title.innerText = titleStr; this.content.appendChild(this.title); // 关闭 button this.closeBtn = commonFuncObj.createElementDiv(); this.closeBtn.innerText = '×'; this.setStyle(this.closeBtn, { "text-decoration": 'none', "color": 'rgb(255, 255, 255)', "position": 'absolute', "right": '10px', "top": '0px', "font-size": '25px', "display": "inline-block", "cursor": "pointer", }); this.title.appendChild(this.closeBtn); $(this.closeBtn).on('click', () => this.close()) } /** * 弹出提示框 * @param 参数 */ dialog(param) { this.middleBox(param); this.dialogContent = commonFuncObj.createElementDiv(); this.setStyle(this.dialogContent, { 'max-height': '550px', 'padding': '12px', }) this.dialogContent.innerHTML = param.content; this.content.appendChild(this.dialogContent); param.onReady(this); } close() { commonFuncObj.commSettings = ""; commonFuncObj.GMSetValue("bilibili_current_config", currentConfig); document.body.removeChild(this.mask); } setStyle(element, styleObj) { Object.keys(styleObj).forEach((item) => { element.style[item] = styleObj[item]; }) } } let popup = null; return (function () { if (!popup) { popup = new Popup(); } return popup; })(); })(); function bilibiliHelper() { this.controlSpeed = function () { function innerClassControlSpeed() { this.elementId = commonFuncObj.randomNumber(); this.ciId = "bilibili_ci_" + this.elementId; this.ciSearchId = "#" + this.ciId; this.speedMin = 0.5; this.speedMax = 4; this.speedStep = 0.05; this.speedReg = /^([1-4]|0\.[5-9][0-9]*|[1-3]\.[0-9]{0,2}|4\.0+)$/; this.video = commonFuncObj.video(); this.createElementHtml = () => { let value = this.__getCiValue(); let cssText = ` #` + this.ciId + ` { position: fixed; right: 12px; bottom: 349px; z-index: 100; max-width: 25px; }`; let htmlText = ` <div id="bilibili_cs_` + this.elementId + `"> <label for="` + this.ciId + `"></label><input id="` + this.ciId + `" type="text" value="` + value + `" min="0.5" max="4" step="0.05"> </div>`; commonFuncObj.GMAddStyle(cssText); commonFuncObj.appendHtml(htmlText); this.__controlSpeed(value); } this.createEventListener = () => { try { const $ciSearch = $(this.ciSearchId); $ciSearch.click(); $ciSearch.blur(); $ciSearch.on("mouseover", this.__eventMouseover); $ciSearch.on("mouseout", this.__eventMouseout); $ciSearch.on("wheel", this.__eventWheel); $ciSearch.on("change", this.__eventChange); $("video").on("loadedmetadata", this.__eventLoadedMetadata) this.__rebindEventMenuListClick(); } catch (e) { console.error("InnerClassControlSpeed.createEventListener " + e) } } this.__eventMouseover = (e) => { e.target.focus(); } this.__eventMouseout = (e) => { e.target.blur(); } this.__eventWheel = (e) => { const $ci = e.target, direction = e.originalEvent.deltaY; let value = parseFloat($ci.value); if (direction > 0) { value -= this.speedStep; if (value < this.speedMin) { value = this.speedMin; } } else if (direction < 0) { value += this.speedStep; if (value > this.speedMax) { value = this.speedMax; } } value = value.toFixed(2); $ci.value = value; this.__setCiValue(value); this.__controlSpeed(value); e.preventDefault() } this.__eventChange = (e) => { const $ci = e.target; const defaultValue = $ci.defaultValue, currentValue = $ci.value; if (!this.speedReg.test(currentValue)) { $ci.value = defaultValue; return; } let value = parseFloat(currentValue); if (value > this.speedMax) { value = this.speedMax; } if (value < this.speedMin) { value = this.speedMin; } $ci.value = value; this.__setCiValue(value); this.__controlSpeed(value); } this.__eventLoadedMetadata = (e) => { this.__controlSpeed(this.__getCiValue()); this.__rebindEventMenuListClick() } this.__eventMenuClick = (e) => { const $ci = $(this.ciSearchId)[0], $target = e.target; let value = $($target).attr("data-value"); $ci.value = value; this.__setCiValue(value); this.__controlSpeed(value); } this.__controlSpeed = (speed) => { this.video.playbackRate = speed; } this.__setCiValue = (value) => { commonFuncObj.GMSetValue("bilibili_ci_value", value); } this.__getCiValue = () => { let value = commonFuncObj.GMGetValue("bilibili_ci_value"); value = !!value ? value : 1; return value; } this.__rebindEventMenuListClick = () => { let pathname = window.location.pathname; let speedMenuListClass = ".bpx-player-ctrl-playbackrate-menu-item"; // if (pathname.indexOf("watchlater") > 0) { // speedMenuListClass = ".bpx-player-ctrl-playbackrate-menu-item"; // @since 0.7 bilibili-player-video-btn-speed-menu-list -> bpx-player-ctrl-playbackrate-menu-item // } else if (pathname.indexOf("video") > 0) { // speedMenuListClass = ".bpx-player-ctrl-playbackrate-menu-item"; // } $(speedMenuListClass).on("click", this.__eventMenuClick); } this.start = function () { this.createElementHtml(); this.createEventListener(); } } try { (new innerClassControlSpeed()).start(); } catch (e) { console.error("innerClassControlSpeed " + e) } }; this.controlModule = function () { function innerClassControlModule() { this.focus = false; this.createEventListener = () => { $(document).on("focus", "input:not([readonly]), textarea", () => { this.focus = true; }); $(document).on("blur", "input, textarea", () => { this.focus = false; }); $(document).on('keydown', this.__eventKeydown) }; this.__eventKeydown = (e) => { if (!e.altKey && !e.shiftKey && !e.ctrlKey && this.focus) { return; } const k = (key) => (key ? 1 : 0); let pressKey = commonFuncObj.KeyMap[e.keyCode] || null; if (pressKey === null) { return; } let command = `${k(e.altKey)}${k(e.ctrlKey)}${k(e.shiftKey)}${pressKey}`; let keyMap = currentConfig.shortcutKeyMap; if (commonFuncObj.commSettings) { let id = commonFuncObj.commSettings; // 新旧键位对比 if (command === currentConfig.shortcutKeyMap[id]) { return; } let conflict = null; // 新键位与其他键位是否冲突 Object.keys(keyMap).forEach((oid) => { if (oid === id) { return; } if (command === currentConfig.shortcutKeyMap[oid]) { conflict = oid; } }) let newShortcutKeyStr = commonFuncObj.getShortcutKey(command); let messageObj; if (conflict) { messageObj = { title: "设置快捷键失败", message: `${newShortcutKeyStr} 已经分配给:\n ${commonFuncObj.moduleList[conflict].title}`, }; } else { document.querySelector(`#${id}`).value = newShortcutKeyStr; currentConfig.shortcutKeyMap[id] = command; messageObj = { title: "设置快捷键成功", message: `${commonFuncObj.moduleList[id].title} 设置为\n ${newShortcutKeyStr}`, }; } commonFuncObj.notification(messageObj); return; } switch (command) { case keyMap.removeCurrentVideo: return this.__skRemoveCurrentVideo(); case keyMap.showModuleMenu: return this.showModuleMenu(); case keyMap.removeHistoryVideo: return this.__skRemoveHistoryVideo(); } } this.showModuleMenu = () => { let ML = commonFuncObj.moduleList; let skItem = ""; Object.keys(ML).forEach((key) => { let mItemObj = ML[key]; let attrs = commonFuncObj.moduleInfoAttr(mItemObj.iptAttrs); skItem += ` <div>${mItemObj.title} <label for="${key}"></label> <input id="${key}" type="${mItemObj.iptType}"${attrs}></div>`; }); let content = ` <div id="skm-box" style="font-size: 15px;"> ${skItem} </div> `; popup.dialog({ "title": "设置", "content": content, "onReady": function ($self) { $("#skm-box input[type='text']").on('click mouseover', (e) => { let $text = e.target, id = $text.id; Object.keys(ML).forEach((key) => { document.querySelector(`#${key}`).style.border = '1px solid #a9a9a9'; }) $text.style.border = '1px solid #3597ff'; commonFuncObj.commSettings = id; }).each((index, ele) => { let id = ele.id; ele.value = commonFuncObj.getShortcutKey(currentConfig.shortcutKeyMap[id]); }) $("#skm-box input[type='checkbox']").on("click", (e) => { let $checkbox = e.target, id = $checkbox.id; if (!currentConfig.moduleMap) { currentConfig.moduleMap = {} } currentConfig.moduleMap[id] = $checkbox.checked; }).each((index, ele) => { let id = ele.id; if (currentConfig.moduleMap[id]) { $(ele).attr("checked", true); } }) } }) } /** * @deprecated */ this.__skRemoveCurrentVideo = () => { let pathname = window.location.pathname; if (pathname.indexOf("watchlater") > 0) { $('.player-auxiliary-playlist-item-active .player-auxiliary-playlist-item-img-del,.player-auxiliary-playlist-item-showp .player-auxiliary-playlist-item-img-del,.siglep-active .del-btn').click(); } else if (pathname.indexOf("video") > 0) { window.close(); } } this.__skRemoveHistoryVideo = () => { let allBtn = $(".player-auxiliary-playlist-list .player-auxiliary-playlist-item-img-del,.action-list-inner .del-btn"); commonFuncObj.loadVideoInfo(); let finishVideos = Object.values(currentConfig.historyVideoInfo) .filter(video => video.finish); if (finishVideos.length > 0) { finishVideos.sort((v1, v2) => v2.index - v1.index); let messageObj = { title: "删除已播放成功", } let message = ""; for (let i = 0; i < finishVideos.length; i++) { let fv = finishVideos[i]; message += `${fv.bv_id}: ${fv.title}\n`; allBtn.get(fv.index).click(); } messageObj.message = message; messageObj.timeout = 5000; commonFuncObj.notification(messageObj); } } this.clearConfig = () => { commonFuncObj.GMSetValue("bilibili_current_config", defaultBiliBiliConfig); window.location.reload(); } this.start = function () { this.createEventListener(); GM_registerMenuCommand("设置", () => this.showModuleMenu()) GM_registerMenuCommand("还原设置", () => this.clearConfig()) } } try { (new innerClassControlModule()).start(); } catch (e) { console.error("innerClassControlModule " + e) } } this.controlHistory = function () { function innerClassControlHistory() { this.video = commonFuncObj.video(); this.videoInfo = null; this.currentBvid = null; this.createEventListener = () => { class VideoEvent extends Enumerable { static abort = new VideoEvent(); static loadedmetadata = new VideoEvent(); static timeupdate = new VideoEvent(); static ended = new VideoEvent(); static _ = this.closeEnum(); constructor() { super(); } } // abort loadedmetadata timeupdate ended $(this.video).on(VideoEvent.enumKeys.join(" "), (e) => { let event = VideoEvent.enumValueOf(e.type); switch (event) { case VideoEvent.loadedmetadata: { this._setCurrentBvid(this._getCurrentBvid()); this._setCurrentVideoDuration(); if (currentConfig.moduleMap.autoLike) { commonFuncObj.moduleAutoLike(); } break; } case VideoEvent.abort : { currentConfig.historyVideoInfo = this.videoInfo; if (this._isFinishCurrentVideo()) { this._setCurrentVideoCurrentTime(); } this._setCurrentBvid(null); commonFuncObj.GMSetValue("bilibili_current_config", currentConfig); break } case VideoEvent.timeupdate: { this._setCurrentVideoCurrentTime(); break; } case VideoEvent.ended: { this._setCurrentVideoFinish(); break } } }) } this._setCurrentVideoDuration = () => { if (this.currentBvid !== null) this.videoInfo[this.currentBvid].duration = this.video.duration; } this._setCurrentVideoCurrentTime = () => { if (this.currentBvid !== null) { let temp = this.videoInfo[this.currentBvid].currentTime; let max = Math.max(this.video.currentTime, temp ? temp : 0); this.videoInfo[this.currentBvid].currentTime = max; } } this._setCurrentBvid = (bvid) => { this.currentBvid = bvid; } this._getCurrentBvid = () => { let bvid; let urlParam = new URLSearchParams(window.location.search); bvid = urlParam.get("bvid"); if (bvid !== null) { return bvid; } let pathname = window.location.pathname; bvid = pathname.substring(pathname.lastIndexOf("/") + 1) return bvid } this._setCurrentVideoFinish = () => { if (this.currentBvid !== null) { this.videoInfo[this.currentBvid].finish = true; } } this._isFinishCurrentVideo = () => { if (this.currentBvid !== null) { let duration = this.videoInfo[this.currentBvid].duration; let currentTime = this.videoInfo[this.currentBvid].currentTime; if (currentTime / duration * 100 > 90) { return true; } } return false; } this.start = () => { // todo 1. 读取列表信息 并 合并历史数据 this.videoInfo = commonFuncObj.loadVideoInfo(); // todo 2. 绑定 video 监听 this.createEventListener(); } } try { let pathname = window.location.pathname; if (pathname.indexOf("watchlater") > 0) { (new innerClassControlHistory()).start(); } } catch (e) { console.error("innerClassControlHistory " + e) } } this.start = function () { this.controlHistory(); this.controlSpeed(); this.controlModule(); } } try { $(document).on("readystatechange", (e) => { setTimeout(() => { (new bilibiliHelper()).start(); console.log("[视频播放速度调整器]: 已加载!"); if (!currentConfig.moduleMap) { currentConfig.moduleMap = {} } if (currentConfig.shortcutKeyMap.showShortcutKeyMenu) { currentConfig.shortcutKeyMap = defaultBiliBiliConfig.shortcutKeyMap; } if (currentConfig.moduleMap.autoLike) { commonFuncObj.moduleAutoLike(true); } }, 3000) }); } catch (e) { console.error(e); } })();