Created
November 21, 2024 13:15
-
-
Save cf/8981856d56fa7ae02400a9f6843c7c0a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DO NOT use this for piracy, this is for educational purposes only | |
/* | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
const HEADERS={ | |
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", | |
"Referer": "https://soundcloud.com/ ", | |
}; | |
function scrapeSoundCloudData(pageHTML){ | |
const hydSound = pageHTML.indexOf(`"hydratable":"sound"`); | |
if(hydSound===-1) { | |
throw new Error("could not find hydratable sound data"); | |
} | |
const dataStart = pageHTML.indexOf(`{`, hydSound); | |
if(dataStart===-1){ | |
throw new Error("could not find hydratable sound data start"); | |
} | |
const dataEnd = pageHTML.indexOf('"user":', dataStart); | |
if(dataEnd===-1){ | |
throw new Error("could not find hydratable sound data end"); | |
} | |
return JSON.parse(pageHTML.substring(dataStart, dataEnd)+'"_scr":1}') | |
} | |
async function getSoundCloudMp3FromData(url, pageData){ | |
const auth = pageData.track_authorization; | |
const goodTranscoding = pageData.media.transcodings.filter(x=>x.format.protocol==="progressive")[0]; | |
if(!goodTranscoding){ | |
throw new Error("could not find transcoding for stream"); | |
} | |
const firstURL = goodTranscoding.url+`?client_id=Bzi0o0nRG6RkdZROE3o4Rsq32X0n7J9E&track_authorization=${encodeURIComponent(auth)}`; | |
const firstURLResp = await (await fetch(firstURL, {headers: { | |
...HEADERS, | |
"Origin": url.origin, | |
"Referer": url+"", | |
}})).json(); | |
return firstURLResp.url; | |
} | |
async function getSoundCloudMp3URL(soundCloudURL){ | |
const pageHTML = await fetch(soundCloudURL, {headers: HEADERS}).then(x=>x.text()); | |
const pageData = scrapeSoundCloudData(pageHTML); | |
const mp3URL = await getSoundCloudMp3FromData(soundCloudURL, pageData); | |
return mp3URL; | |
} | |
function errorResp(origin, error, errorCode){ | |
return new Response(JSON.stringify({error: error+""}), { | |
status: errorCode || 400, | |
headers: { | |
"Access-Control-Allow-Origin": origin, | |
"Vary": "Origin", | |
"Content-Type": "application/json", | |
} | |
}); | |
} | |
function jsonResp(origin, jsonData){ | |
return new Response(JSON.stringify(jsonData), { | |
status: 200, | |
headers: { | |
"Access-Control-Allow-Origin": origin, | |
"Vary": "Origin", | |
"Content-Type": "application/json", | |
} | |
}); | |
} | |
export default { | |
async fetch(request) { | |
const corsHeaders = { | |
"Access-Control-Allow-Origin": "*", | |
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS", | |
"Access-Control-Max-Age": "86400", | |
}; | |
// The URL for the remote third party API you want to fetch from | |
// but does not implement CORS | |
// The rest of this snippet for the demo page | |
function rawHtmlResponse(html) { | |
return new Response(html, { | |
headers: { | |
"content-type": "text/html;charset=UTF-8", | |
}, | |
}); | |
} | |
const DEMO_PAGE = ` | |
<!DOCTYPE html> | |
<html> | |
<head><title>Re-enable SoundCloud in Shadertoy</title></head> | |
<body> | |
<h1>Sound Cloud Re-enabled</h1> | |
</body> | |
</html> | |
`; | |
async function handleRequest(request) { | |
const url = new URL(request.url); | |
let apiUrl = url.searchParams.get("url"); | |
if (apiUrl == null || !apiUrl.startsWith("https://soundcloud.com")) { | |
return errorResp(request.headers.get("Origin"), "Please provide a sound cloud url query parameter", 400); | |
} | |
try { | |
const mp3URL = await getSoundCloudMp3URL(apiUrl); | |
}catch(err){ | |
return errorResp(request.headers.get("Origin"), err+"", 400); | |
} | |
return jsonResp(request.headers.get("Origin"), {url: mp3URL}); | |
} | |
async function handleOptions(request) { | |
if ( | |
request.headers.get("Origin") !== null && | |
request.headers.get("Access-Control-Request-Method") !== null && | |
request.headers.get("Access-Control-Request-Headers") !== null | |
) { | |
// Handle CORS preflight requests. | |
return new Response(null, { | |
headers: { | |
...corsHeaders, | |
"Access-Control-Allow-Headers": request.headers.get( | |
"Access-Control-Request-Headers", | |
), | |
}, | |
}); | |
} else { | |
// Handle standard OPTIONS request. | |
return new Response(null, { | |
headers: { | |
Allow: "GET, HEAD, POST, OPTIONS", | |
}, | |
}); | |
} | |
} | |
const url = new URL(request.url); | |
if (url.pathname.startsWith("/sc")) { | |
if (request.method === "OPTIONS") { | |
// Handle CORS preflight requests | |
return handleOptions(request); | |
} else if ( | |
request.method === "GET" || | |
request.method === "HEAD" || | |
request.method === "POST" | |
) { | |
// Handle requests to the API server | |
return handleRequest(request); | |
} else { | |
return new Response(null, { | |
status: 405, | |
statusText: "Method Not Allowed", | |
}); | |
} | |
} else { | |
return rawHtmlResponse(DEMO_PAGE); | |
} | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment