Download song SoundCloud.
// ==UserScript== // @name SoundCloud Downloader // @namespace Hello // @version 1.0.0 // @description Download song SoundCloud. // @author Jvais // @match https://soundcloud.com/* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/ponyfill.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/StreamSaver.min.js // @grant none // @icon https://a-v2.sndcdn.com/assets/images/sc-icons/favicon-2cadd14bdb.ico // ==/UserScript== streamSaver.mitm = 'https://maple3142.github.io/StreamSaver.js/mitm.html' function hook(obj, name, callback) { const fn = obj[name] obj[name] = function (...args) { callback.apply(this, args) fn.apply(this, args) } return () => { // restore obj[name] = fn } } function triggerDownload(url, name) { const a = document.createElement('a') document.body.appendChild(a) a.href = url a.download = name a.click() a.remove() } const btn = { init() { this.el = document.createElement('button') this.el.textContent = 'Download' this.el.classList.add('sc-button') this.el.classList.add('sc-button-medium') this.el.classList.add('sc-button-responsive') this.el.classList.add('sc-button-download') }, attach() { const par = document.querySelector( '.listenEngagement__footer .sc-button-toolbar .sc-button-group' ) if (par) par.insertAdjacentElement('beforeend', this.el) } } btn.init() function load() { if ( /^(\/(you|stations|discover|stream|upload|search|settings))/.test( location.pathname ) ) return const restore = hook( XMLHttpRequest.prototype, 'open', async (method, url) => { const u = new URL(url, document.baseURI) const clientId = u.searchParams.get('client_id') if (!clientId) return restore() const r###lt = await fetch( `https://api-v2.soundcloud.com/resolve?url=${encodeURIComponent( location.href )}&client_id=${clientId}` ).then(r => r.json()) btn.el.onclick = async () => { const progressive = r###lt.media.transcodings.find( t => t.format.protocol === 'progressive' ) if (progressive) { const { url } = await fetch( progressive.url + `?client_id=${clientId}` ).then(r => r.json()) const resp = await fetch(url) const ws = streamSaver.createWriteStream( r###lt.title + '.mp3', { size: resp.headers.get('Content-Length') } ) const rs = resp.body if (rs.pipeTo) { console.log(rs, ws) return rs.pipeTo(ws) } const reader = rs.getReader() const writer = ws.getWriter() const pump = () => reader .read() .then(res => res.done ? writer.close() : writer.write(res.value).then(pump) ) return pump() } alert('Sorry, downloading this music is currently unsupported.') } btn.attach() console.log('changed') } ) } load() for (const f of ['pushState', 'replaceState', 'forward', 'back', 'go']) { hook(history, f, () => load()) }