Created
October 17, 2021 13:37
-
-
Save zyzsdy/e1169e1d5f021cbd6934f0adbc750161 to your computer and use it in GitHub Desktop.
B站国际版网页字幕下载&强制字幕中文
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name B站国际版网页字幕下载&强制字幕中文 | |
// @namespace Violentmonkey Scripts | |
// @match https://www.biliintl.com/en/play/* | |
// @grant none | |
// @version 1.0 | |
// @author 网上商务模式 | |
// @description 2021/10/17 下午5:43:39 | |
// ==/UserScript== | |
(() =>{ | |
console.log("MSI Fcine Loaded."); | |
XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open; | |
Object.defineProperty(XMLHttpRequest.prototype, "open", { | |
get: function() { | |
return this._open; | |
}, | |
set: function(f) { | |
this._open = new Proxy(f, { | |
apply: function(f, instance, fargs) { | |
let url1 = fargs[1]; | |
if (url1 && url1.startsWith("https://api.biliintl.com/intl/gateway/m/subtitle")) { | |
fargs[1] = url1.replace(/s_locale=(.+)/, "s_locale=zh_Hans"); | |
listen.call(instance, ...fargs); | |
console.log("xhr modified"); | |
} | |
return f.call(instance, ...fargs); | |
} | |
}); | |
} | |
}); | |
XMLHttpRequest.prototype.open = XMLHttpRequest.prototype.open; | |
const listen = function(){ | |
this.addEventListener("load", function() { | |
if (this.readyState === 4){ | |
let resJson = JSON.parse(this.responseText); | |
addSubinfoArea(resJson); | |
} | |
}); | |
}; | |
const addSubinfoArea = function(resJson){ | |
//先检测有没有以前留下来的,如果有先删了 | |
let oldDiv = document.getElementById("__ikp_msi_fcine_ext"); | |
if (oldDiv) { | |
oldDiv.parentElement.removeChild(oldDiv); | |
} | |
//添加新的 | |
let infoarea = document.querySelector("#app > div > div.layout-body.media-width > div > div.bstar-web__content > div > div > div.video-info__container.media-size__video"); | |
let extArea = document.createElement('div'); | |
extArea.id = "__ikp_msi_fcine_ext"; | |
let subtArea = document.createElement('div'); | |
subtArea.className = "_ext_subtArea"; | |
let subspan = document.createElement('span'); | |
if (resJson && resJson.data && resJson.data.subtitles) { | |
subspan.innerHTML = "检测到字幕(单击下载):"; | |
subtArea.appendChild(subspan); | |
let sublistUl = document.createElement('ul'); | |
for(let si of resJson.data.subtitles){ | |
let sublistLi = document.createElement('li'); | |
sublistLi.innerHTML = si.title; | |
sublistLi.dataset.key = si.key; | |
sublistLi.dataset.id = si.id; | |
sublistLi.dataset.url = si.url; | |
sublistLi.onclick = downloadSub; | |
sublistUl.appendChild(sublistLi); | |
} | |
subtArea.appendChild(sublistUl); | |
}else{ | |
subspan.innerHTML = "未检测到字幕"; | |
subtArea.appendChild(subspan); | |
} | |
extArea.appendChild(subtArea); | |
infoarea.parentNode.insertBefore(extArea, infoarea); | |
let styleElement = document.createElement('style'); | |
styleElement.innerHTML=` | |
._ext_subtArea{ | |
color: #999; | |
margin-top: 15px; | |
} | |
._ext_subtArea>span{ | |
display: inline; | |
} | |
._ext_subtArea>ul{ | |
display: inline; | |
} | |
._ext_subtArea>ul>li{ | |
display: inline; | |
background-color: rgba(51,51,51,.6); | |
padding: 6px 12px; | |
margin: 6px; | |
border-radius: 4px; | |
cursor: pointer; | |
transition: all .2s; | |
font-size: 14px; | |
} | |
._ext_subtArea>ul>li:hover{ | |
background-color: rgb(51,51,51); | |
}`; | |
extArea.appendChild(styleElement); | |
} | |
const downloadSub = function(e) { | |
let subName = e.target.innerText; | |
let subUrl = e.target.dataset.url; | |
let subId = e.target.dataset.id; | |
let subKey = e.target.dataset.key; | |
console.log(`开始下载字幕:${subName}: ${subUrl}`); | |
let xhr = new XMLHttpRequest(); | |
xhr.open('GET', subUrl, true); | |
xhr.send(); | |
xhr.onreadystatechange = function(){ | |
if (xhr.readyState === 4 && xhr.status === 200){ | |
let subData = JSON.parse(xhr.responseText); | |
let title = `${document.title}_${subId}_${subKey}.srt`; | |
downloadSrt(title, srtBuild(subData)); | |
} | |
} | |
} | |
const timeFormat = function(s){ | |
let h = Math.floor(s / 3600); | |
let m = Math.floor(s / 60 % 60); | |
let lp = s % 60; | |
let hS = h.toString().padStart(2, '0'); | |
let mS = m.toString().padStart(2, '0'); | |
let lpS = lp.toFixed(3); | |
let secS = lp < 10 ? '0'+lpS : lpS; | |
return `${hS}:${mS}:${secS}`; | |
} | |
const srtBuild = function(data){ | |
let res = ""; | |
for (let i in data.body){ | |
let idx = parseInt(i) + 1; | |
let line = data.body[i]; | |
let fromStr = timeFormat(line.from); | |
let toStr = timeFormat(line.to); | |
res += `${idx}\r\n`; | |
res += `${fromStr} --> ${toStr}\r\n`; | |
res += `${line.content}\r\n\r\n`; | |
} | |
return res; | |
} | |
const downloadSrt = function(name, str){ | |
let b = new Blob([str], {type: 'text/plain'}); | |
let a = document.createElement('a'); | |
a.download = name; | |
a.href = URL.createObjectURL(b); | |
a.click(); | |
URL.revokeObjectURL(b); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment