Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Bookmarklet that generates download link for a Soundcloud upload
(function(window){var i,$sound,$buttonGroup;var $sounds=$(".sound");var clientId=require("config").get("client_id");var oauthToken=require("lib/connect").getAuthToken();var conversionHelper=require("lib/helpers/conversion-helper");var $downloadButton,size;var params,downloadUrl,onSuccess;for(i=$sounds.length-1;i>=0;i--){$sound=$($sounds[i]);var soundcloudUrl="https://soundcloud.com"+($sound.find(".soundTitle__title").attr("href")||window.location.pathname);params={url:soundcloudUrl,client_id:clientId};onSuccess=function($sound){return function(data){var params={client_id:clientId};downloadUrl=require("lib/url").stringify({query:params},data.stream_url+".mp3");$buttonGroup=$($sound.find(".sound__soundActions .sc-button-group")[0]);size=$buttonGroup.find(".sc-button:first")[0].className.match(/sc-button-((?:small)|(?:medium))/)[1];$downloadButton=$('<a class="sc-button sc-button-download sc-button-icon sc-button-responsive">Download</a>').attr({title:"Download this sound ("+conversionHelper.bytesToMB(data.original_content_size)+")",href:downloadUrl,download:$sound.find(".soundTitle__title").first().text()+".mp3"}).addClass("sc-button-"+size);if(0==$sound.find(".sc-button-download").length){$buttonGroup.append($downloadButton)}}}($sound);$.getJSON("http://api.soundcloud.com/resolve.json",params).success(onSuccess)}})(window);
(function(window) {
var i, $sound, $buttonGroup;
var $sounds = $(".sound");
var clientId = require("config").get("client_id");
var oauthToken = require("lib/connect").getAuthToken();
var conversionHelper = require("lib/helpers/conversion-helper");
var $downloadButton, size;
var params, downloadUrl, onSuccess;
for (i = $sounds.length - 1; i >= 0; i--) {
$sound = $($sounds[i]);
var soundcloudUrl = "https://soundcloud.com" + ($sound.find(".soundTitle__title").attr("href") || window.location.pathname);
params = {
url: soundcloudUrl,
client_id: clientId
};
onSuccess = (function($sound) {
return function(data) {
var params = {
client_id: clientId
};
downloadUrl = require("lib/url").stringify({ query: params }, data.stream_url + ".mp3");
$buttonGroup = $($sound.find(".sound__soundActions .sc-button-group")[0]);
size = ($buttonGroup.find(".sc-button:first")[0].className).match(/sc-button-((?:small)|(?:medium))/)[1];
$downloadButton = $('<a class="sc-button sc-button-download sc-button-icon sc-button-responsive">Download</a>').attr({
title: "Download this sound (" + conversionHelper.bytesToMB(data.original_content_size) + ")",
href: downloadUrl,
download:$sound.find(".soundTitle__title").first().text()+'.mp3'
}).addClass("sc-button-" + size);
if (0 == $sound.find(".sc-button-download").length) {
$buttonGroup.append($downloadButton);
}
};
})($sound);
$.getJSON("http://api.soundcloud.com/resolve.json", params).success(onSuccess);
}
})(window);

turian commented Mar 26, 2012

Can you convert this to a one-line cut and paste? What's the easiest way to do that?

Owner

duncanbeevers commented Mar 26, 2012

If you have uglifyjs installed you can simply issue the following command.

curl -s https://raw.github.com/gist/2157987/1fc
9f57df09938cd5091ce28fa87aee659c93e5a/sc-dl.js | uglifyjs -q | pbcopy
Owner

duncanbeevers commented Mar 26, 2012

(function(a){function f(a){var b=a.find(".actionbar");return b.length?b:a.prepend('<div class="actionbar"><div class="actions"><div class="primary"></div></div></div>')}function e(a,b){var c=b.find(".actions"),d=c.find(".download");d.length||c.prepend('<a href="'+a.streamUrl+'" class="download pl-button" download="'+a.title+'" title="'+a.title+'">Download track</a>')}function d(a,b){var d=null;b.hasClass("nano")?d=f(b.closest(".track-title").find(".track")).css({marginLeft:"18px"}):b.hasClass("small")&&(d=f(b.find(".container"))),d&&c(a,d)}function c(a,b){var c=b.find(".download, .mock-download");if(!c.length){var d=$('<a class="pl-button mock-download"><span>Download</span></a>').css({backgroundImage:"url(http://soundcloud.com/images/icons_mini.png?unicorn26)",backgroundRepeat:"no-repeat",backgroundPosition:"-77px -236px",paddingLeft:"18px"}).attr("download",a.title+".mp3");b.find(".primary").append(d),d.attr("href",a.streamUrl)}}function b(a){var b=$.helpers,f=b.getPlayerTrack(b.getPlayerNode(a)),g=a.find(".actionbar");!f||(a.hasClass("player")?g.length?c(f,g):d(f,a):e(f,a))}var g,h,i=a.querySelectorAll("[data-sc-track]");for(g=0,h=i.length;g<h;g++)b($(i[g]))})(document)
Owner

duncanbeevers commented Apr 1, 2012

Added a minified version to the repo

A ready-to-use bookmarklet based on this gist can be found here: http://marklets.com/SoundCloud+Download+Enabler.aspx

Owner

duncanbeevers commented May 8, 2012

@felixfischer Thanks

This is one awesome!

But am wondering why in Firefox it fails!
Take for example this sound: http://soundcloud.com/babalani/afro-melody
Via Firefox (7.0.1 on OpenSuse 12.1), this is what is saved in the body of the mp3 (instead of the actual file):

<TITLE>Error</TITLE> An error occurred while processing your request.

Reference #50.2fb40395.1352806837.527a416

But in Chrome it works fine.

Is there any plan to support "The Next Soundcloud"?

incace commented Dec 1, 2012

Is there any way to bypass the disabled download button and still get this to work? So far, if theres a disabled download button already there, then this will not work.

incace commented Dec 1, 2012

Ok... to the above, a sort of hack solution, using adblock plus add the following rule without quotes "soundcloud.com##.disabled"
and then it will work again.

Owner

duncanbeevers commented Dec 27, 2012

Updated to work with Soundcloud Next.

I wrote a similar script that works for all browsers. It has a Greasemonkey/ User script here:
http://userscripts.org/scripts/show/154933
This also has Firefox and Chrome extensions (links in the above page).

I found a couple bugs with this gist:

The download button will often get appended multiple times per sound class element. Moving the download button check to inside the onSuccess function fixes the issue:

if (0 == $sound.find(".sc-button-download").length) {
    $buttonGroup.append($downloadButton);
}

Second, if there is a "buy button" in a sound then there will be two buttonGroups captured resulting in duplicate download buttons. To fix we only want to select one buttonGroup:

$buttonGroup = $($sound.find(".sound__soundActions .sc-button-group")[0]);

Complete gist with fixes:

(function(window) {
    var i, $sound, $buttonGroup;

    var $sounds = $(".sound");
    var clientId = require("config").get("client_id");
    var oauthToken = require("lib/connect").getAuthToken();
    var conversionHelper = require("lib/helpers/conversion-helper");
    var $downloadButton, size;
    var params, downloadUrl, onSuccess;

    for (i = $sounds.length - 1; i >= 0; i--) {
        $sound = $($sounds[i]);

        var soundcloudUrl = "https://soundcloud.com" + ($sound.find(".soundTitle__title").attr("href") || window.location.pathname);

        params = {
            url: soundcloudUrl,
            client_id: clientId
        };

        onSuccess = (function($sound) {
            return function(data) {
                var params = {
                  client_id: clientId
                };
                downloadUrl = require("lib/url").stringify({ query: params }, data.stream_url + ".mp3");

                $buttonGroup = $($sound.find(".sound__soundActions .sc-button-group")[0]);
                size = ($buttonGroup.find(".sc-button:first")[0].className).match(/sc-button-((?:small)|(?:medium))/)[1];

                $downloadButton = $('<a class="sc-button sc-button-download sc-button-icon sc-button-responsive">Download</a>').attr({
                  title: "Download this sound (" + conversionHelper.bytesToMB(data.original_content_size) + ")",
                  href: downloadUrl
                }).addClass("sc-button-" + size);

                if (0 == $sound.find(".sc-button-download").length) {
                    $buttonGroup.append($downloadButton);
                }
            };
        })($sound);

        $.getJSON("http://api.soundcloud.com/resolve.json", params).success(onSuccess);
    }
})(window);

Other than those small nits, still works great! :)

hi @markpiro i added HTML5 download Attribute,

download:$sound.find(".soundTitle__title").first().text()+'.mp3'

inside of $downloadButton

Greetings!

@haroldSanchezb can you tell me where your code goes exactly ?

post your updated version if possible or just the updated $downloadButton version that you have :)

Thanks!

hello @marcandrebenoit
apology for the delay, with the option that i said, would look like:

(function(window) {
    var i, $sound, $buttonGroup;

    var $sounds = $(".sound");
    var clientId = require("config").get("client_id");
    var oauthToken = require("lib/connect").getAuthToken();
    var conversionHelper = require("lib/helpers/conversion-helper");
    var $downloadButton, size;
    var params, downloadUrl, onSuccess;

    for (i = $sounds.length - 1; i >= 0; i--) {
        $sound = $($sounds[i]);

        var soundcloudUrl = "https://soundcloud.com" + ($sound.find(".soundTitle__title").attr("href") || window.location.pathname);

        params = {
            url: soundcloudUrl,
            client_id: clientId
        };

        onSuccess = (function($sound) {
            return function(data) {
                var params = {
                  client_id: clientId
                };
                downloadUrl = require("lib/url").stringify({ query: params }, data.stream_url + ".mp3");

                $buttonGroup = $($sound.find(".sound__soundActions .sc-button-group")[0]);
                size = ($buttonGroup.find(".sc-button:first")[0].className).match(/sc-button-((?:small)|(?:medium))/)[1];

                $downloadButton = $('<a class="sc-button sc-button-download sc-button-icon sc-button-responsive">Download</a>').attr({
                  title: "Download this sound (" + conversionHelper.bytesToMB(data.original_content_size) + ")",
                  href: downloadUrl,
                  download:$sound.find(".soundTitle__title").first().text()+'.mp3'
                }).addClass("sc-button-" + size);

                if (0 == $sound.find(".sc-button-download").length) {
                    $buttonGroup.append($downloadButton);
                }
            };
        })($sound);

        $.getJSON("http://api.soundcloud.com/resolve.json", params).success(onSuccess);
    }
})(window);
Owner

duncanbeevers commented Mar 14, 2014

@haroldSanchezb @marcandrebenoit Updated to the latest version posted by @haroldSanchezb. These changes are great.

stechico commented Apr 8, 2014

This still working for people? I tried recently and I get a "Failed - No file" via Chrome. Thanks in advance.

@haroldSanchezb @duncanbeevers

hi @stechico still working, but remember, only works in single songs no in continuous songs, or playlists on stream.

Hello, Anyone know how I can use your code and put it in a contextmenu of chrome?

Thanks @haroldSanchezb, I'll keep that in mind an retest. Cheers!

@GustavoHahn is possible, simply set up your context menu that captures the link by pressing right click, and send that url to a script that makes the function of generating the link to download.

Greetings!

It seems that this works, although the filenames are just a string of letters and numbers with 128 at the end showing their bit rate. Any reason why I am running into this issue?

Doesn't work in Safari with newest SoundCloud

Korayem commented Oct 11, 2014

This doesn't work on chrome. I guess soundcloud are changing their protection mechanisms.

kjvarga commented Oct 19, 2014

'require' is a missing reference. Perhaps SC is not using requireJS anymore?

aleemb commented Nov 9, 2014

Yup, broken now. Is there a quick fix?

Ok, i will check tonight

kjvarga commented Dec 1, 2014

Anything to report?

Time to give this an update?

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