CryptalEquine / Radiopaedia Stack Downloader

// ==UserScript==
// @name          Radiopaedia Stack Downloader
// @namespace     rsd
// @license       MIT
// @description   Adds a button on studies to download all slides from a stack
// @author        CryptalEquine
// @include       *radiopaedia.org/cases/*
// @version       1.0.4
// @run-at        document-start
// @noframes      true
// @grant         GM_download
// @downloadURL   https://openuserjs.org/install/CryptalEquine/Radiopaedia_Stack_Downloader.user.js
// @updateURL     https://openuserjs.org/meta/CryptalEquine/Radiopaedia_Stack_Downloader.meta.js
// @require       https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js
// @require       https://gist.githubusercontent.com/BrockA/2625891/raw/9c97aa67ff9c5d56be34a55ad6c18a314e5eb548/waitForKeyElements.js
// ==/UserScript==

var iconURL = 'https://i.imgur.com/XgaqfQ8.png';
var currentDownloads = 0;

waitForKeyElements('div.download[data-name="download"]', function(d) {
   (async function() {
      try {
         var e = $(d).parents('div.case-study[data-study-id]');
         var studyAPI = e.attr('data-study-stacks-url');
         var studyData = await $.ajax({url: 'https://radiopaedia.org' + studyAPI});
         var id = "dlstack_" + getModalityData(e, studyData).type;
         
         $(d).parent().append(`<style>.icon.dlstack { background: transparent url(` + iconURL + `) top left no-repeat; }</style>
                        <div class="js-tooltip icon dlstack" data-placement="top" data-theme="grey" data-size="small" 
                        data-original-title="Download Stack" id="` + id + `" style="padding-left: 5px;"></div>`);
         
         (function(_e, _studyData) {
            $('#' + id).click(async function() {
               var queue = [];
               var modality = getModalityData(_e, _studyData);
               modality.data.forEach(function(value, index) {
                  queue.push({index: value.position, image: value.fullscreen_filename});
               });
               
               while (queue.length > 0) {
                  if (currentDownloads > 5) {
                     await sleep(1800);
                     continue;
                  }
                  var item = queue.shift();
                  (function(_item) {
                     GM_download({
                        url: _item.image + '?',
                        name: [modality.type, modality.name.trim(), _item.index].join('_'),
                        saveAs: false,
                        onerror: function(error) {
                           queue.unshift(_item);
                           currentDownloads--;
                        },
                        onload: function() {
                           currentDownloads--;
                        }
                     });
                     
                     currentDownloads++;
                  })(item);
               }
            });
         })(e, studyData);
      }
      catch(error) {
         console.log(error);
      }
   })();
}, false);

function getModalityData(e, studyData) {
   var currentModality = e.find('li.jcarousel-item.current');
   var multiModalities = currentModality.length > 0;
   var modalityName = !multiModalities ? e.find('div.title').html() : currentModality.find('span').eq(0).html();
   var modalityIndex = !multiModalities ? 0 : e.find('li.jcarousel-item').index(currentModality);
   return {
      name: modalityName,
      index: modalityIndex,
      type: studyData[modalityIndex].modality,
      data: studyData[modalityIndex].images
   };
}

function sleep(ms) {
   return new Promise(resolve => setTimeout(resolve, ms));
}