Greasy Fork

Disney+ Audio Downloader

Download audio from Disney+

目前为 2020-06-24 提交的版本。查看 最新版本

// ==UserScript==
// @name           Disney+ Audio Downloader
// @name:fr        Disney+ Audio Downloader
// @namespace      https://greasyfork.org/fr/users/572942-stegner
// @description    Download audio from Disney+
// @description:fr Télécharger l'audio de Disney+
// @version        1.0
// @author         stegner
// @match          https://www.disneyplus.com/*
// @grant          none
// ==/UserScript==

function start(){
	if (typeof document.initaudio !== "undefined") {
		document.initaudio();
	}
	if (typeof document.initsub !== "undefined") {
		document.initsub();
	}
	listensend();
    document.handleinterval = setInterval(buttonhandle,100);
}

window.onload = start;

document.initaudio = function(){
    document.audios = new Array();
	document.content = new Uint8Array();
	document.baseurl="";
	document.m3u8found=false;
	document.wait=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(){
    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.wait=false;
}

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) {
	            // audio infos
	            if(line.indexOf('GROUP-ID="eac-3"')>0 && (quality==null||quality=="eac-3")){
	            	quality="eac-3";
	            	document.audios.push(linetoarray(line));
	            }
	            else if(line.indexOf('GROUP-ID="aac-128k"')>0 && (quality==null||quality=="aac-128k")){
	            	quality="aac-128k";
	            	document.audios.push(linetoarray(line));
	            }
	            else if(line.indexOf('GROUP-ID="aac-64k"')>0 && (quality==null||quality=="aac-64k")){
	            	quality="aac-64k";
	            	document.audios.push(linetoarray(line));
	            }
	        }
	    });
	}
	else if(response.indexOf(".mp4a")>0) {
		//console.log(lines);
		downloadmp4a(response);
	}
    
}

function downloadmp4a(m3u8data){
	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
				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("DUB_CARD")<0){
				// Get mp4a data
				url = document.baseurl+uri.substring(0,uri.lastIndexOf("/")+1)+line;
			}

			if(url!=null && !document.wait){
				// Download file
				getpagecontent(mp4aloaded,url,true);
				document.wait=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();
		}
	},10);
}


function mp4aloaded(response) {
	document.content=appendbuffer(document.content,response);
	document.wait=false;
}

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.audios = [];
        document.langs = [];
    }

    document.oldlocation=window.location.href;
}

document.clickhandleaudio = function() {
    var picker = document.getElementsByClassName("options-picker audio-track-picker");
    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) {
    var width = this.offsetWidth;
    // Check click position
    if(e.layerX>=width-30&&e.layerX<=width-10&&e.layerY>=5&&e.layerY<=25){
        // Download sub
        download(this.childNodes[0].childNodes[1].innerHTML);
        // Cancel selection
        return false;
    }
}

function download(langname) {
    if(!document.wait){
    	var count=0;
        document.audios.forEach(function(audio) {
            if(audio.NAME==langname) {
                document.audioid=count;
                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) {
	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) {
			callback("");
		}
	}
	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) {
	var blob, url;
	var output = document.filename;
    if(document.episode!="") {
        output+= " - "+document.episode.replace(':','');
    }
    output += "."+document.audios[document.audioid].LANGUAGE;
    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) {
	var a;
	a = document.createElement('a');
	a.href = data;
	a.download = fileName;
	document.body.appendChild(a);
	a.style = 'display: none';
	a.click();
	a.remove();
};