-
-
Save Xiao-mingGitHub/ecccf55907ce020f4f6b02edf8868edd to your computer and use it in GitHub Desktop.
lynda.com 字幕翻译脚本,并支持下载视频和字幕文件
This file contains hidden or 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 lynda.com 字幕翻译 | |
// @description lynda.com 字幕翻译脚本,并支持下载视频和字幕文件 | |
// @namespace https://github.com/journey-ad | |
// @version 0.3.3 | |
// @icon https://cdn.lynda.com/static/favicon.ico | |
// @author journey-ad | |
// @match *://www.lynda.com/* | |
// @require https://cdn.jsdelivr.net/npm/[email protected]/download.min.js | |
// @license MIT | |
// @updateURL https://gist.githubusercontent.com/journey-ad/d3d2869d266c00188e6f99a33fdb669c/raw | |
// @run-at document-end | |
// @grant GM_xmlhttpRequest | |
// @grant GM_download | |
// @grant GM_notification | |
// ==/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;}@media (max-width: 768px){.mejs-container .mejs-captions-layer {font-size: 16px; line-height:18px;}}", | |
container = document.createElement("div"); | |
addStyle(css); | |
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 addDownloadTab() { | |
window.loading = true; | |
var courseId = unsafeWindow.lynda.courseId, | |
videoId = unsafeWindow.currentVideoId, | |
elVideoName = document.querySelector('#course-page .course-toc .toc-items .current .video-name'), | |
videoName = '{{ index }}_{{ zh }}_{{ en }}'.render({ | |
index: elVideoName.dataset.index, | |
en: elVideoName.dataset.original.replace(/\s+/g, '_'), | |
zh: elVideoName.dataset.translate.replace(/\s+/g, '') | |
}), | |
panelTemplate = videoDownloadTabTemplate, | |
videoData = { | |
class: '', | |
style: document.getElementsByClassName('tab-video-download')[0].classList.contains('selected') ? 'display:block' : 'display:none', | |
subtitleNameZH: videoName + '_chs.srt', | |
subtitleNameEN: videoName + '_eng.srt' | |
}, | |
_TIMECODE_REGEX = /\[(\d+:\d+:\d+[\.,]\d+)\]/; | |
getVideoUrl(courseId, videoId, function (data) { | |
data[0]['qualities'].forEach(function (quality) { | |
videoData['videoName' + quality] = videoName + '_' + quality + 'p.' + (quality === '64' ? 'mp3' : 'mp4'); | |
videoData['videoUrl' + quality] = data[0]['urls'][quality]; | |
}); | |
fixSubtitles(videoId, function (subtitles) { | |
var zhSrt = '', | |
enSrt = '', | |
seqCounter = 0; | |
for (var pos = 0; pos < subtitles.length - 1; pos++) { | |
var seqCurrent = subtitles[pos]; | |
var mCurrent = _TIMECODE_REGEX.exec(seqCurrent['Timecode']); | |
if (mCurrent === null) continue; | |
var seqNext = subtitles[pos + 1]; | |
var mNext = _TIMECODE_REGEX.exec(seqNext['Timecode']); | |
if (mNext === null) continue; | |
var appearTime = mCurrent[1].replace('.', ',').concat('0'); | |
var disappearTime = mNext[1].replace('.', ',').concat('0'); | |
var text = seqCurrent['Caption'].trim(); | |
if (text) { | |
seqCounter++; | |
text = text.replace(/\r?\n|\r/g, " ").trim(); | |
zhSrt += '{{ num }}\r\n{{ from }} --> {{ to }}\r\n{{ translate }}\r\n{{ text }}\r\n\r\n'.render({ | |
num: seqCounter, | |
from: appearTime, | |
to: disappearTime, | |
translate: window.subtitleTrans[seqCounter - 1], | |
text: text | |
}); | |
enSrt += '{{ num }}\r\n{{ from }} --> {{ to }}\r\n{{ text }}\r\n\r\n'.render({ | |
num: seqCounter, | |
from: appearTime, | |
to: disappearTime, | |
text: text | |
}); | |
} | |
} | |
window.downloadPanel = document.getElementById('tab-video-download'); | |
window.downloadPanel.outerHTML = panelTemplate.render(videoData); | |
document.getElementById('tab-video-download').addEventListener('click', function (e) { | |
var el = e.target; | |
while (el.tagName !== 'LI') { | |
el = el.parentNode; | |
if (el === document.getElementById('tab-video-download')) { | |
el = null; | |
break; | |
} | |
} | |
if (el) { | |
if (el.classList.contains('subtitle')) { | |
var lang = el.dataset.lang; | |
if (lang === 'zh') { | |
download(zhSrt, videoData['subtitleNameZH'], 'text/plain'); | |
} else { | |
download(enSrt, videoData['subtitleNameEN'], 'text/plain'); | |
} | |
} else if (el.classList.contains('quality')) { | |
el.classList.add('disable'); | |
el.classList.add('progress'); | |
var elProg = el.getElementsByClassName('file-prog')[0], | |
prog = 0, | |
quality = el.dataset.quality; | |
elProg.innerText = prog + '%'; | |
GM_download({ | |
url: videoData['videoUrl' + quality], | |
name: videoData['videoName' + quality], | |
saveAs: true, | |
onload: function onload() { | |
GM_notification("下载完成", videoData['videoName' + quality]); | |
el.classList.remove('disable'); | |
el.classList.remove('progress'); | |
}, | |
onerror: function onerror() { | |
GM_notification("下载失败", videoData['videoName' + quality]); | |
el.classList.remove('disable'); | |
}, | |
onprogress: function onprogress(e) { | |
prog = (e.loaded / e.total * 100).toFixed(2); | |
elProg.innerText = prog + '%'; | |
el.style.backgroundSize = prog + '% auto'; | |
} | |
}); | |
GM_notification("开始下载", videoData['videoName' + quality]); | |
} | |
} | |
}); | |
window.loading = false; | |
document.getElementById('tab-video-download').classList.remove('loading'); | |
console.log("下载面板添加完成"); | |
}); | |
}); | |
} | |
function getVideoUrl(courseId, videoId, callback) { | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: "https://www.lynda.com/ajax/course/".concat(courseId, "/").concat(videoId, "/play"), | |
headers: { | |
"accept": "application/json" | |
}, | |
onload: function onload(response) { | |
var data = JSON.parse(response.responseText); | |
callback(data); | |
} | |
}); | |
} | |
function fixSubtitles(videoId, callback) { | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: "https://www.lynda.com/ajax/player?videoId=".concat(videoId, "&type=transcript"), | |
headers: { | |
"accept": "application/json" | |
}, | |
onload: function onload(response) { | |
var data = JSON.parse(response.responseText); | |
callback(data); | |
} | |
}); | |
} | |
function transText() { | |
document.getElementById('tab-video-download').classList.add('loading'); | |
window.subtitleTrans = []; | |
var s = "", | |
r = "", | |
arr = [], | |
num = 0, | |
count = 0, | |
subtitle = entries.text; | |
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"); | |
window.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 = (window.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 + " 翻译完成"); | |
if (!window.loading) { | |
addDownloadTab(); | |
} | |
} | |
}); | |
} | |
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({ | |
text: text.trim(), | |
index: index | |
}, callback); | |
}); | |
return count; // 返回分块数量 | |
} | |
function server() { | |
var list = { | |
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:gh0nd9ybc4a7mvb2unqi" | |
}, | |
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); // 执行回调,在回调中拼接 | |
} | |
}); | |
} | |
}; | |
return list[transServer].apply(null, arguments); | |
} | |
var liVideoDownloadTemplate = "\n<li class=\"tab-video-download\" data-tab=\"video-download\">\n <a href=\"#tab\" id=\"video-download-tab\" aria-controls=\"tab-video-download\" tabindex=\"0\">Download</a>\n</li>"; | |
var videoDownloadTabTemplate = "\n<section id=\"tab-video-download\" class=\"{{ class }}\" aria-hidden=\"true\" style=\"{{ style }}\">\n <div class=\"video-download-tab \" id=\"video-download\" tabindex=\"0\">\n <div class=\"content unlocked\">\n <div class=\"col-xs-12 col-sm-9\">\n <div class=\"headline\">\n \u4E0B\u8F7D\u89C6\u9891\u548C\u5B57\u5E55\u6587\u4EF6\n </div>\n <p>\u5B57\u5E55\u6587\u4EF6\u683C\u5F0F\u4E3A.srt\uFF0C\u4E0B\u8F7D\u5728\u540E\u53F0\u8FDB\u884C\uFF0C\u8BF7\u8010\u5FC3\u7B49\u5F85</p>\n <ul class=\"video-download-popover\">\n <li class=\"subtitle subtitle-zh\" data-lang=\"zh\">\n <a href=\"javascript:void(0)\" class=\"course-file clearfix\" tabindex=\"0\">\n <span class=\"file-name col-xs-8 col-sm-9 col-xl-10\">{{ subtitleNameZH }}</span>\n <span class=\"file-type\">[CHS]</span>\n </a>\n </li>\n <li class=\"subtitle subtitle-en\" data-lang=\"en\">\n <a href=\"javascript:void(0)\" class=\"course-file clearfix\" tabindex=\"0\">\n <span class=\"file-name col-xs-8 col-sm-9 col-xl-10\">{{ subtitleNameEN }}</span>\n <span class=\"file-type\">[ENG]</span>\n </a>\n </li>\n <hr>\n <li class=\"quality quality-720\" data-quality=\"720\">\n <a href=\"javascript:void(0)\" class=\"course-file clearfix\" tabindex=\"0\">\n <span class=\"file-name col-xs-8 col-sm-9 col-xl-10\">{{ videoName720 }}</span>\n <span class=\"file-prog\">-%</span>\n <span class=\"file-type\">[HD720P]</span>\n </a>\n </li>\n <li class=\"quality quality-540\" data-quality=\"540\">\n <a href=\"javascript:void(0)\" class=\"course-file clearfix\" tabindex=\"0\">\n <span class=\"file-name col-xs-8 col-sm-9 col-xl-10\">{{ videoName540 }}</span>\n <span class=\"file-prog\">-%</span>\n <span class=\"file-type\">[SD540P]</span>\n </a>\n </li>\n <li class=\"quality quality-360\" data-quality=\"360\">\n <a href=\"javascript:void(0)\" class=\"course-file clearfix\" tabindex=\"0\">\n <span class=\"file-name col-xs-8 col-sm-9 col-xl-10\">{{ videoName360 }}</span>\n <span class=\"file-prog\">-%</span>\n <span class=\"file-type\">[360P]</span>\n </a>\n </li>\n <li class=\"quality quality-64\" data-quality=\"64\">\n <a href=\"javascript:void(0)\" class=\"course-file clearfix\" tabindex=\"0\">\n <span class=\"file-name col-xs-8 col-sm-9 col-xl-10\">{{ videoName64 }}</span>\n <span class=\"file-prog\">-%</span>\n <span class=\"file-type\">[Audio]</span>\n </a>\n </li>\n </ul>\n </div>\n <div class=\"hidden-xs col-sm-3\">\n <div class=\"files unlocked\">\n <div>\n <i class=\"lyndacon project-files\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"video-download-disclaimer\">\n <p>\u4E0B\u8F7D\u6B64\u6587\u4EF6\u610F\u5473\u7740\u4F60\u540C\u610F\u4F7F\u7528\u6B64\u6587\u4EF6\u7684\u884C\u4E3A\u987B\u7B26\u5408<a href=\"/aboutus/lotterms.aspx\" target=\"_blank\">\u670D\u52A1\u6761\u6B3E</a>\u4E2D\u7684\u7EA6\u5B9A\uFF0C\u4F60\u53EA\u80FD\u4EE5\u4E2A\u4EBA\u7528\u9014\u5728\u8BFE\u7A0B\u8BA2\u9605\u671F\u95F4\u89C2\u770B\u6B64\u6587\u4EF6\u4E2D\u7684\u5185\u5BB9\uFF0C\u4E0D\u5141\u8BB8\u516C\u5F00\u6216\u6563\u5E03\u6B64\u6587\u4EF6\u5185\u5BB9</p>\n </div>\n</section>"; | |
function addStyle(css) { | |
if (typeof GM_addStyle != "undefined") { | |
GM_addStyle(css); | |
} else if (typeof PRO_addStyle != "undefined") { | |
PRO_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); | |
} | |
} | |
} | |
String.prototype.render = function (context) { | |
return this.replace(/{{(.*?)}}/g, function (_match, key) { | |
return context[key.trim()]; | |
}); | |
}; | |
Number.prototype.pad = function(size) { | |
var s = String(this); | |
while (s.length < (size || 2)) {s = "0" + s;} | |
return s; | |
} | |
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, | |
b, | |
c, | |
d; | |
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) b = Jo; else { | |
b = Ho(String.fromCharCode(84)); | |
c = Ho(String.fromCharCode(75)); | |
b = [b(), b()]; | |
b[1] = c(); | |
b = (Jo = tkk || "") || ""; | |
} | |
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); | |
} | |
(function () { // 翻译目录&添加下载面板HTML | |
var videoNameList = document.querySelectorAll('.video-name'), | |
s = [], | |
r = []; | |
videoNameList.forEach(function (videoName, i) { | |
s[i] = videoName.innerText; | |
}); | |
translate(s.join('\n'), function (data) { | |
r = data.split('\n'); | |
videoNameList.forEach(function (videoName, i) { | |
videoName.dataset.index = i.pad(2), | |
videoName.dataset.original = videoName.innerText; | |
videoName.dataset.translate = r[i]; | |
videoName.innerText = r[i] + ' (' + videoName.innerText + ')'; | |
}); | |
}); | |
// 添加下载面板HTML | |
var css = '.video-download-tab{position:relative}.video-download-tab.fade .content{max-height:300px;padding-bottom:30px}.video-download-tab .content{max-height:338px;min-height:220px;overflow-y:auto;padding:30px 20px;}.video-download-tab .content .col-xs-6{position:static}.video-download-tab .content.locked{min-height:280px}.video-download-tab .content.unlocked li a:hover{background:#f7f7f7;text-decoration:none}.video-download-tab .headline{font-size:15px;font-weight:700;line-height:18px}.video-download-tab hr{margin:6px 0;}.video-download-tab .video-download-popover+.headline{margin-top:10px}.video-download-tab p{line-height:30px;margin:0}.video-download-tab li.disable{pointer-events:none;opacity:.5;}.video-download-tab li.progress{border-radius:6px;background:linear-gradient(#eee,#eee);background-size:0% auto;background-repeat:no-repeat;}.video-download-tab li .file-prog{display:none;}.video-download-tab li.progress .file-prog{display:inline-block;}.video-download-tab .quality-64 a .file-type{color:#9E9E9E;}.video-download-tab .quality-720 a .file-type,.video-download-tab .subtitle-zh a .file-type{color:#4ec57d;}.video-download-tab li a,.video-download-tab li>span{display:block;line-height:1em;padding:10px}.video-download-tab li a .lock,.video-download-tab li>span .lock{color:#999;font-size:10px;margin:-3px 0 0 -13px;vertical-align:text-top}.video-download-tab li a .file-type,.video-download-tab li>span .file-type{float:right;font-weight:bold;color:#4e5bc5;}.video-download-tab li a .file-name,.video-download-tab li>span .file-name{display:inline-block;line-height:1em;max-width:610px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.video-download-tab .files{margin-top:5px;text-align:center;z-index:2;float:right;margin-right:15px}.video-download-tab .files.unlocked .project-files{color:#ddd}.video-download-tab .files .project-files{color:#666;font-size:120px}.video-download-tab .files .discover{font-size:17px;line-height:21px}.video-download-tab .files button{display:block;margin-top:15px;width:100%}.video-download-disclaimer{margin-top:20px;border-top:2px solid #eee;padding:20px 30px}.video-download-disclaimer p{line-height:1.4em;margin-bottom:0}#tab-video-download.loading{pointer-events:none;}#tab-video-download.loading .video-download-tab .content{overflow:hidden;}#tab-video-download.loading p,#tab-video-download.loading span,#tab-video-download.loading .headline,#tab-video-download.loading .video-download-disclaimer a{box-sizing:border-box;color:transparent;background:#eee;padding:6px;}#tab-video-download.loading .video-download-disclaimer a,#tab-video-download.loading .headline{display:none;}#tab-video-download.loading p,#tab-video-download.loading span.file-name{box-sizing:border-box;color:transparent;background:#eee;padding:15px;margin:10px 0;animation-duration:2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:placeHolderShimmer;animation-timing-function:linear;background:#f6f7f8;background:linear-gradient(to right,#eeeeee 8%,#dddddd 18%,#eeeeee 33%);background-size:1400px 100px;}#tab-video-download.loading span.file-name{padding:6px;margin:0;}@keyframes placeHolderShimmer{0%{background-position:-200px 0}33%,100%{background-position:900px 0}}'; | |
addStyle(css); | |
window.downloadTab = document.createElement('li'); | |
document.getElementById('course-tabs').appendChild(window.downloadTab); | |
window.downloadTab.outerHTML = liVideoDownloadTemplate; | |
window.downloadPanel = document.createElement('section'); | |
document.getElementsByClassName('tab-container')[0].appendChild(window.downloadPanel); | |
window.downloadPanel.outerHTML = videoDownloadTabTemplate.render({ | |
class: 'loading', | |
style: document.getElementsByClassName('tab-video-download')[0].classList.contains('selected') ? 'display:block' : 'display:none' | |
}); | |
window.downloadPanel = document.getElementById('tab-video-download'); | |
})(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment