// ==UserScript==
// @name Disney+ Audio Downloader
// @name:fr Disney+ Audio Downloader
// @namespace https://greasyfork.org/users/572942-stegner
// @homepage https://greasyfork.org/scripts/405994-disney-audio-downloader
// @description Download audio from Disney+
// @description:fr Télécharger l'audio de Disney+
// @version 1.8
// @author stegner
// @license MIT; https://opensource.org/licenses/MIT
// @match https://www.disneyplus.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function(open, send) {
'use strict';
var debug = (location.hash=="#debug");
debuglog("Script loaded : Disney+ Audio Downloader");
function init(){
debuglog("Document state : "+document.readyState);
if (document.readyState == "complete" || document.readyState == "loaded"){
start();
debuglog("Already loaded");
}
else {
if (window.addEventListener) {
window.addEventListener("load", start, false);
debuglog("Onload method : addEventListener");
} else if (window.attachEvent) {
window.attachEvent("onload", start);
debuglog("Onload method : attachEvent");
} else {
window.onload = start;
debuglog("Onload method : onload");
}
}
// Globalization fallback
document.globalization = {
"audio": [
{
"language": "en",
"renditions": {
"primary": "English",
"descriptive": "English [Audio Description]"
}
},
{
"language": "en-AU",
"renditions": {
"primary": "English (Australia)",
"descriptive": "English (Australia) [Audio Description]"
}
},
{
"language": "en-GB",
"renditions": {
"primary": "English (UK)",
"descriptive": "English (UK) [Audio Description]"
}
},
{
"language": "nl",
"renditions": {
"primary": "Nederlands",
"descriptive": "Nederlands [Audiobeschrijving]"
}
},
{
"language": "nl-BE",
"renditions": {
"primary": "Vlaams",
"descriptive": "Vlaams [Audiobeschrijving]"
}
},
{
"language": "fr-FR",
"renditions": {
"primary": "Français",
"descriptive": "Français [Audiodescription]"
}
},
{
"language": "fr-CA",
"renditions": {
"primary": "Français (Canada)",
"descriptive": "Français (Canada) [Vidéodescription]"
}
},
{
"language": "es-419",
"renditions": {
"primary": "Español (Latinoamérica)",
"descriptive": "Español (Latinoamérica) [Audiodescripción]"
}
},
{
"language": "es-ES",
"renditions": {
"primary": "Español",
"descriptive": "Español [Audiodescripción]"
}
},
{
"language": "it",
"renditions": {
"primary": "Italiano",
"descriptive": "Italiano [Audiodescrizione]"
}
},
{
"language": "de",
"renditions": {
"primary": "Deutsch",
"descriptive": "Deutsch [Audiodeskription]"
}
},
{
"language": "de-AT",
"renditions": {
"primary": "Deutsch",
"descriptive": "Deutsch (Österreich) [Audiodeskription]"
}
},
{
"language": "da",
"renditions": {
"primary": "Dansk",
"descriptive": "Dansk [Synstolkning]"
}
},
{
"language": "fi",
"renditions": {
"primary": "Suomi",
"descriptive": "Suomi [Kuvailutulkkaus]"
}
},
{
"language": "no",
"renditions": {
"primary": "Norsk",
"descriptive": "Norsk [Synstolking]"
}
},
{
"language": "sv",
"renditions": {
"primary": "Svenska",
"descriptive": "Svenska [Syntolkning]"
}
},
{
"language": "pt-PT",
"renditions": {
"primary": "Português",
"descriptive": "Português [Audiodescrição]"
}
},
{
"language": "pt-BR",
"renditions": {
"primary": "Português (Brasil)",
"descriptive": "Português (Brasil) [Audiodescrição]"
}
},
{
"language": "pl",
"renditions": {
"primary": "Polski",
"descriptive": "Polski [Audiodeskrypcja]"
}
},
{
"language": "ja",
"renditions": {
"primary": "日本語",
"descriptive": "日本語 [副音声]"
}
},
{
"language": "zh-HK",
"renditions": {
"primary": "Chinese (粵語)",
"descriptive": "Chinese (粵語) [口述影像]"
}
},
{
"language": "zh-Hans",
"renditions": {
"primary": "Chinese (普通话)",
"descriptive": "Chinese (普通话) [口述影像]"
}
},
{
"language": "zh-Hant",
"renditions": {
"primary": "Chinese (中文)",
"descriptive": "Chinese (中文) [口述影像]"
}
},
{
"language": "yue",
"renditions": {
"primary": "Chinese (粵語)",
"descriptive": "Chinese (粵語) [口述影像]"
}
},
{
"language": "cmn",
"renditions": {
"primary": "Chinese (普通话)",
"descriptive": "Chinese (普通话) [口述影像]"
}
},
{
"language": "cmn-TW",
"renditions": {
"primary": "Chinese (中文)",
"descriptive": "Chinese (中文) [口述影像]"
}
},
{
"language": "ca",
"renditions": {
"primary": "Català",
"descriptive": "Català [Audiodescripció]"
}
},
{
"language": "is",
"renditions": {
"primary": "Íslenska",
"descriptive": "Íslenska [hljóðlýsing]"
}
},
{
"language": "ko",
"renditions": {
"primary": "한국어",
"descriptive": "한국어 [화면 해설]"
}
},
{
"language": "cs",
"renditions": {
"primary": "Čeština",
"descriptive": "Čeština [zvukový popis]"
}
},
{
"language": "sk",
"renditions": {
"primary": "Slovenčina",
"descriptive": "Slovenčina [zvukový popis]"
}
},
{
"language": "ro",
"renditions": {
"primary": "Română",
"descriptive": "Română [Descriere audio]"
}
},
{
"language": "hu",
"renditions": {
"primary": "Magyar",
"descriptive": "Magyar [képnarrációval]"
}
},
{
"language": "tr",
"renditions": {
"primary": "Türkçe",
"descriptive": "Türkçe [Sesli Betimleme]"
}
},
{
"language": "th",
"renditions": {
"primary": "ไทย",
"descriptive": "ไทย [เสียงบรรยายภาพ]"
}
},
{
"language": "vi",
"renditions": {
"primary": "Tiếng Việt",
"descriptive": "Tiếng Việt [Mô tả âm thanh]"
}
},
{
"language": "ms",
"renditions": {
"primary": "Bahasa Melayu",
"descriptive": "Bahasa Melayu [Penerangan Audio]"
}
},
{
"language": "id",
"renditions": {
"primary": "Bahasa Indonesia",
"descriptive": "Bahasa Indonesia [Deskripsi Audio]"
}
},
{
"language": "el",
"renditions": {
"primary": "Ελληνικά",
"descriptive": "Ελληνικά [ηχητική περιγραφή]"
}
},
{
"language": "mi",
"renditions": {
"primary": "Māori",
"descriptive": "Māori [Whakaahuatanga orooro]"
}
}
],
"timedText": [
{
"language": "en",
"renditions": {
"captions": "English [CC]",
"subtitles": "English",
"forced": "en--forced--",
"sdh": "English [CC]"
}
},
{
"language": "en-AU",
"renditions": {
"captions": "English (Australia) [CC]",
"subtitles": "English (Australia)",
"forced": "en-AU--forced--",
"sdh": "English (Australia) [CC]"
}
},
{
"language": "en-GB",
"renditions": {
"captions": "English (UK) [CC]",
"subtitles": "English (UK)",
"forced": "en-GB--forced--",
"sdh": "English (UK) [CC]"
}
},
{
"language": "nl",
"renditions": {
"captions": "Nederlands [CC]",
"subtitles": "Nederlands",
"forced": "nl--forced--",
"sdh": "Nederlands [CC]"
}
},
{
"language": "nl-BE",
"renditions": {
"captions": "Vlaams [CC]",
"subtitles": "Vlaams",
"forced": "nl-BE--forced--",
"sdh": "Vlaams [CC]"
}
},
{
"language": "fr-FR",
"renditions": {
"captions": "Français [CC]",
"subtitles": "Français",
"forced": "fr-FR--forced--",
"sdh": "Français [CC]"
}
},
{
"language": "fr-CA",
"renditions": {
"captions": "Français (Canada) [CC]",
"subtitles": "Français (Canada)",
"forced": "fr-CA--forced--",
"sdh": "Français (Canada) [CC]"
}
},
{
"language": "it",
"renditions": {
"captions": "Italiano [CC]",
"subtitles": "Italiano",
"forced": "it--forced--",
"sdh": "Italiano [CC]"
}
},
{
"language": "de",
"renditions": {
"captions": "Deutsch [CC]",
"subtitles": "Deutsch",
"forced": "de--forced--",
"sdh": "Deutsch [CC]"
}
},
{
"language": "es-ES",
"renditions": {
"captions": "Español [CC]",
"subtitles": "Español",
"forced": "es-ES--forced--",
"sdh": "Español [CC]"
}
},
{
"language": "es-419",
"renditions": {
"captions": "Español (Latinoamérica) [CC]",
"subtitles": "Español (Latinoamérica)",
"forced": "es-419--forced--",
"sdh": "Español (Latinoamérica) [CC]"
}
},
{
"language": "da",
"renditions": {
"captions": "Dansk [CC]",
"subtitles": "Dansk",
"forced": "da--forced--",
"sdh": "Dansk [CC]"
}
},
{
"language": "fi",
"renditions": {
"captions": "Suomi [CC]",
"subtitles": "Suomi",
"forced": "fi--forced--",
"sdh": "Suomi [CC]"
}
},
{
"language": "nb",
"renditions": {
"captions": "Norsk (bokmål) [CC]",
"subtitles": "Norsk (bokmål)",
"forced": "no--forced--",
"sdh": "Norsk (bokmål) [CC]"
}
},
{
"language": "no",
"renditions": {
"captions": "Norsk (bokmål) [CC]",
"subtitles": "Norsk (bokmål)",
"forced": "no--forced--",
"sdh": "Norsk (bokmål) [CC]"
}
},
{
"language": "sv",
"renditions": {
"captions": "Svenska [CC]",
"subtitles": "Svenska",
"forced": "sv--forced--",
"sdh": "Svenska [CC]"
}
},
{
"language": "pt-PT",
"renditions": {
"captions": "Português [CC]",
"subtitles": "Português",
"forced": "pt-PT--forced--",
"sdh": "Português [CC]"
}
},
{
"language": "pt-BR",
"renditions": {
"captions": "Português (Brasil) [CC]",
"subtitles": "Português (Brasil)",
"forced": "pt-BR--forced--",
"sdh": "Português (Brasil) [CC]"
}
},
{
"language": "zh-Hans",
"renditions": {
"captions": "Chinese (简体) [CC]",
"subtitles": "Chinese (简体)",
"forced": "zh-Hans--forced--",
"sdh": "Chinese (简体) [CC]"
}
},
{
"language": "zh-Hant",
"renditions": {
"captions": "Chinese (繁體) [CC]",
"subtitles": "Chinese (繁體)",
"forced": "zh-Hant--forced--",
"sdh": "Chinese (繁體) [CC]"
}
},
{
"language": "zh-HK",
"renditions": {
"captions": "Chinese (粵語) [CC]",
"subtitles": "Chinese (粵語)",
"forced": "zh-HK--forced--",
"sdh": "Chinese (粵語) [CC]"
}
},
{
"language": "ko",
"renditions": {
"captions": "한국어 [CC]",
"subtitles": "한국어",
"forced": "ko--forced--",
"sdh": "한국어 [CC]"
}
},
{
"language": "ja",
"renditions": {
"captions": "日本語 [CC]",
"subtitles": "日本語",
"forced": "ja--forced--",
"sdh": "日本語 [CC]"
}
},
{
"language": "id",
"renditions": {
"captions": "Bahasa Indonesia [CC]",
"subtitles": "Bahasa Indonesia",
"forced": "id--forced--",
"sdh": "Bahasa Indonesia [CC]"
}
},
{
"language": "vi",
"renditions": {
"captions": "Tiếng Việt [CC]",
"subtitles": "Tiếng Việt",
"forced": "vi--forced--",
"sdh": "Tiếng Việt [CC]"
}
},
{
"language": "th",
"renditions": {
"captions": "ไทย [CC]",
"subtitles": "ไทย",
"forced": "th--forced--",
"sdh": "ไทย [CC]"
}
},
{
"language": "ms",
"renditions": {
"captions": "Bahasa Melayu [CC]",
"subtitles": "Bahasa Melayu",
"forced": "ms--forced--",
"sdh": "Bahasa Melayu [CC]"
}
},
{
"language": "el",
"renditions": {
"captions": "Ελληνικά [CC]",
"subtitles": "Ελληνικά",
"forced": "el--forced--",
"sdh": "Ελληνικά [CC]"
}
},
{
"language": "pl",
"renditions": {
"captions": "Polski [CC]",
"subtitles": "Polski",
"forced": "pl--forced--",
"sdh": "Polski [CC]"
}
},
{
"language": "is",
"renditions": {
"captions": "Íslenska [CC]",
"subtitles": "Íslenska",
"forced": "is--forced--",
"sdh": "Íslenska [CC]"
}
},
{
"language": "ca",
"renditions": {
"captions": "Català [CC]",
"subtitles": "Català",
"forced": "ca-forced--",
"sdh": "Català [CC]"
}
},
{
"language": "de-AT",
"renditions": {
"captions": "Deutsch (Österreich) [CC]",
"subtitles": "Deutsch (Österreich)",
"forced": "de-AT--forced--",
"sdh": "Deutsch (Österreich) [CC]"
}
},
{
"language": "tr",
"renditions": {
"captions": "Türkçe [CC]",
"subtitles": "Türkçe",
"forced": "tr--forced--",
"sdh": "Türkçe [CC]"
}
},
{
"language": "cs",
"renditions": {
"captions": "Čeština [CC]",
"subtitles": "Čeština",
"forced": "cs--forced--",
"sdh": "Čeština [CC]"
}
},
{
"language": "sk",
"renditions": {
"captions": "Slovenčina [CC]",
"subtitles": "Slovenčina",
"forced": "sk--forced--",
"sdh": "Slovenčina [CC]"
}
},
{
"language": "ro",
"renditions": {
"captions": "Română [CC]",
"subtitles": "Română",
"forced": "ro--forced--",
"sdh": "Română [CC]"
}
},
{
"language": "hu",
"renditions": {
"captions": "Magyar [CC]",
"subtitles": "Magyar",
"forced": "hu--forced--",
"sdh": "Magyar [CC]"
}
}
]
}
document.listen=true;
}
function start(){
debuglog("start");
if (typeof document.initaudio !== "undefined") {
document.initaudio();
}
if (typeof document.initsub !== "undefined") {
document.initsub();
}
listensend();
document.handleinterval = setInterval(buttonhandle,100);
}
if(!document.listen){
init();
}
document.initaudio = function(){
debuglog("initaudio");
document.audios = [];
document.content = new Uint8Array();
document.baseurl="";
document.m3u8found=false;
document.wait=false;
document.downloading=false;
document.filename="";
document.episode="";
document.audioid=null;
// Add download icon
document.styleSheets[0].addRule('#audioTrackPicker > div:before','content:"";color:#fff;padding-right:35px;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:18px;position:absolute;top:6px;right:10px;opacity:0.6;cursor:pointer;');
document.styleSheets[0].addRule('#audioTrackPicker > div:hover:before','opacity:1;');
}
// Catch M3U8 files
function listensend(){
debuglog("listensend");
var newOpen = function(...args) {
if(!document.m3u8found && args.length>=2){
if(args[1].indexOf(".m3u8")>0 && document.url!=args[1]) {
// m3u8 url
debuglog("m3u8 found : "+args[1]);
document.url = args[1];
document.langs = [];
document.baseurl=document.url.substring(0,document.url.lastIndexOf('/')+1);
document.m3u8found=true;
getpagecontent(m3u8loaded,document.url);
}
}
open.call(this,...args);
}
var newSend = function(...args) {
if(args[0] && args[0].match && args[0].match(/globalization/)){
this.addEventListener('readystatechange', function(e) {
try {
document.globalization = JSON.parse(e.target.response).data.globalization;
} catch(e) {}
}, false);
}
send.call(this,...args);
}
if(typeof unsafeWindow !== "undefined"){
debuglog("Window state : unsafe");
var define = Object.defineProperty;
define(unsafeWindow.XMLHttpRequest.prototype, "open", {value: exportFunction(newOpen, window)});
define(unsafeWindow.XMLHttpRequest.prototype, "send", {value: exportFunction(newSend, window)});
}
else {
debuglog("Window state : safe");
XMLHttpRequest.prototype.open = newOpen;
XMLHttpRequest.prototype.send = newSend;
}
}
function m3u8loaded(response) {
debuglog("m3u8loaded");
if (typeof document.m3u8sub !== "undefined") {
document.m3u8sub(response);
}
if (typeof document.m3u8audio !== "undefined") {
document.m3u8audio(response);
}
}
document.m3u8audio = function(response){
var lines = response.split('#');
var found = false;
if(lines[2].indexOf("EXT-X-INDEPENDENT-SEGMENTS")==0){
// Audio tracks list
var quality=null;
lines.forEach(function(line) {
if(line.indexOf('TYPE=AUDIO')>0) {
var lang = linetoarray(line);
lang.LOCALIZED = document.globalization.audio.find(t => t.language == lang.LANGUAGE);
// audio infos
if(line.indexOf('GROUP-ID="eac-3"')>0 && (quality==null||quality=="eac-3")){
quality="eac-3";
document.audios.push(lang);
debuglog("Audio found : "+document.audios[document.audios.length-1].NAME);
}
else if(line.indexOf('GROUP-ID="aac-128k"')>0 && (quality==null||quality=="aac-128k")){
quality="aac-128k";
document.audios.push(lang);
debuglog("Audio found : "+document.audios[document.audios.length-1].NAME);
}
else if(line.indexOf('GROUP-ID="aac-64k"')>0 && (quality==null||quality=="aac-64k")){
quality="aac-64k";
document.audios.push(lang);
debuglog("Audio found : "+document.audios[document.audios.length-1].NAME);
}
}
});
}
else if(response.indexOf(".mp4a")>0) {
downloadmp4a(response);
}
}
function downloadmp4a(m3u8data){
debuglog("downloadmp4a");
var lines = m3u8data.split(/\r?\n/g);
var mapfound=false;
var percent;
var i=0;
document.downloadInterval = setInterval(function () {
var line = lines[i];
var url=null;
if(line!=null){
var uri = document.audios[document.audioid].URI;
if(line.indexOf("map.mp4a")>0 && !mapfound){
// Get mp4a map
debuglog("Download map");
url = document.baseurl+uri.substring(0,uri.lastIndexOf("/")+1)+line.substring(line.indexOf('"')+1,line.lastIndexOf('"'));
mapfound=true;
}
else if(line.indexOf("_000.mp4a")>0 && line.indexOf("MAIN")>0){
// Get mp4a data
url = document.baseurl+uri.substring(0,uri.lastIndexOf("/")+1)+line;
}
if(url!=null && !document.downloading){
// Download file
getpagecontent(mp4aloaded,url,true);
document.downloading=true;
i++;
}
else if(url==null){
// Skip line
i++;
}
if(percent!=Math.round((i/lines.length)*100)){
percent=Math.round((i/lines.length)*100);
document.styleSheets[0].addRule('#audioTrackPicker > div:nth-child('+(document.audioid+1)+'):before','content:"'+percent+'%";');
}
}
else {
// Download finished
clearInterval(document.downloadInterval);
document.styleSheets[0].addRule('#audioTrackPicker > div:nth-child('+(document.audioid+1)+'):before','content:"";');
exportblob(document.content, 'video/mp4');
document.content=new Uint8Array();
document.wait=false;
}
},10);
}
function mp4aloaded(response) {
debuglog("mp4aloaded");
document.downloading=false;
document.content=appendbuffer(document.content,response);
}
function linetoarray(line) {
var result = [];
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.audios = [];
document.langs = [];
}
document.oldlocation=window.location.href;
}
document.clickhandleaudio = function() {
var picker = document.getElementsByClassName("options-picker audio-track-picker");
if(picker && picker[0]) {
picker[0].childNodes.forEach(function(child) {
var element = child.childNodes[0];
var lang = element.childNodes[1].innerHTML;
if(child.onclick==null) {
child.onclick = selectaudio;
}
});
}
}
function selectaudio(e) {
debuglog("selectaudio");
var width = this.offsetWidth;
// Check click position
if(e.layerX>=width-30&&e.layerX<=width-10&&e.layerY>=5&&e.layerY<=25){
// Download audio
download(this.childNodes[0].childNodes[1].innerHTML);
// Cancel selection
return false;
}
}
function download(langname) {
if(!document.wait){
debuglog("Download audio : "+langname);
var count=0;
document.audios.forEach(function(audio) {
if(audio.LOCALIZED && Object.values(audio.LOCALIZED.renditions).includes(langname)) {
document.audioid=count;
document.ad=(audio.NAME.indexOf("[Audio Description]")>0);
getpagecontent(m3u8loaded,document.baseurl+audio.URI);
document.wait=true;
}
count++;
});
if(count==0){
alert("An error has occurred, please reload the page.");
}
}
}
function getpagecontent(callback,url,binary) {
debuglog("Downloading : "+url);
var http=new XMLHttpRequest();
http.open("GET", url, true);
if(binary){
http.responseType = "arraybuffer";
}
http.onloadend = function() {
if(http.readyState == 4 && http.status == 200) {
if(binary){
callback(http.response);
}
else {
callback(http.responseText);
}
}
else if (http.status === 404) {
debuglog("Not found");
callback("");
}
else {
debuglog("Unknown error, retrying");
setTimeout(function () { getpagecontent(callback,url,binary); },100);
}
}
http.send();
}
function appendbuffer(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp;
};
// Save file as arraybuffer
function exportblob(data, mimeType) {
debuglog("exportblob");
var blob, url;
var output = document.filename;
if(document.episode!="") {
output+= " - "+document.episode.replace(':','');
}
output += "."+document.audios[document.audioid].LANGUAGE;
if(document.ad){
output +=".ad";
}
output += ".mp4";
blob = new Blob([data], {
type: mimeType
});
url = window.URL.createObjectURL(blob);
downloadurl(url, output);
setTimeout(function() {
return window.URL.revokeObjectURL(url);
}, 1000);
};
function downloadurl(data, fileName) {
debuglog("Save audio");
var a;
a = document.createElement('a');
a.href = data;
a.download = fileName;
document.body.appendChild(a);
a.style = 'display: none';
a.click();
a.remove();
};
function debuglog(message){
if(debug){
console.log("%c [debug] "+message, 'background: #222; color: #bada55');
}
}
})(XMLHttpRequest.prototype.open, XMLHttpRequest.prototype.send);