From fdcc47fa799d0143516827718749be08a3a96028 Mon Sep 17 00:00:00 2001 From: Big Duckie Date: Fri, 6 Jun 2025 16:41:51 -0600 Subject: [PATCH] Attempt to fix up some really bad code. Ideally this would be rewritten to use less jquery stuff, and have proper async. --- bandcamp_downloader.user.js | 355 ++++++++++++++++++++++-------------- 1 file changed, 217 insertions(+), 138 deletions(-) diff --git a/bandcamp_downloader.user.js b/bandcamp_downloader.user.js index a8c9428..54c0efe 100644 --- a/bandcamp_downloader.user.js +++ b/bandcamp_downloader.user.js @@ -4,157 +4,236 @@ // @author Big Duckie // @copyright 2025, Big Duckie (https://bigduckie.dev) // @license MIT -// @version 2025.1.13 +// @version 2025.6.6 // @description Adds a sidebar to download songs from Bandcamp. // @include *://*.bandcamp.com/* -// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js -// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js +// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js +// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js // @grant GM_xmlhttpRequest -// @grant GM_notification // @grant GM_addStyle // @grant GM_download +// @connect bcbits.com // ==/UserScript== // Register Globals -var sidebar_visible = false; -var tracklist = TralbumData.trackinfo; +let tracklist = TralbumData.trackinfo; +unsafeWindow.GM_download = GM_download; (function () { - "use strict"; + "use strict"; - // Sidebar construction - $("body") - .append( - $("
") - .addClass("dl-sidebar-container dl-sidebar dl-color-1 hidden") - .append( - $("

").text( - "Click the files to download, or use the download album button." - ) - ) - .append($("

    ").addClass("dl-tracklist")) - .append( - $("
    ") - .addClass("dl-all-button dl-progress dl-color-2") - .append($("
    ").addClass("dl-progress-bar")) - .append( - $("
    ").addClass("dl-progress-text").text("Download Album") - ) - ) - ) - .append( - $("
    ") - .addClass("dl-sidebar-toggle dl-sidebar dl-color-1 hidden") - .text("Bandcamp Downloader") - ); - - tracklist.forEach((track) => { - $(".dl-tracklist").append( - $("
  1. ") - .attr("id", "track-" + track.track_num) - .append( - $("") - .addClass("dl-link custom-color") - .attr("data-url", track.file["mp3-128"]) - .attr("data-trackid", tracklist.indexOf(track)) - .text(track.title) - ) - ); - }); - - // Style sidebar to match page - GM_addStyle( - ".dl-sidebar-toggle{font-family:biolinum;left:0;top:50px;position:fixed;transform:rotate(90deg);transform-origin:left bottom 0;border-top-right-radius:5px;border-top-left-radius:5px;margin:0;padding:5px;z-index:5001;background:inherit;border:1px solid #000000;border-bottom:none;cursor:pointer}.dl-sidebar-toggle.active{left:400px}.dl-sidebar-container{width:390px;min-height:300px;left:-400px;top:50px;max-height:500px;overflow-y:scroll;padding:5px;position:fixed;z-index:5000;border:1px solid #000000;border-left:none;user-select:none;cursor:default}.dl-sidebar-container.active{left:0}.dl-all-button{margin-left:0;margin-right:0;cursor:pointer;background-color:rgb(255, 255, 255);color:rgb(0, 0, 0);border-color:rgb(0, 0, 0)}.dl-progress{position:relative;z-index:5001}.dl-progress,.dl-progress-bar,.dl-progress-text{height:25px;left:0;right:0;top:0;bottom:0}.dl-progress-bar{width:0;overflow:hidden;z-index:5002;-moz-transition:width 0.2s linear;-webkit-transition:width 0.2s linear;-o-transition:width 0.2s linear;transition:width 0.2s linear;background-color:rgb(0, 0, 0)}.dl-progress-text{text-align:center;z-index:5003;position:absolute;line-height:25px;color:inherit}.dl-sidebar{opacity:1;transition:opacity 1s, left 0.5s}.dl-sidebar.hidden{opacity:0}.dl-sidebar::-webkit-scrollbar{width:10px}.dl-sidebar::-webkit-scrollbar-track{background:rgb(0, 0, 0)}.dl-sidebar::-webkit-scrollbar-thumb{background:rgb(255, 255, 255)}" - ); - - $(".dl-color-1").css({ - "background-color": $("#pgBd").css("background-color"), - color: $("#pgBd").css("color"), - "border-color": $("#pgBd").css("color"), - }); - $(".dl-color-2").css({ - "background-color": $("#follow-unfollow").css("background-color"), - color: $("#follow-unfollow").css("color"), - "border-color": $("#follow-unfollow").css("border-color"), - }); - $(".dl-progress-bar").css({ - "background-color": $("#pgFt").css("background-color"), - }); - - $(".dl-sidebar").removeClass("hidden"); - - // Handle events - $(".dl-sidebar-toggle").click((e) => { - $(".dl-sidebar").toggleClass("active"); - }); - - $(".dl-link").click((e) => { - GM_download( - $(e.target).attr("data-url"), - `[${pad(tracklist[$(e.target).attr("data-trackid")].track_num, 2)}] ${ - TralbumData.artist - } - ${tracklist[$(e.target).attr("data-trackid")].title}.mp3` - ); - }); - - $(".dl-all-button").click((e) => { - downloadAlbum(); - }); - - // Main functions - function downloadAlbum() { - // Emergency polyfill - var Promise = window.Promise; - if (!Promise) { - Promise = JSZip.external.Promise; - } - - // Fetch binary data using GM_xmlhttprequest - function urlToPromise(url) { - return new Promise((resolve, reject) => { - GM_xmlhttpRequest({ - method: "GET", - url: url, - responseType: "arraybuffer", - onload: (response) => { - resolve(response.response); - }, - onerror: (response) => { - reject(response.response); - }, - }); - }); - } - - var zip = new JSZip(); + // Sidebar construction + jQuery("body") + .append(jQuery("
    ") + .addClass("dl-sidebar-container dl-sidebar dl-color-1 hidden") + .append(jQuery("

    ").text("Click the files to download, or use the download album button.")) + .append(jQuery("

      ").addClass("dl-tracklist")) + .append(jQuery("
      ") + .addClass("dl-all-button dl-progress dl-color-2") + .append(jQuery("
      ").addClass("dl-progress-bar")) + .append(jQuery("
      ").addClass("dl-progress-text").text("Download Album")))) + .append(jQuery("
      ") + .addClass("dl-sidebar-toggle dl-sidebar dl-color-1 hidden") + .text("Bandcamp Downloader")); tracklist.forEach((track) => { - zip.file( - `[${track.track_num}] ${TralbumData.artist} - ${track.title}.mp3`, - urlToPromise(track.file["mp3-128"]), - { binary: true } - ); + jQuery(".dl-tracklist").append(jQuery("
    1. ") + .attr("id", "track-" + track.track_num) + .append(jQuery("") + .addClass("dl-link custom-color") + .attr("data-url", track.file["mp3-128"]) + .attr("data-trackid", tracklist.indexOf(track)) + .text(track.title))); }); - zip - .generateAsync({ type: "blob" }, (metadata) => { - $(".dl-progress-bar").css("width", `${metadata.percent}%`); - }) - .then( - (blob) => { - GM_download( - URL.createObjectURL(blob), - `${BandData.name} - ${TralbumData.current.title}.zip` - ); - }, - (e) => { - console.error(e); - } - ); - } + function css(strings) { + return strings; + } - // Supporting functions - function pad(n, width = 3, z = 0) { - return (String(z).repeat(width) + String(n)).slice(String(n).length); - } -})(); + // Style sidebar to match page + GM_addStyle(css`.dl-sidebar-toggle { + font-family: biolinum; + left: 0; + top: 50px; + position: fixed; + transform: rotate(90deg); + transform-origin: left bottom 0; + border-top-right-radius: 5px; + border-top-left-radius: 5px; + margin: 0; + padding: 5px; + z-index: 5001; + background: inherit; + border: 1px solid #000000; + border-bottom: none; + cursor: pointer + } + + .dl-sidebar-toggle.active { + left: 400px + } + + .dl-sidebar-container { + display: flex; + flex-direction: column; + width: 390px; + min-height: 300px; + left: -400px; + top: 50px; + max-height: 500px; + overflow-y: scroll; + padding: 5px; + position: fixed; + z-index: 5000; + border: 1px solid #000000; + border-left: none; + user-select: none; + cursor: default + } + + .dl-sidebar-container.active { + left: 0 + } + + .dl-tracklist { + flex-grow: 1; + } + + .dl-all-button { + margin-left: 0; + margin-right: 0; + cursor: pointer; + background-color: rgb(255, 255, 255); + color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0) + } + + .dl-progress { + position: relative; + z-index: 5001 + } + + .dl-progress, + .dl-progress-bar, + .dl-progress-text { + height: 25px; + left: 0; + right: 0; + top: 0; + bottom: 0 + } + + .dl-progress-bar { + width: 0; + overflow: hidden; + z-index: 5002; + -moz-transition: width 0.2s linear; + -webkit-transition: width 0.2s linear; + -o-transition: width 0.2s linear; + transition: width 0.2s linear; + background-color: rgb(0, 0, 0) + } + + .dl-progress-text { + text-align: center; + z-index: 5003; + position: absolute; + line-height: 25px; + color: inherit + } + + .dl-sidebar { + opacity: 1; + transition: opacity 1s, left 0.5s + } + + .dl-sidebar.hidden { + opacity: 0 + } + + .dl-sidebar::-webkit-scrollbar { + width: 10px + } + + .dl-sidebar::-webkit-scrollbar-track { + background: rgb(0, 0, 0) + } + + .dl-sidebar::-webkit-scrollbar-thumb { + background: rgb(255, 255, 255) + }`); + + jQuery(".dl-color-1").css({ + "background-color": jQuery("#pgBd").css("background-color"), + color: jQuery("#pgBd").css("color"), + "border-color": jQuery("#pgBd").css("color"), + }); + jQuery(".dl-color-2").css({ + "background-color": jQuery("#follow-unfollow").css("background-color"), + color: jQuery("#follow-unfollow").css("color"), + "border-color": jQuery("#follow-unfollow").css("border-color"), + }); + jQuery(".dl-progress-bar").css({ + "background-color": jQuery("#recommendations_container").css("background-color"), + }); + + jQuery(".dl-sidebar").removeClass("hidden"); + + // Handle events + jQuery(".dl-sidebar-toggle").click((e) => { + jQuery(".dl-sidebar").toggleClass("active"); + }); + + jQuery(".dl-link").click((e) => { + GM_download(jQuery(e.target).attr("data-url"), `[${pad(tracklist[jQuery(e.target).attr("data-trackid")].track_num, 2)}] ${TralbumData.artist} - ${tracklist[jQuery(e.target).attr("data-trackid")].title}.mp3`); + }); + + jQuery(".dl-all-button").click((e) => { + console.debug("downloading album"); + downloadAlbum(); + }); + + // Main functions + function downloadAlbum() { + let zip = new JSZip(); + + tracklist.forEach((track) => { + console.debug("downloading track", track); + zip.file( + `[${track.track_num}] ${TralbumData.artist} - ${track.title}.mp3`, + urlToPromise(track.file["mp3-128"])); + }); + + console.debug("generating zip"); + zip.generateAsync({type: "blob"}, (metadata) => { + console.debug("processing zip"); + jQuery(".dl-progress-bar").css("width", `${metadata.percent}%`); + }) + .then((blob) => { + let url = URL.createObjectURL(blob); + console.debug("compiled zip", url); + + GM_download(url, `${TralbumData.artist} - ${TralbumData.current.title}.zip`); + }, (e) => { + console.error(e); + }); + } + + // Supporting functions + function pad(n, width = 3, z = 0) { + return (String(z).repeat(width) + String(n)).slice(String(n).length); + } + + function urlToPromise(url) { + return new Promise((resolve, reject) => { + GM.xmlHttpRequest({ + method: "GET", url: url, responseType: "blob", onload: (response) => { + console.debug("fetched song", url, response.response); + resolve(response.response); + }, onerror: (response) => { + reject(response.response); + }, + }); + }); + } +})(); \ No newline at end of file