Greasy Fork is available in English.
快捷键【c】打开关闭弹幕,跳过充电鸣谢,自动下一个,视频时长统计
// ==UserScript== // @name 哔哩哔哩工具[快捷键|视频时长] // @namespace http://tampermonkey.net/ // @version 2.4 // @description 快捷键【c】打开关闭弹幕,跳过充电鸣谢,自动下一个,视频时长统计 // @author maple // @match https://www.bilibili.com/* // @require https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js // @require https://unpkg.com/[email protected]/log.js // @icon https://www.bilibili.com/favicon.ico // @grant none // ==/UserScript== "use strict"; let logger = Logger.log("debug") class Handler { constructor(videoList) { this.videoList = videoList; } execute() { let curLookInx = this.getCurrentLookVideoIndex(); // 获取所有时间以及未观看时间 的 二维数组表示形式 let allVideoTimeArr = this.getVideoListTimeArr(0); let unLookVideoTimeArr = this.getVideoListTimeArr(curLookInx); // 格式化视频总时间数组 [小时,分钟] let videoSumTimeArr = this.listTimeArrFormat(allVideoTimeArr); // 格式化未看视频时间数组 [小时,分钟] let unLookVideoSumTimeArr = this.listTimeArrFormat(unLookVideoTimeArr); // 渲染标签 Handler.renderTimeTag(videoSumTimeArr, unLookVideoSumTimeArr, this.getUnLookVideoCount(curLookInx)); } /** * 获取当前正在观看视频索引 */ getCurrentLookVideoIndex() { } /** * 获取未观看视频个数 * @param curIndex 当前观看视频索引 * @returns {number} 未观看视频个数 */ getUnLookVideoCount(curIndex) { return this.videoList.length - curIndex; } /** * 获取视频列表时间的二维数组 * @param index 从索引位置开始获取 * 格式如下: * [ * 视频1:[小时, 分钟,秒] * 视频2:[小时, 分钟,秒] * ] */ getVideoListTimeArr(index) { } /** * 视频列表获取到的二维数组时间格式化汇总 * @param timeTwoArray * @returns {(number|number|string)[]} 格式:[小时, 分钟] */ listTimeArrFormat(timeTwoArray) { //如果为0没有数据,就出错了 logger.debug("视频列表长度:" + timeTwoArray.length); let h = 0, m = 0, s = 0; for (let i = 0; i < timeTwoArray.length; i++) { h += Number(timeTwoArray[i][0]); m += Number(timeTwoArray[i][1]); s += Number(timeTwoArray[i][2]); } //将秒转换为分钟 let m1 = Math.floor(s / 60); m += m1; //分钟转换为小时 let temp = m / 60; let h1 = Math.floor(temp); //小于一小时的转换为分钟 let m2 = ('0.' + String(temp).split('.')[1]) * 60; //最终结果 h += h1; m = Math.floor(m2); //分钟出现NaN,原因是因为没有分钟,全是小时,直接赋值 if (isNaN(m)) { m = "00"; } logger.debug("小时:" + h); logger.debug("分钟:" + m); return [h, m]; } /** * 渲染时间标签 * @param all 所有时间一维数组 格式:[小时,分] * @param undone 未完成时间一维数组 格式:[小时,分] * @param unLookCount 为观看视频个数 */ static renderTimeTag(all, undone, unLookCount) { //找到显示面板 let plain = document.getElementsByClassName("video-info-detail")[0]; try { plain.removeChild() } catch (e) { logger.debug("移除异常") } let data_tag = document.getElementById("data_tag"); let isNull = data_tag === null; //没有创建过这个标签就创建 if (isNull) { //创建 data_tag = document.createElement("span"); logger.debug("创建标签:", data_tag) //id赋值,用于下次更新查找 data_tag.setAttribute("id", "data_tag"); data_tag.style = '\n' + ' background-color: #24c7b4;\n' + ' color: white;\n' + ' font-size: -0.9rem;\n' + ' text-align: center;\n' + ' margin-top: 100rem;\n' + ' margin-bottom: 100rem;\n' + ' padding:0.5rem;\n' + ' cursor: pointer;\n' + ' border-radius: 1rem;\n' + ' '; } //写入html data_tag.innerHTML = show_Str(); //数据添加到面板 if (isNull) { plain.appendChild(data_tag); } function show_Str() { let all_time = "总时长:" + all.join(' : '); let undone_time = " 未观看:" + undone.join(' : '); let num = " 未看个数:" + unLookCount; return all_time + undone_time + num; } } } /** * 合集视频 */ class HeJiHandler extends Handler { getCurrentLookVideoIndex() { for (let i = 0; i < this.videoList.length; i++) { let videoEle = this.videoList[i]; if (videoEle.querySelector(".video-episode-card__info-playing")) { return i; } } logger.error("未获取到索引") } getVideoListTimeArr(index) { let r###ltTwoArr = [] for (let i = index; i < this.videoList.length; i++) { let videoEle = this.videoList[i]; let videoTimeStr = videoEle.querySelector(".video-episode-card__info-duration").innerHTML; let child_array = videoTimeStr.split(":"); if (child_array.length < 3) { //数组首部添加0 child_array.unshift('0'); } r###ltTwoArr.push(child_array); } return r###ltTwoArr; } } /** * 分批视频 */ class FenPHandler extends Handler { getCurrentLookVideoIndex() { let index = 0; logger.debug("视频列表:", this.videoList) for (let i = 0; i < this.videoList.length; i++) { //当前观看的视频 let current = this.videoList[i]; //延迟之后获取class值 let class_name = current.className; //当前观看 if (class_name === 'watched on' || class_name === 'on') { logger.debug("当前视频索引:" + index) return i; } } logger.error("未查询到索引") return index; } getVideoListTimeArr(index) { //如果没有这个参数(用于统计总时长) if (index === undefined) { index = 0; } let parent_array = []; for (let i = index; i < this.videoList.length; i++) { let durationDivTag = this.videoList[i].querySelector(".duration"); logger.debug("单个视频时长标签:", durationDivTag) //每个视频的时长 let duration = durationDivTag.innerHTML; //添加到数组 let child_array = duration.split(":"); if (child_array.length < 3) { //数组首部添加0 child_array.unshift('0'); } parent_array.push(child_array); } return parent_array; } } class HandlerFactor { static getHandler(videoType, videoList) { if (videoType === 1) { return new FenPHandler(videoList); } else if (videoType === 2) { return new HeJiHandler(videoList); } throw "视频类型异常"; } } window.onload = function () { logger.info("B-Tools运行中") // 第一次延迟两秒执行,等待视频列表渲染 setTimeout(main, 2000) // 后续10秒执行,更新时间 setInterval(main, 10000) } function main() { let videoType = isLoadedGetVideoType(); logger.debug("视频类型:" + (videoType === 1 ? "分批视频" : "合集视频")) if (!videoType) { logger.info("bTools-不是视频列表;结束") return; } let videoList = getVideoList(); sleepCallbackExecute(200).then(() => { HandlerFactor.getHandler(videoType, videoList).execute(); }) } function getVideoList() { let nodeList = document.querySelector('.list-box'); // 合集视频 let nodeList1 = document.querySelector('.video-section-list'); if (nodeList) { return nodeList.childNodes; } if (nodeList1) { return nodeList1.childNodes; } } function isLoadedGetVideoType() { // 分p视频 if (document.querySelector('.list-box')) { return 1; } // 合集视频 if (document.querySelector('.video-section-list')) { return 2; } } function sleepCallbackExecute(time) { return new Promise((resolve) => setTimeout(resolve, time)); } ////////////////////////////////快捷键/////////////////////////////////////// //注册【c】按钮监听 window.onkeydown = function(ev){ //ev表示onkeydown事件对象,63为字母 c if(ev.keyCode===67){ let input = document.querySelector(".bui-danmaku-switch-input"); input.click(); } }