您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows you to download subtitles from Netflix
当前为
// ==UserScript== // @name Netflix subtitle downloader // @description Allows you to download subtitles from Netflix // @namespace tithen-firion // @include https://www.netflix.com/* // @version 1.8 // @require https://gf.qytechs.cn/scripts/26651-xhrhijacker/code/xhrHijacker.js?version=171120 // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.js // @grant GM_registerMenuCommand // ==/UserScript== function pad(n, w) { n = n + ''; w = w || 2; return n.length >= w ? n : new Array(w - n.length + 1).join(0) + n; } function downloadThis() { if(typeof subFile === "undefined") window.setTimeout(downloadThis, 100); else { var blob = new Blob([subFile.content], {type: "text/plain;charset=utf-8"}); saveAs(blob, subFile.name, true); } } function downloadAll() { batch = true; if(typeof subFile === "undefined") window.setTimeout(downloadThis, 100); else { zip = zip || new JSZip(); zip.file(subFile.name, subFile.content); var el = document.querySelector(".player-next-episode:not(.player-hidden), .button-nfplayerNextEpisode"); if(el) el.click(); else zip.generateAsync({type:"blob"}) .then(function(content) { saveAs(content, seriesTitle + ".zip"); zip = undefined; batch = false; }); } } function formatTime(time) { var tmp = time; var ms = pad(time%1000, 3); time = Math.floor(time/1000); var s = pad(time%60); time = Math.floor(time/60); var m = pad(time%60); var h = pad(Math.floor(time/60)); return h + ":" + m + ":" + s + "," + ms; } function saveAsSrt(subs, filename) { txt = ""; subs.forEach(function(sub, i) { txt += (i+1) + "\n" + formatTime(sub.s) + " --> " + formatTime(sub.e) + "\n" + sub.t + "\n\n"; }); subFile = { name: filename + ".srt", content: txt }; if(batch) downloadAll(); } function toText(node, styles) { var txt = ""; var children = node.childNodes; for(let i = 0; i < children.length; ++i) { if(children[i].nodeType === 3) txt += children[i].textContent; else if(children[i].nodeType === 1) { if(children[i].nodeName.toUpperCase() === "BR") txt += "\n"; else txt += toText(children[i], styles); } } if(node.hasAttribute("style")) { var s = node.getAttribute("style"); if(s in styles) txt = styles[s].s + txt + styles[s].e; } return txt; } function styleParserHelper(style, styleElem, attribute, expectedValue, tag, colour) { var closeTag = false; if(styleElem.hasAttribute(attribute)) { let value = styleElem.getAttribute(attribute).trim(); let equal = value === expectedValue; if(colour) { if(!equal) { style.s = "<" + tag + ' color="' + value + '">' + style.s; closeTag = true; } } else if(equal) { style.s = "<" + tag + ">" + style.s; closeTag = true; } if(closeTag) style.e += "</" + tag + ">"; } } function processXml(xml, filename) { try { var styles = {}, prevStart = -1, subs = [{s: 0, e: 500, t: "Subtitles downloaded with 'Netflix subtitle downloader' UserScript by Tithen-Firion."}]; var styleElems = xml.querySelectorAll("styling style"); for(let i = 0; i < styleElems.length; ++i) { let id = styleElems[i].getAttribute("xml:id"); styles[id] = {s: "", e: ""}; styleParserHelper(styles[id], styleElems[i], "tts:fontWeight", "bold", "b"); styleParserHelper(styles[id], styleElems[i], "tts:fontStyle", "italic", "i"); styleParserHelper(styles[id], styleElems[i], "tts:textDecoration", "underline", "u"); styleParserHelper(styles[id], styleElems[i], "tts:color", "white", "font", true); if(styles[id].s === "") delete styles[id]; } var subElems = xml.querySelectorAll("div p"); for(let i = 0; i < subElems.length; ++i) { let el = subElems[i]; let start = Math.round(parseInt(el.getAttribute("begin"))/10000); let end = Math.round(parseInt(el.getAttribute("end"))/10000); let txt = toText(el, styles); if(start === prevStart) subs[subs.length-1].t += "\n" + txt; else subs.push({s: start, e: end, t: txt}); prevStart = start; } } catch(e) { console.error(e); alert('Failed to convert to SRT format'); return; } saveAsSrt(subs, filename); } function processResponse(responseText) { try{ var el = document.querySelector(".player-status-main-title, .ellipsize-text>h4"); if(el === null) { window.setTimeout(processResponse, 200, responseText); return; } var title = seriesTitle = el.innerText.replace(/[:*?"<>|\\\/]+/g, "_").replace(/ /g, ".") + "."; var nextEl = el.nextElementSibling; if(nextEl) { var m = nextEl.innerText.match(/^[^\d]*?(\d+)[^\d]*?(\d+)[^\d]*?$/); if(m && m.length === 3) title += "S" + pad(m[1]) + "E" + pad(m[2]) + ".WEBRip.Netflix"; } var selectedSubs = document.querySelector(".player-timed-text-tracks > .player-track-selected"); if(selectedSubs !== null) title += "." + selectedSubs.getAttribute("data-id").split(";")[2]; try { var parser = new DOMParser(); var xmlDoc = parser.parseFromString(responseText, "text/xml"); } catch(e) { console.error(e); alert('Failed to parse XML subtitle file'); return; } processXml(xmlDoc, title); }catch(e){console.log(e)} } var IDs = [], batch = false, seriesTitle, zip, subFile; xhrHijacker(function(xhr, id, origin, args) { if(origin === "open") { if(args[1].indexOf("/?o=") > -1) IDs.push(id); } else if(origin === "load") { var index = IDs.indexOf(id); if(index > -1) { IDs.splice(index, 1); processResponse(xhr.response); } } }); GM_registerMenuCommand("Download subs for this episode", downloadThis); GM_registerMenuCommand("Download subs from this ep till last available", downloadAll);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址