- // ==UserScript==
- // @name Disney+ Audio Downloader
- // @name:fr Disney+ Audio Downloader
- // @namespace https://gf.qytechs.cn/users/572942-stegner
- // @homepage https://gf.qytechs.cn/scripts/405994-disney-audio-downloader
- // @description Download audio from Disney+
- // @description:fr Télécharger l'audio de Disney+
- // @version 1.9
- // @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");
- }
- }
- 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);