Disney+ Audio Downloader

Download audio from Disney+

  1. // ==UserScript==
  2. // @name Disney+ Audio Downloader
  3. // @name:fr Disney+ Audio Downloader
  4. // @namespace https://gf.qytechs.cn/users/572942-stegner
  5. // @homepage https://gf.qytechs.cn/scripts/405994-disney-audio-downloader
  6. // @description Download audio from Disney+
  7. // @description:fr Télécharger l'audio de Disney+
  8. // @version 1.9
  9. // @author stegner
  10. // @license MIT; https://opensource.org/licenses/MIT
  11. // @match https://www.disneyplus.com/*
  12. // @grant none
  13. // @run-at document-start
  14. // ==/UserScript==
  15.  
  16. (function(open, send) {
  17. 'use strict';
  18. var debug = (location.hash=="#debug");
  19. debuglog("Script loaded : Disney+ Audio Downloader");
  20.  
  21. function init(){
  22. debuglog("Document state : "+document.readyState);
  23. if (document.readyState == "complete" || document.readyState == "loaded"){
  24. start();
  25. debuglog("Already loaded");
  26. }
  27. else {
  28. if (window.addEventListener) {
  29. window.addEventListener("load", start, false);
  30. debuglog("Onload method : addEventListener");
  31. } else if (window.attachEvent) {
  32. window.attachEvent("onload", start);
  33. debuglog("Onload method : attachEvent");
  34. } else {
  35. window.onload = start;
  36. debuglog("Onload method : onload");
  37. }
  38. }
  39. document.listen=true;
  40. }
  41.  
  42. function start(){
  43. debuglog("start");
  44. if (typeof document.initaudio !== "undefined") {
  45. document.initaudio();
  46. }
  47. if (typeof document.initsub !== "undefined") {
  48. document.initsub();
  49. }
  50. listensend();
  51. document.handleinterval = setInterval(buttonhandle,100);
  52. }
  53.  
  54. if(!document.listen){
  55. init();
  56. }
  57.  
  58. document.initaudio = function(){
  59. debuglog("initaudio");
  60. document.audios = [];
  61. document.content = new Uint8Array();
  62. document.baseurl="";
  63. document.m3u8found=false;
  64. document.wait=false;
  65. document.downloading=false;
  66. document.filename="";
  67. document.episode="";
  68. document.audioid=null;
  69.  
  70. // Add download icon
  71. 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;');
  72. document.styleSheets[0].addRule('#audioTrackPicker > div:hover:before','opacity:1;');
  73. }
  74.  
  75. // Catch M3U8 files
  76. function listensend(){
  77. debuglog("listensend");
  78.  
  79. var newOpen = function(...args) {
  80. if(!document.m3u8found && args.length>=2){
  81. if(args[1].indexOf(".m3u8")>0 && document.url!=args[1]) {
  82. // m3u8 url
  83. debuglog("m3u8 found : "+args[1]);
  84. document.url = args[1];
  85. document.langs = [];
  86. document.baseurl=document.url.substring(0,document.url.lastIndexOf('/')+1);
  87. document.m3u8found=true;
  88. getpagecontent(m3u8loaded,document.url);
  89. }
  90. }
  91.  
  92. open.call(this,...args);
  93. }
  94.  
  95. var newSend = function(...args) {
  96. if(args[0] && args[0].match && args[0].match(/globalization/)){
  97. this.addEventListener('readystatechange', function(e) {
  98. try {
  99. document.globalization = JSON.parse(e.target.response).data.globalization;
  100. } catch(e) {}
  101. }, false);
  102. }
  103. send.call(this,...args);
  104. }
  105.  
  106. if(typeof unsafeWindow !== "undefined"){
  107. debuglog("Window state : unsafe");
  108. var define = Object.defineProperty;
  109. define(unsafeWindow.XMLHttpRequest.prototype, "open", {value: exportFunction(newOpen, window)});
  110. define(unsafeWindow.XMLHttpRequest.prototype, "send", {value: exportFunction(newSend, window)});
  111. }
  112. else {
  113. debuglog("Window state : safe");
  114. XMLHttpRequest.prototype.open = newOpen;
  115. XMLHttpRequest.prototype.send = newSend;
  116. }
  117. }
  118.  
  119. function m3u8loaded(response) {
  120. debuglog("m3u8loaded");
  121. if (typeof document.m3u8sub !== "undefined") {
  122. document.m3u8sub(response);
  123. }
  124. if (typeof document.m3u8audio !== "undefined") {
  125. document.m3u8audio(response);
  126. }
  127. }
  128.  
  129. document.m3u8audio = function(response){
  130. var lines = response.split('#');
  131. var found = false;
  132. if(lines[2].indexOf("EXT-X-INDEPENDENT-SEGMENTS")==0){
  133. // Audio tracks list
  134. var quality=null;
  135. lines.forEach(function(line) {
  136. if(line.indexOf('TYPE=AUDIO')>0) {
  137. var lang = linetoarray(line);
  138. lang.LOCALIZED = document.globalization.audio.find(t => t.language == lang.LANGUAGE);
  139. // audio infos
  140. if(line.indexOf('GROUP-ID="eac-3"')>0 && (quality==null||quality=="eac-3")){
  141. quality="eac-3";
  142. document.audios.push(lang);
  143. debuglog("Audio found : "+document.audios[document.audios.length-1].NAME);
  144. }
  145. else if(line.indexOf('GROUP-ID="aac-128k"')>0 && (quality==null||quality=="aac-128k")){
  146. quality="aac-128k";
  147. document.audios.push(lang);
  148. debuglog("Audio found : "+document.audios[document.audios.length-1].NAME);
  149. }
  150. else if(line.indexOf('GROUP-ID="aac-64k"')>0 && (quality==null||quality=="aac-64k")){
  151. quality="aac-64k";
  152. document.audios.push(lang);
  153. debuglog("Audio found : "+document.audios[document.audios.length-1].NAME);
  154. }
  155. }
  156. });
  157. }
  158. else if(response.indexOf(".mp4a")>0) {
  159. downloadmp4a(response);
  160. }
  161. }
  162.  
  163. function downloadmp4a(m3u8data){
  164. debuglog("downloadmp4a");
  165. var lines = m3u8data.split(/\r?\n/g);
  166. var mapfound=false;
  167. var percent;
  168. var i=0;
  169. document.downloadInterval = setInterval(function () {
  170. var line = lines[i];
  171. var url=null;
  172. if(line!=null){
  173. var uri = document.audios[document.audioid].URI;
  174. if(line.indexOf("map.mp4a")>0 && !mapfound){
  175. // Get mp4a map
  176. debuglog("Download map");
  177. url = document.baseurl+uri.substring(0,uri.lastIndexOf("/")+1)+line.substring(line.indexOf('"')+1,line.lastIndexOf('"'));
  178. mapfound=true;
  179. }
  180. else if(line.indexOf("_000.mp4a")>0 && line.indexOf("MAIN")>0){
  181. // Get mp4a data
  182. url = document.baseurl+uri.substring(0,uri.lastIndexOf("/")+1)+line;
  183. }
  184.  
  185. if(url!=null && !document.downloading){
  186. // Download file
  187. getpagecontent(mp4aloaded,url,true);
  188. document.downloading=true;
  189. i++;
  190. }
  191. else if(url==null){
  192. // Skip line
  193. i++;
  194. }
  195.  
  196. if(percent!=Math.round((i/lines.length)*100)){
  197. percent=Math.round((i/lines.length)*100);
  198. document.styleSheets[0].addRule('#audioTrackPicker > div:nth-child('+(document.audioid+1)+'):before','content:"'+percent+'%";');
  199. }
  200. }
  201. else {
  202. // Download finished
  203. clearInterval(document.downloadInterval);
  204. document.styleSheets[0].addRule('#audioTrackPicker > div:nth-child('+(document.audioid+1)+'):before','content:"";');
  205. exportblob(document.content, 'video/mp4');
  206. document.content=new Uint8Array();
  207. document.wait=false;
  208. }
  209. },10);
  210. }
  211.  
  212.  
  213. function mp4aloaded(response) {
  214. debuglog("mp4aloaded");
  215. document.downloading=false;
  216. document.content=appendbuffer(document.content,response);
  217. }
  218.  
  219. function linetoarray(line) {
  220. var result = [];
  221. var values = line.split(',');
  222. values.forEach(function(value) {
  223. var data = value.replace(/\r\n|\r|\n/g,'').split('=');
  224. if(data.length>1) {
  225. var key = data[0];
  226. var content = data[1].replace(/"/g,'');
  227. result[key]=content;
  228. }
  229. });
  230. return result;
  231. }
  232.  
  233. function buttonhandle() {
  234. var buttons = document.getElementsByClassName("control-icon-btn");
  235. if(buttons.length>0) {
  236. if (typeof document.clickhandlesub !== "undefined") {
  237. document.clickhandlesub();
  238. }
  239. if (typeof document.clickhandleaudio !== "undefined") {
  240. document.clickhandleaudio();
  241. }
  242.  
  243. document.filename = document.getElementsByClassName("title-field")[0]?.innerText;
  244. if(document.getElementsByClassName("subtitle-field").length>0) {
  245. document.episode = document.getElementsByClassName("subtitle-field")[0]?.innerText
  246. }
  247. }
  248.  
  249. if(document.oldlocation!=window.location.href&&document.oldlocation!=null) {
  250. // location changed
  251. document.m3u8found=false;
  252. document.audios = [];
  253. document.langs = [];
  254. }
  255.  
  256. document.oldlocation=window.location.href;
  257. }
  258.  
  259. document.clickhandleaudio = function() {
  260. var picker = document.getElementsByClassName("options-picker audio-track-picker");
  261. if(picker && picker[0]) {
  262. picker[0].childNodes.forEach(function(child) {
  263. var element = child.childNodes[0];
  264. var lang = element.childNodes[1].innerHTML;
  265. if(child.onclick==null) {
  266. child.onclick = selectaudio;
  267. }
  268. });
  269. }
  270. }
  271.  
  272. function selectaudio(e) {
  273. debuglog("selectaudio");
  274. var width = this.offsetWidth;
  275. // Check click position
  276. if(e.layerX>=width-30&&e.layerX<=width-10&&e.layerY>=5&&e.layerY<=25){
  277. // Download audio
  278. download(this.childNodes[0].childNodes[1].innerHTML);
  279. // Cancel selection
  280. return false;
  281. }
  282. }
  283.  
  284. function download(langname) {
  285. if(!document.wait){
  286. debuglog("Download audio : "+langname);
  287. var count=0;
  288. document.audios.forEach(function(audio) {
  289. if(audio.LOCALIZED && Object.values(audio.LOCALIZED.renditions).includes(langname)) {
  290. document.audioid=count;
  291. document.ad=(audio.NAME.indexOf("[Audio Description]")>0);
  292. getpagecontent(m3u8loaded,document.baseurl+audio.URI);
  293. document.wait=true;
  294. }
  295. count++;
  296. });
  297. if(count==0){
  298. alert("An error has occurred, please reload the page.");
  299. }
  300. }
  301. }
  302.  
  303. function getpagecontent(callback,url,binary) {
  304. debuglog("Downloading : "+url);
  305. var http=new XMLHttpRequest();
  306. http.open("GET", url, true);
  307. if(binary){
  308. http.responseType = "arraybuffer";
  309. }
  310. http.onloadend = function() {
  311. if(http.readyState == 4 && http.status == 200) {
  312. if(binary){
  313. callback(http.response);
  314. }
  315. else {
  316. callback(http.responseText);
  317. }
  318. }
  319. else if (http.status === 404) {
  320. debuglog("Not found");
  321. callback("");
  322. }
  323. else {
  324. debuglog("Unknown error, retrying");
  325. setTimeout(function () { getpagecontent(callback,url,binary); },100);
  326. }
  327. }
  328. http.send();
  329. }
  330.  
  331. function appendbuffer(buffer1, buffer2) {
  332. var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
  333. tmp.set(new Uint8Array(buffer1), 0);
  334. tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
  335. return tmp;
  336. };
  337.  
  338. // Save file as arraybuffer
  339. function exportblob(data, mimeType) {
  340. debuglog("exportblob");
  341. var blob, url;
  342. var output = document.filename;
  343. if(document.episode!="") {
  344. output+= " - "+document.episode.replace(':','');
  345. }
  346. output += "."+document.audios[document.audioid].LANGUAGE;
  347. if(document.ad){
  348. output +=".ad";
  349. }
  350. output += ".mp4";
  351.  
  352.  
  353. blob = new Blob([data], {
  354. type: mimeType
  355. });
  356. url = window.URL.createObjectURL(blob);
  357. downloadurl(url, output);
  358. setTimeout(function() {
  359. return window.URL.revokeObjectURL(url);
  360. }, 1000);
  361. };
  362.  
  363. function downloadurl(data, fileName) {
  364. debuglog("Save audio");
  365. var a;
  366. a = document.createElement('a');
  367. a.href = data;
  368. a.download = fileName;
  369. document.body.appendChild(a);
  370. a.style = 'display: none';
  371. a.click();
  372. a.remove();
  373. };
  374.  
  375. function debuglog(message){
  376. if(debug){
  377. console.log("%c [debug] "+message, 'background: #222; color: #bada55');
  378. }
  379. }
  380. })(XMLHttpRequest.prototype.open, XMLHttpRequest.prototype.send);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址