Spaces:
Paused
Paused
| import { env } from "../../config.js"; | |
| import { cleanString } from "../../misc/utils.js"; | |
| const cachedID = { | |
| version: '', | |
| id: '' | |
| } | |
| async function findClientID() { | |
| try { | |
| let sc = await fetch('https://soundcloud.com/').then(r => r.text()).catch(() => {}); | |
| let scVersion = String(sc.match(/<script>window\.__sc_version="[0-9]{10}"<\/script>/)[0].match(/[0-9]{10}/)); | |
| if (cachedID.version === scVersion) return cachedID.id; | |
| let scripts = sc.matchAll(/<script.+src="(.+)">/g); | |
| let clientid; | |
| for (let script of scripts) { | |
| let url = script[1]; | |
| if (!url?.startsWith('https://a-v2.sndcdn.com/')) { | |
| return; | |
| } | |
| let scrf = await fetch(url).then(r => r.text()).catch(() => {}); | |
| let id = scrf.match(/\("client_id=[A-Za-z0-9]{32}"\)/); | |
| if (id && typeof id[0] === 'string') { | |
| clientid = id[0].match(/[A-Za-z0-9]{32}/)[0]; | |
| break; | |
| } | |
| } | |
| cachedID.version = scVersion; | |
| cachedID.id = clientid; | |
| return clientid; | |
| } catch {} | |
| } | |
| export default async function(obj) { | |
| let clientId = await findClientID(); | |
| if (!clientId) return { error: "fetch.fail" }; | |
| let link; | |
| if (obj.url.hostname === 'on.soundcloud.com' && obj.shortLink) { | |
| link = await fetch(`https://on.soundcloud.com/${obj.shortLink}/`, { redirect: "manual" }).then(r => { | |
| if (r.status === 302 && r.headers.get("location").startsWith("https://soundcloud.com/")) { | |
| return r.headers.get("location").split('?', 1)[0] | |
| } | |
| }).catch(() => {}); | |
| } | |
| if (!link && obj.author && obj.song) { | |
| link = `https://soundcloud.com/${obj.author}/${obj.song}${obj.accessKey ? `/s-${obj.accessKey}` : ''}` | |
| } | |
| if (!link && obj.shortLink) return { error: "fetch.short_link" }; | |
| if (!link) return { error: "link.unsupported" }; | |
| let json = await fetch(`https://api-v2.soundcloud.com/resolve?url=${link}&client_id=${clientId}`) | |
| .then(r => r.status === 200 ? r.json() : false) | |
| .catch(() => {}); | |
| if (!json) return { error: "fetch.fail" }; | |
| if (!json.media.transcodings) return { error: "fetch.empty" }; | |
| let bestAudio = "opus", | |
| selectedStream = json.media.transcodings.find(v => v.preset === "opus_0_0"), | |
| mp3Media = json.media.transcodings.find(v => v.preset === "mp3_0_0"); | |
| // use mp3 if present if user prefers it or if opus isn't available | |
| if (mp3Media && (obj.format === "mp3" || !selectedStream)) { | |
| selectedStream = mp3Media; | |
| bestAudio = "mp3" | |
| } | |
| let fileUrlBase = selectedStream.url; | |
| let fileUrl = `${fileUrlBase}${fileUrlBase.includes("?") ? "&" : "?"}client_id=${clientId}&track_authorization=${json.track_authorization}`; | |
| if (!fileUrl.startsWith("https://api-v2.soundcloud.com/media/soundcloud:tracks:")) | |
| return { error: "fetch.empty" }; | |
| if (json.duration > env.durationLimit * 1000) { | |
| return { error: "content.too_long" }; | |
| } | |
| let file = await fetch(fileUrl) | |
| .then(async r => (await r.json()).url) | |
| .catch(() => {}); | |
| if (!file) return { error: "fetch.empty" }; | |
| let fileMetadata = { | |
| title: cleanString(json.title.trim()), | |
| artist: cleanString(json.user.username.trim()), | |
| } | |
| return { | |
| urls: file, | |
| filenameAttributes: { | |
| service: "soundcloud", | |
| id: json.id, | |
| title: fileMetadata.title, | |
| author: fileMetadata.artist | |
| }, | |
| bestAudio, | |
| fileMetadata | |
| } | |
| } | |