// ==UserScript== // @name B站视频同传弹幕提取 // @version 0.0.1 // @description 用于提取在B站视频里的同传弹幕,主要针对直播回放 // @author yellowko // @namespace www.yellowko.com // @supportURL https://github.com/yellowko/bilibili-video-danmaku-translation-extract/issues // @homepage https://github.com/yellowko/bilibili-video-danmaku-translation-extract // @require https://code.jquery.com/jquery-3.4.0.min.js // @require https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.min.js // @match https://www.bilibili.com/video/* // @icon https://www.bilibili.com/favicon.ico // @grant GM_xmlhttpRequest // @downloadURL none // ==/UserScript== (function () { 'use strict'; let url = $(location).attr('href'); let retBV = /[\s\S]*(BV[a-z|A-Z|0-9]{10})[?p=]*([0-9]*)[\s\S]*/; let retTranslation = /(.*)【(.*)】|(.*)【(.*)/; let retTime = /([0-9]*\.[0-9]*)/; let BVresult = url.match(retBV); let bvid = BVresult[1]; let page = 1; if (BVresult.length > 2 && !(BVresult[2] == "" || BVresult[2] == null)) page = BVresult[2]; let cid; let danmakuTranslation = []; let delay = 9000; let index = 0; let timeout = 1000; let lastTime = 0; let currentTime = 0; let init = 0; GM_xmlhttpRequest({ method: "GET", url: "https://api.bilibili.com/x/player/pagelist?bvid=" + bvid + "&jsonp=jsonp", onload: function (res) { if (res.status == 200) { var text = res.responseText; var json = JSON.parse(text); cid = json.data[page - 1].cid; GM_xmlhttpRequest({ method: "GET", url: "https://api.bilibili.com/x/v1/dm/list.so?oid=" + cid, onload: function (res) { if (res.status == 200) { var text = res.responseText; $(text).find("d").each(function (i) { let danmaku = $(this).text(); if (retTranslation.test(danmaku)) { let time = parseInt($(this)[0].outerHTML.match(retTime)[0] * 1000); let tanslation; let j = 1; let matchres = danmaku.match(retTranslation); for (; j < matchres.length; j++) { if (matchres[j] != null && matchres[j] != "") { tanslation = danmaku.match(retTranslation)[j]; break; } } let danmakuObj = new Object(); danmakuObj.time = time; danmakuObj.tanslation = tanslation; danmakuTranslation.push(danmakuObj); } }); //本视频有同传弹幕,开始初始化 if (danmakuTranslation.length > 0) { //生成同传弹幕 $(".bilibili-player-video").before('
" + v.tanslation + "
"); }); //同传字幕显示 setTimeout(function showTranslation() { if (danmakuTranslation.length > 0) { lastTime = currentTime; currentTime = new Date("1970-1-1" + $(".bilibili-player-video-time-now").text()).getTime() - 7200000 + delay; if (currentTime > delay && init == 0) { //同传弹幕延迟输入框,值为0时会关闭。(由于b站播放器是异步加载的,需要在播放器加载完毕后才能把同传弹幕绑定上去) init = 1; $(".SubtitleBody").show(); $(".bilibili-player-video-time").after('') $("#danmakuTranslation-delay").on("change", () => { delay = parseInt($("#danmakuTranslation-delay").val()); if (delay == 0) { $(".SubtitleBody").hide(); clearTimeout(showTranslation); } else { $(".SubtitleBody").show(); showTranslation(); } }) //处理在同传弹幕上的双击 $(".SubtitleTextBody").on("dblclick", "p", (event) => { index = event.currentTarget.dataset.index; let playerTime = new Date("1970-1-1" + $(".bilibili-player-video-time-now").text()).getTime() - 7200000; delay = danmakuTranslation[index].time - playerTime; $("#danmakuTranslation-delay").val(delay); $(".currentdanmaku").removeClass("currentdanmaku"); $(".SubtitleTextBodyFrame").scrollTop((index - 1) * 18.6); index++; $(".SubtitleTextBody p:nth-child(" + index + ")").addClass("currentdanmaku") }); //处理在同传弹幕上的右键 $(".SubtitleTextBody").on("contextmenu", function () { return false; }) $(".SubtitleTextBody").on("mouseup", (function (event) { if (3 == event.which) { $(".SubtitleTextBodyFrame").scrollTop((index - 2) * 18.6); } })); //滚动查看同传弹幕时拦截b站播放器的音量变化 $(".SubtitleTextBodyFrame").on('mousewheel', function (event) { event.stopPropagation(); }); } //当前同传弹幕更新 if (currentTime < lastTime) { index = 0; } while (danmakuTranslation[index].time < currentTime && index < danmakuTranslation.length) { index++; } while (currentTime <= danmakuTranslation[index].time && (currentTime + timeout + 100) > danmakuTranslation[index].time) { $(".currentdanmaku").removeClass("currentdanmaku"); $(".SubtitleTextBodyFrame").scrollTop((index - 1) * 18.6); index++; $(".SubtitleTextBody p:nth-child(" + index + ")").addClass("currentdanmaku") } } setTimeout(showTranslation, timeout); }, timeout); } } } }); } } }); //去掉input="number"时的小箭头 $("head").append(''); // 以下CSS以及字幕框元素修改自SOW社团的自动字幕组件 // 发布帖链接:http://nga.178.com/read.php?tid=17180967 $("head").append(''); })(); function sortBy(attr, rev) { //第二个参数没有传递 默认升序排列 if (rev == undefined) { rev = 1; } else { rev = (rev) ? 1 : -1; } return function (a, b) { a = a[attr]; b = b[attr]; if (a < b) { return rev * -1; } if (a > b) { return rev * 1; } return 0; } }