// ==UserScript==
// @name Disney+ Subtitles Downloader
// @name:fr Disney+ Subtitles Downloader
// @namespace https://gf.qytechs.cn/fr/users/572942-stegner
// @homepage https://gf.qytechs.cn/fr/scripts/404223-disney-subtitles-downloader
// @description Download subtitles from Disney+
// @description:fr Télécharger les sous-titres de Disney+
// @version 2.1
// @author stegner
// @match https://www.disneyplus.com/*
// @grant none
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// ==/UserScript==
function start(){
if (typeof document.initsub !== "undefined") {
document.initsub();
}
if (typeof document.initaudio !== "undefined") {
document.initaudio();
}
listensend();
document.handleinterval = setInterval(buttonhandle,100);
}
window.onload = start;
document.initsub = function(){
document.langs = new Array();
document.segments = "";
document.forced=false;
document.wait=false;
document.m3u8found=false;
document.url=null;
document.oldlocation=null;
document.filename="";
document.episode="";
document.downloadall=false;
document.downloadid=0;
document.waitsub=false;
var segid=0;
var vtturl;
// Add download icon
document.styleSheets[0].addRule('#subtitleTrackPicker > div','--percent: "All";');
document.styleSheets[0].addRule('#subtitleTrackPicker > div:before','content:"";color:#fff;padding-right:25px;padding-top:2px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIGNIUk0AAHonAACAgwAA+mQAAIDSAAB2hgAA7OkAADmeAAAV/sZ+0zoAAAE4SURBVHja1JS7LkRRFIa/M6aYRCEuCUEUgihFBolGVGqiFY1ConfpNB7CiygUGm8hOiMukwiCCMl8mj2xc5yZM8M0/mTlrLP2v75zydo7UclRL3AGlIAl4L6ZuUC+5oEZYBoo55lbAdai/LPTwFongG3pfwI3gZ3ovhjlXVG+BWz/6FbjKPuto1CbjWoLobYf1RZjRho4pt5F5g11QK2F6FFXo/UXdbwZEHVQvY2aztWPECdR/TkNawREHUpB03pSJ7J6Cf9gL3xOvDiiXmfAHtSplLek7qorqI/BeJjxxFG1kgNDPQjrn4VoLPozRqgCzAGXwFXILzJ8w+H6XgRegW7grcGs3gCTOfP8UgfGg139wwapxrugDl0H+oCkTZjAcsiTxBaO7HZUBI6BtfCmv4Un4aw8/RoA7wq6AO4uOhAAAAAASUVORK5CYII=) no-repeat right;width:20px;height:20px;position:absolute;top:6px;right:10px;opacity:0.6;cursor:pointer;');
document.styleSheets[0].addRule('#subtitleTrackPicker > div:hover:before','opacity:1;');
document.styleSheets[0].addRule('#subtitleTrackPicker > div:first-child:before','content:var(--percent);');
};
// Catch M3U8 files
function listensend(){
XMLHttpRequest.prototype.realSend = XMLHttpRequest.prototype.send;
var newSend = function(vData) {
if(!document.m3u8found) {
try {
var objData = JSON.parse(vData);
if(typeof objData.url !== 'undefined') {
if(objData.url.indexOf(".m3u8")>0 && document.url!=objData.url) {
// m3u8 url
document.url = objData.url;
document.langs = [];
document.baseurl=document.url.substring(0,document.url.lastIndexOf('/')+1);
document.m3u8found=true;
getpagecontent(m3u8loaded,document.url);
}
}
} catch (error) {}
}
this.realSend(vData);
}
XMLHttpRequest.prototype.send = newSend;
}
function m3u8loaded(response) {
if (typeof document.m3u8sub !== "undefined") {
document.m3u8sub(response);
}
if (typeof document.m3u8audio !== "undefined") {
document.m3u8audio(response);
}
}
document.m3u8sub = function(response){
var lines = response.split('#');
var found = false;
lines.forEach(function(line) {
if(line.indexOf('GROUP-ID="sub-main"')>0) {
// sub infos
document.langs.push(linetoarray(line));
}
else if(line.indexOf('.vtt')>0 && !found && line.indexOf('BUMPER')<0) {
// vtt url
var uri = document.langs[document.langid].URI;
var url = line.split(',')[1].substring(1);
found=true;
if(url.indexOf("/")<0){
url = uri.substring(2,uri.lastIndexOf("/")+1) + url;
}
getsegment(document.baseurl+uri.substring(0,2)+url,0);
}
});
}
function vttloaded(response) {
if(response.length>0) {
// save segment
document.segments+=response.substring(response.indexOf("-->")-13);
getsegment(vtturl,segid+1);
}
else if(document.segments.length>0) {
// export segments
exportfile(vtttosrt(document.segments));
}
else {
alert("Unknown error, please report a bug for this video.");
}
}
function vtttosrt(vtt) {
var lines = vtt.split(/\r\n|\r|\n/);
var result = [];
var subcount = 0;
lines.forEach(function (line) {
if(line.indexOf("-->") == 13) {
subcount++;
result.push(subcount);
result.push(line.substring(0,29).replace(/[.]/g,','));
}
else if(subcount>0) {
result.push(line.replace(/<\/?c(\.\w{1,})?>/g,'').replace(/&/g,'&'));
}
});
return result.join('\r\n');
}
function linetoarray(line) {
var result = new Array();
var values = line.split(',');
values.forEach(function(value) {
var data = value.replace(/\r\n|\r|\n/g,'').split('=');
if(data.length>1) {
var key = data[0];
var content = data[1].replace(/"/g,'');
result[key]=content;
}
});
return result;
}
function buttonhandle() {
var buttons = document.getElementsByClassName("control-icon-btn");
if(buttons.length>0) {
if (typeof document.clickhandlesub !== "undefined") {
document.clickhandlesub();
}
if (typeof document.clickhandleaudio !== "undefined") {
document.clickhandleaudio();
}
document.filename = document.getElementsByClassName("title-field")[0].innerText;
if(document.getElementsByClassName("subtitle-field").length>0) {
document.episode = document.getElementsByClassName("subtitle-field")[0].innerText
}
}
if(document.oldlocation!=window.location.href&&document.oldlocation!=null) {
// location changed
document.m3u8found=false;
document.langs = [];
document.audios = [];
}
document.oldlocation=window.location.href;
}
document.clickhandlesub = function() {
var picker = document.getElementsByClassName("options-picker subtitle-track-picker");
picker[0].childNodes.forEach(function(child) {
var element = child.childNodes[0];
if(child.onclick==null) {
child.onclick = selectsub;
}
});
}
function selectsub(e) {
var width = this.offsetWidth;
// Check click position
if(e.layerX>=width-30&&e.layerX<=width-10&&e.layerY>=5&&e.layerY<=25){
var lang = this.childNodes[0].childNodes[1].innerHTML;
if(lang=="Off"){
// Download all subs
document.zip = new JSZip();
document.downloadall=true;
document.downloadid=-1;
downloadnext();
}
else {
// Download sub
download(lang);
}
// Cancel selection
return false;
}
}
function downloadnext(){
document.downloadid++;
while(typeof document.langs[document.downloadid] !== 'undefined' && document.langs[document.downloadid].FORCED=="YES"){
document.downloadid++;
}
if(document.downloadid<document.langs.length){
document.styleSheets[0].addRule('#subtitleTrackPicker > div:first-child:before','padding-right:35px;content:"'+Math.round((document.downloadid/document.langs.length)*100)+'%";');
download(document.langs[document.downloadid].NAME);
}
else {
clearInterval(document.downloadinterval);
document.styleSheets[0].addRule('#subtitleTrackPicker > div:first-child:before','padding-right:25px;content:"All";');
document.zip.generateAsync({type:"blob"}).then(function(content) {
var output = document.filename;
if(document.episode!="") {
output+= " - "+document.episode.replace(':','');
}
saveAs(content, output+".zip");
});
}
}
function download(langname) {
if(!document.wait){
var language;
var count=0;
document.forced=false;
document.langs.forEach(function(lang) {
if(lang.NAME==langname) {
language=lang.LANGUAGE;
document.langid=count;
getpagecontent(m3u8loaded,document.baseurl+lang.URI);
document.wait=true;
}
count++;
});
document.langs.forEach(function(lang) {
if(lang.LANGUAGE==language && lang.NAME!=langname && lang.FORCED=="YES") {
document.waitsub=true;
document.waitInterval = setInterval(function () {
if(!document.wait) {
document.forced=true;
clearInterval(document.waitInterval);
getpagecontent(m3u8loaded,document.baseurl+lang.URI);
document.wait=true;
}
},10);
}
});
if(count==0){
alert("An error has occurred, please reload the page.");
}
}
}
function getsegment(url,seg) {
if(seg==0) {
document.segments="";
}
segid = seg;
vtturl = url;
getpagecontent(vttloaded,vtturl.substring(0,vtturl.length-10)+segid.toString().lpad("0", 5)+".vtt");
}
function exportfile(text) {
var output = document.filename;
if(document.episode!="") {
output+= " - "+document.episode.replace(':','');
}
output += "."+document.langs[document.langid].LANGUAGE;
if(document.forced) {
output += ".forced";
document.waitsub=false;
}
output += ".srt";
if(document.downloadall){
document.zip.file(output, text);
document.downloadinterval = setTimeout(function () {
document.wait = false;
if(!document.waitsub){
downloadnext();
}
},20);
}
else {
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/text,' + encodeURI(text);
hiddenElement.target = '_blank';
hiddenElement.download = output;
hiddenElement.click();
setTimeout(function () { document.wait = false; },50);
}
}
function getpagecontent(callback,url) {
var http=new XMLHttpRequest();
http.open("GET", url, true);
http.onloadend = function() {
if(http.readyState == 4 && http.status == 200) {
callback(http.responseText);
}
else if (http.status === 404) {
callback("");
}
}
http.send();
}
String.prototype.lpad = function(padString, length) {
var str = this;
while (str.length < length) {
str = padString + str;
}
return str;
}