您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
将 lynda.com 的字幕译为中文,支持搜狗、彩云、Google翻译接口
当前为
// ==UserScript== // @name lynda.com 字幕翻译 // @description 将 lynda.com 的字幕译为中文,支持搜狗、彩云、Google翻译接口 // @namespace https://github.com/journey-ad // @version 0.2.2 // @author journey-ad // @match *://www.lynda.com/* // @license MIT // @run-at document-end // @grant GM_xmlhttpRequest // ==/UserScript== (function () { "use strict"; var transServer = "caiyun"; // 配置翻译接口,可选值为 caiyun, sogou, google var entries = null; window.transTimer = window.setInterval(init, 100); // 用定时器检查待翻译文本是否已准备好 function init() { if (unsafeWindow.mejs && getDeepProperty(mejs, "players.mep_0.selectedTrack")) { window.clearInterval(window.transTimer); // 清除定时器 addTranslateBtn(); entries = mejs.players.mep_0.selectedTrack.entries; Object.defineProperty(mejs.players.mep_0.selectedTrack, "entries", { get: function get() { return entries; }, set: function set(data) { entries = data; if (entries.text) { console.log("字幕文本可用"); transText(); } } }); console.log("字幕文本可用"); transText(); } } function addTranslateBtn() { window.isTranslateEnable = true; var controls = document.getElementsByClassName("mejs-controls")[0], css = ".mejs-container .mejs-controls .mejs-button.translate {right: 110px;}@media (min-width: 768px){.mejs-container .mejs-controls .mejs-button.translate {right: 132px;}.mejs-container .mejs-controls .mejs-button.mejs-playback-rate-button {right: 166px;}}.modal.video-modal .modal-player-cont .mejs-container .mejs-controls .mejs-button.translate {right: 90px;}#translate-btn {height: 16px;margin-top: 2px;font-size: 16px;font-weight: bold;color: #ccc;transition: all .3s;}#translate-btn:hover {color: #fff;}#translate-btn.enable {color: #ffba00;}", container = document.createElement("div"); if (typeof GM_addStyle != "undefined") { GM_addStyle(css); } else if (typeof PRO_addStyle != "undefined") { PRO_addStyle(css); } else if (typeof addStyle != "undefined") { addStyle(css); } else { var node = document.createElement("style"); node.type = "text/css"; node.appendChild(document.createTextNode(css)); var heads = document.getElementsByTagName("head"); if (heads.length > 0) { heads[0].appendChild(node); } else { // no head yet, stick it whereever document.documentElement.appendChild(node); } } controls.appendChild(container); container.outerHTML = '<div class="mejs-button translate"><button class="enable" id="translate-btn" type="button">文</button></div>'; var transBtn = document.getElementById("translate-btn"); transBtn.addEventListener("click", function () { transBtn.classList.toggle("enable"); window.isTranslateEnable = !window.isTranslateEnable; }, false); } function transText() { var s = "", r = "", arr = [], num = 0, count = 0, subtitle = entries.text, subtitleTrans = []; subtitle.forEach(function (e) { // 去除每条字幕的换行符并按行排列 s += e.replace(/\r?\n|\r/g, " ") + "\n"; }); num = translate(s, function (data, index) { // 调用翻译方法并处理回调 count++; arr[index] = data; // 按分块原始下标放回结果数组 if (count >= num) { // 所有翻译文本已取回 r = arr.join("\n"); subtitleTrans = r.split("\n"); mejs.players.mep_0.displayCaptions = function () { // 重写displayCaptions方法 var subtitle = null; if ("undefined" != typeof mejs.players.mep_0.tracks) { var t, e = mejs.players.mep_0, i = e.selectedTrack; if (null !== i && i.isLoaded) { for (t = 0; t < i.entries.times.length; t++) { if (e.media.currentTime >= i.entries.times[t].start && e.media.currentTime <= i.entries.times[t].stop) { if (window.isTranslateEnable) { // 拼接双语字幕 subtitle = subtitleTrans[t] + "\n" + i.entries.text[t].replace(/\r?\n|\r/g, " "); } else { subtitle = i.entries.text[t]; } return e.captionsText.html(subtitle).attr("class", "mejs-captions-text " + (i.entries.times[t].identifier || "")), void e.captions.show(); } } e.captions.hide(); } else e.captions.hide(); } }; console.log(r); console.log("使用 " + transServer + " 翻译完成"); } }); } function translate(str, callback) { var textArr = [], count = 1; if (str.length > 5000) { //大于5000字符分块翻译 var strArr = str.split("\n"), i = 0; strArr.forEach(function (v) { textArr[i] = textArr[i] || ""; if ((textArr[i] + v).length > (i + 1) * 5000) { // 若加上此行后长度超出5000字符则分块 i++; textArr[i] = ""; } textArr[i] += v + "\n"; }); count = i + 1; // 记录块的数量 } else { textArr[0] = str; } textArr.forEach(function (text, index) { // 遍历每块分别进行翻译 server[transServer]({ text: text, index: index }, callback); }); return count; // 返回分块数量 } var server = { sogou: function sogou(r, callback) { var KEY = "b33bf8c58706155663d1ad5dba4192dc"; // 硬编码于搜狗网页翻译js var data = { "from": "auto", "to": "zh-CHS", "client": "pc", "fr": "browser_pc", "text": r.text, "pid": "sogou-dict-vr", "useDetect": "on", "useDetectResult": "on", "oxford": "on", "isReturnSugg": "on", "needQc": 1, "s": md5("autozh-CHS".concat(r.text).concat(KEY)) // 签名算法 }; GM_xmlhttpRequest({ method: "POST", url: "https://fanyi.sogou.com/reventondc/translateV1", headers: { "accept": "application/json", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" }, data: serialize(data), onload: function onload(response) { var result = JSON.parse(response.responseText); callback(result.data.translate.dit, r.index); // 执行回调,在回调中拼接 } }); }, caiyun: function caiyun(r, callback) { var data = { "source": r.text.split('\n'), "trans_type": "en2zh", "request_id": "web_fanyi", "media": "text", "os_type": "web", "dict": true, "cached": true, "replaced": true }; GM_xmlhttpRequest({ method: "POST", url: "https://api.interpreter.caiyunai.com/v1/translator", headers: { "accept": "application/json", "content-type": "application/json; charset=UTF-8", "X-Authorization": "token:cy4fgbil24jucmh8jfr5" }, data: JSON.stringify(data), onload: function onload(response) { var result = JSON.parse(response.responseText); callback(result.target.join('\n'), r.index); // 执行回调,在回调中拼接 } }); }, google: function google(r, callback) { var data = { "q": r.text, "client": "webapp", "sl": "auto", "tl": "zh-CN", "hl": "zh-CN", "dt": "t", "otf": 1, "pc": 1, "ssel": 0, "tsel": 0, "kc": 5, "tk": tk(r.text) }; GM_xmlhttpRequest({ method: "POST", url: "https://translate.google.cn/translate_a/single", headers: { "accept": "application/json", "content-type": "application/x-www-form-urlencoded; charset=UTF-8" }, data: serialize(data), onload: function onload(response) { var result = JSON.parse(response.responseText), arr = []; result[0].forEach(function (t) { t && arr.push(t[0]); }); callback(arr.join(''), r.index); // 执行回调,在回调中拼接 } }); } }; function getDeepProperty(obj, propstr) { var prop = propstr.split('.'); for (var i = 0; i < prop.length; i++) { if (typeof obj === 'object') obj = obj[prop[i]]; } return obj; } function serialize(obj) { return Object.keys(obj).map(function (k) { return encodeURIComponent(k) + "=" + encodeURIComponent(obj[k]).replace("%20", "+"); }).join("&"); } function md5(str) { var k = [], i = 0; for (i = 0; i < 64;) { k[i] = 0 | Math.abs(Math.sin(++i)) * 4294967296; } var b, c, d, j, x = [], str2 = unescape(encodeURI(str)), a = str2.length, h = [b = 1732584193, c = -271733879, ~b, ~c]; for (i = 0; i <= a;) { x[i >> 2] |= (str2.charCodeAt(i) || 128) << 8 * (i++ % 4); } x[str = (a + 8 >> 6) * 16 + 14] = a * 8; i = 0; for (; i < str; i += 16) { a = h; j = 0; for (; j < 64;) { a = [d = a[3], (b = a[1] | 0) + ((d = a[0] + [b & (c = a[2]) | ~b & d, d & b | ~d & c, b ^ c ^ d, c ^ (b | ~d)][a = j >> 4] + (k[j] + (x[[j, 5 * j + 1, 3 * j + 5, 7 * j][a] % 16 + i] | 0))) << (a = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21][4 * a + j++ % 4]) | d >>> 32 - a), b, c]; } for (j = 4; j;) { h[--j] = h[j] + a[j]; } } str = ""; for (; j < 32;) { str += (h[j >> 3] >> (1 ^ j++ & 7) * 4 & 15).toString(16); } return str; } function tk(a) { var tkk = '429175.1243284773', Jo = null; function Ho(a) { return function () { return a; }; } function Io(a, b) { for (var c = 0; c < b.length - 2; c += 3) { var d = b.charAt(c + 2); d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d); d = "+" == b.charAt(c + 1) ? a >>> d : a << d; a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d; } return a; } if (null !== Jo) var b = Jo; else { b = Ho(String.fromCharCode(84)); var c = Ho(String.fromCharCode(75)); b = [b(), b()]; b[1] = c(); b = (Jo = tkk || "") || ""; } var d = Ho(String.fromCharCode(116)); c = Ho(String.fromCharCode(107)); d = [d(), d()]; d[1] = c(); d = b.split("."); b = Number(d[0]) || 0; for (var e = [], f = 0, g = 0; g < a.length; g++) { var k = a.charCodeAt(g); 128 > k ? e[f++] = k : (2048 > k ? e[f++] = k >> 6 | 192 : (55296 == (k & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (k = 65536 + ((k & 1023) << 10) + (a.charCodeAt(++g) & 1023), e[f++] = k >> 18 | 240, e[f++] = k >> 12 & 63 | 128) : e[f++] = k >> 12 | 224, e[f++] = k >> 6 & 63 | 128), e[f++] = k & 63 | 128); } a = b; for (f = 0; f < e.length; f++) { a += e[f], a = Io(a, "+-a^+6"); } a = Io(a, "+-3^+b+-f"); a ^= Number(d[1]) || 0; 0 > a && (a = (a & 2147483647) + 2147483648); a %= 1E6; return a.toString() + "." + (a ^ b); } })();