Skip to content

Instantly share code, notes, and snippets.

@webketje
Last active October 16, 2025 09:56
Show Gist options
  • Select an option

  • Save webketje/8cd2e6ae8a86dbe0533c5d2c612c42c6 to your computer and use it in GitHub Desktop.

Select an option

Save webketje/8cd2e6ae8a86dbe0533c5d2c612c42c6 to your computer and use it in GitHub Desktop.
Soundcloud Downloader Clean - Tampermonkey userscript OR bookmarklet

Tampermonkey userscript - Soundcloud Downloader Clean

An ad-less, multilingual, clean Soundcloud downloader with robust code.
Adds a 'Download' button to all single track views.

// ==UserScript==
// @name Soundcloud Downloader Clean
// @namespace https://openuserjs.org/users/webketje
// @version 0.1.0
// @description An ad-less, multilingual, clean Soundcloud downloader with robust code. Adds a 'Download' button in the toolbar of all single track views.
// @author webketje
// @license MIT
// @icon https://a-v2.sndcdn.com/assets/images/sc-icons/favicon-2cadd14bdb.ico
// @homepageURL https://gist.github.com/webketje/8cd2e6ae8a86dbe0533c5d2c612c42c6
// @supportURL https://gist.github.com/webketje/8cd2e6ae8a86dbe0533c5d2c612c42c6
// @updateURL https://openuserjs.org/meta/webketje/Soundcloud_Downloader_Clean.meta.js
// @downloadURL https://openuserjs.org/install/webketje/Soundcloud_Downloader_Clean.user.js
// @noframes
// @match https://soundcloud.com/*
// @grant unsafeWindow
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// ==/UserScript==
/* globals saveAs */
(function() {
'use strict';
var win = unsafeWindow || window;
var containerSelector = '.listenEngagement__footer .sc-button-toolbar';
/**
* @desc Log to console only if debug is true
*/
function log() {
var stamp = new Date().toLocaleString(),
args = [].slice.call(arguments),
prefix = ['SCDLC', stamp, '-'];
scdl.debug && console.log(prefix.concat(args).join(' '));
};
/**
* @desc There is no other way to retrieve a Soundcloud client_id than by spying on existing requests.
* We temporarily patch the XHR.send method to retrieve the url passed to it.
* @param restoreIfTrue - restores the original prototype method when true is returned
* @param onRestore - a function to exec when the restoreIfTrue condition is met
*/
function patchXHR(restoreIfTrue, onRestore) {
var originalXHR = win.XMLHttpRequest.prototype.open;
win.XMLHttpRequest.prototype.open = function() {
originalXHR.apply(this, arguments);
var restore = restoreIfTrue.apply(this, arguments);
if (restore) {
win.XMLHttpRequest.prototype.open = originalXHR;
onRestore(restore);
}
};
};
var scdl = {
debug: false,
client_id: '',
dlButtonId: 'scdlc-btn'
};
scdl.getTrackName = function(trackJSON) {
return [
trackJSON.user.username,
trackJSON.title
].join(' - ');
};
scdl.getStreamURL = function(url, onresolve, onerror) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var trackJSON = JSON.parse(xhr.responseText);
onresolve(trackJSON.errors || !trackJSON.stream_url ? false : {
stream_url: trackJSON.stream_url + '?client_id=' + this.client_id,
track_name: this.getTrackName(trackJSON)
});
}.bind(this);
xhr.onerror = function() {
onerror(false);
};
xhr.open('GET', 'https://api.soundcloud.com/resolve?url=' + encodeURIComponent(url) + '&client_id=' + this.client_id);
xhr.send();
};
scdl.button = {
label: {
en: 'Download',
es: 'Descargar',
fr: 'Télécharger',
nl: 'Download',
de: 'Download',
pl: 'Ściągnij',
it: 'Scaricare',
pt_BR: 'Baixar',
sv: 'Ladda ner'
},
download: function(e) {
e.preventDefault();
saveAs(e.target.href, e.target.dataset.title);
},
render: function(href, title, onClick) {
var label = scdl.button.label[document.documentElement.lang];
var a = document.createElement('a');
a.className = "sc-button";
a.href = href;
a.id = scdl.dlButtonId;
a.textContent = label;
a.dataset.title = title + '.mp3';
a.setAttribute('download', title + '.mp3');
a.target = '_blank';
a.onclick = onClick;
a.style.marginLeft = '5px';
a.style.cssFloat = 'left';
return a;
},
attach:function() {
this.remove();
var f = document.querySelector(containerSelector);
if (f)
f.insertAdjacentElement('afterend', this.render.apply(this, arguments));
},
remove: function() {
var btn = document.getElementById(scdl.dlButtonId);
if (btn)
btn.parentNode.removeChild(btn);
}
};
scdl.parseClientIdFromURL = function(url) {
var search = /client_id=([\w\d]+)&*/;
return url && url.match(search) && url.match(search)[1];
};
scdl.getClientID = function(onClientIDFound) {
patchXHR(function(method, url) {
return scdl.parseClientIdFromURL(url);
}, onClientIDFound);
};
scdl.load = function(url) {
// for now only make available for single track pages
if (/^(\/(you|stations|discover|stream|upload|search|settings))/.test(win.location.pathname)) {
scdl.button.remove();
return;
}
scdl.getStreamURL(url,
function onSuccess(result) {
if (!result) {
scdl.button.remove();
} else {
log('Detected valid Soundcloud artist track URL. Requesting info...');
scdl.button.attach(
result.stream_url,
result.track_name,
scdl.button.download
);
}
},
scdl.button.remove
);
};
// patch front-end navigation
['pushState','replaceState','forward','back','go'].forEach(function(event) {
var tmp = win.history.pushState;
win.history[event] = function() {
tmp.apply(win.history, arguments);
scdl.load(win.location.href);
}
});
scdl.getClientID(function(id) {
log('Found Soundcloud client id:', id, '. Initializing...');
scdl.client_id = id;
scdl.load(win.location.href);
});
})();
@xxturboxx
Copy link

I was so excited about this but it doesn't seem to load properly.

@webketje
Copy link
Author

@xxturboxx what browser are you on? For me it works perfectly (Chrome/ Firefox). Try for example this URL https://soundcloud.com/throttle/dreamer

@xxturboxx
Copy link

xxturboxx commented Jan 19, 2020 via email

@TheDarkTron
Copy link

Thanks mate, this one is awesome! Clean and functional as it should be <3

@RAGAGAG
Copy link

RAGAGAG commented Dec 30, 2021

HOW TO DOWNLOAD 320KBPS ON soundcloud?

@webketje
Copy link
Author

@RAGAGAG Last time I worked on this script, you couldn't. There was only 128kbps. Maybe Soundcloud added extra sources, I should have a look.
Another type of music this script cannot download is HLS (streaming) sources (which are m3u files composed of temporary links to chunks of data of a song). I tried to add it but it's too hard, the chunks are encrypted with AES and a private key through a third-party library I cannot obtain access to them

@nascentt
Copy link

Thi script is incredible, thanks for releasing it.

@Cabbasca
Copy link

the saveAs function doesn't work, you can use GM_download. Example:
GM_download({ url: e.target.href, name: e.target.dataset.title, saveAs: true });

@webketje
Copy link
Author

@Cabbasca GM_download only works in the context of TamperMonkey. Filesaver.js also works as a bookmarklet. If it doesn't work for you, that means the page failed to load https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js because I just tested and it works as intended.

@nascentt
Copy link

Interestingly this stopped working for me, my script had version 0.2.1 but and greasyfork has 0.2.1 but I found 1.0.0 on openuserjs so updated from there - there was a 90%+ change (@@ -1,214 +1,370 @@)
and now everything works again
i'd strongly recommend updating the script at greasyfork with the downloadurl and updateurl of openuserjs

@webketje
Copy link
Author

webketje commented Mar 1, 2024

@nascentt thx I forgot to update it there, it's ok now

@sunset4myval
Copy link

sunset4myval commented Jun 22, 2024

How to install this extension?

@webketje
Copy link
Author

@sunset4myval You need to install the Tampermonkey browser extension and then click the "Install" button here: https://openuserjs.org/scripts/webketje/Soundcloud_Downloader_Clean

@AllKillahNoFillah
Copy link

Hi all Im working from mac, very novice to extensions and all this tampermonkey business. I cant get it to operate on my chrome soundcloud url, any suggestions?

@AllKillahNoFillah
Copy link

When I click on tamper monkey extension icon (when visiting Soundcloud), it says no script is running, is this potentially the issue?
Uploading Screenshot 2024-09-03 at 1.04.09 PM.png…

@webketje
Copy link
Author

webketje commented Sep 3, 2024

@AllKillahNoFillah if no script is running in Tampermonkey, you haven't properly installed or enabled this script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment