// ==UserScript== // @name 在线视频外挂字幕 // @namespace https://truework.top // @version 0.24 // @description 目前支持bilibili和爱奇艺,按Q键+100ms,按W键-100ms,按E键显示/隐藏字幕,console可作为transcript使用 // @author cyj98 // @match https://www.bilibili.com/bangumi/* // @match https://www.iqiyi.com/* // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js // @require https://greasyfork.org/scripts/373379-subtitle-utils-module/code/subtitle%20utils%20module.js?version=637875 // @downloadURL none // ==/UserScript== (function () { 'use strict'; $("body").prepend('') $("#open-file").click(() => { let hostname = $(location).attr('hostname') let subtitleSel, curTimeSel if (hostname === 'www.bilibili.com') { // '.bilibili-player-video-subtitle' subtitleSel = '.subtitle-position' curTimeSel = 'bilibili-player-video-time-now' } else if (hostname === 'www.iqiyi.com') { subtitleSel = '.iqp-subtitle' curTimeSel = 'iqp-time-cur' $(".iqp-subtitle").css({ "display": "block", "height": "50px" }); } $('head').append(''); $(subtitleSel).prepend('
') $(subtitleSel).prepend('
') let fileInput = document.createElement("input") fileInput.type = 'file' fileInput.style.display = 'none' fileInput.onchange = (e) => { let file = e.target.files[0]; if (!file) { return; } let isShowSubtitle = false let isShowDelay = false let reader = new FileReader(); reader.onload = (e) => { let delayTime = 0 $(window).on('keypress', (e) => { let key = e.key.toUpperCase() if (key === 'Q' || key === 'W') { if (key === 'Q') delayTime += 100 if (key === 'W') delayTime -= 100 // console.log("delayTime: ", delayTime) var delaySnackbar = document.getElementById("snackbar"); delaySnackbar.innerHTML = 'delaytime: ' + delayTime if (isShowDelay) { return } delaySnackbar.className = "show"; isShowDelay = true setTimeout(() => { delaySnackbar.className = delaySnackbar.className.replace("show", ""); isShowDelay = false }, 2000); } else if (key === 'E') { if (!isShowSubtitle) { $('#custom-subtitle').css({ "display": "none" }) console.log("hide subtitle") } else { $('#custom-subtitle').css({ "display": "block" }) console.log("show subtitle") } isShowSubtitle = !isShowSubtitle } }); let contents = e.target.result; document.body.removeChild(fileInput) // console.log(contents) let targetNode = document.getElementsByClassName(curTimeSel)[0] let config = { attributes: true, childList: true, subtree: true }; let prevPos = -2 let binarySearch = (target, arr) => { let start = 0; let end = arr.length - 1; while (start <= end) { let mid = parseInt(start + (end - start) / 2); if (target >= arr[mid].start && target <= arr[mid].end) { return mid; } else if (target > arr[mid].end) { start = mid + 1; } else { end = mid - 1; } } return -1; } let callback = (_, observer) => { // console.log("observer.subtitles", observer.subtitles) let strTime = targetNode.innerHTML if (strTime.length <= 5) { strTime = "00:" + strTime + ",000" } else { strTime = strTime + ",000" } let time = window.Subtitle.toMS(strTime); let subtitles = observer.subtitles; let pos = binarySearch(time + delayTime, subtitles) if (pos === -1) { $('#custom-subtitle').css({ "display": "none" }) return; } if (pos === prevPos) { prevPos = pos return } console.log(subtitles[pos].text); if (isShowSubtitle === false) { $('#custom-subtitle').css({ "display": "block" }) } $('#custom-subtitle').html(subtitles[pos].text) window.subtitleCount += 1 prevPos = pos }; let observer = new MutationObserver(callback); // 将字幕转换为对象数组 try { observer.subtitles = window.Subtitle.parse(contents) } catch (e) { alert("字幕解析出现问题"); } observer.observe(targetNode, config); } reader.readAsText(file) } document.body.appendChild(fileInput) let eventMouse = document.createEvent("MouseEvents") eventMouse.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null) fileInput.dispatchEvent(eventMouse) }) })();