2024-05-16 16:57:48 +02:00
|
|
|
import { env } from "../../config.js";
|
2023-08-20 14:14:15 +02:00
|
|
|
import { cleanString } from "../../sub/utils.js";
|
2023-02-12 08:40:49 +01:00
|
|
|
|
2024-04-16 17:51:27 +02:00
|
|
|
const cachedID = {
|
|
|
|
version: '',
|
|
|
|
id: ''
|
2024-04-30 09:38:01 +02:00
|
|
|
}
|
2023-02-12 08:40:49 +01:00
|
|
|
|
|
|
|
async function findClientID() {
|
|
|
|
try {
|
2024-05-16 17:28:42 +02:00
|
|
|
let sc = await fetch('https://soundcloud.com/').then(r => r.text()).catch(() => {});
|
2023-02-12 08:40:49 +01:00
|
|
|
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 && !url.startsWith('https://a-v2.sndcdn.com')) return;
|
|
|
|
|
2024-05-16 17:28:42 +02:00
|
|
|
let scrf = await fetch(url).then(r => r.text()).catch(() => {});
|
2023-02-12 08:40:49 +01:00
|
|
|
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;
|
2024-04-30 09:38:01 +02:00
|
|
|
} catch {}
|
2023-02-12 08:40:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export default async function(obj) {
|
2023-08-22 20:49:02 +02:00
|
|
|
let clientId = await findClientID();
|
|
|
|
if (!clientId) return { error: 'ErrorSoundCloudNoClientId' };
|
|
|
|
|
|
|
|
let link;
|
2023-12-15 00:04:05 +01:00
|
|
|
if (obj.url.hostname === 'on.soundcloud.com' && obj.shortLink) {
|
2024-05-16 17:28:42 +02:00
|
|
|
link = await fetch(`https://on.soundcloud.com/${obj.shortLink}/`, { redirect: "manual" }).then(r => {
|
2023-08-22 20:49:02 +02:00
|
|
|
if (r.status === 302 && r.headers.get("location").startsWith("https://soundcloud.com/")) {
|
|
|
|
return r.headers.get("location").split('?', 1)[0]
|
|
|
|
}
|
2023-12-15 00:04:05 +01:00
|
|
|
}).catch(() => {});
|
2023-02-12 08:40:49 +01:00
|
|
|
}
|
2023-12-15 00:04:05 +01:00
|
|
|
|
2023-08-22 20:49:02 +02:00
|
|
|
if (!link && obj.author && obj.song) {
|
|
|
|
link = `https://soundcloud.com/${obj.author}/${obj.song}${obj.accessKey ? `/s-${obj.accessKey}` : ''}`
|
2023-02-12 08:40:49 +01:00
|
|
|
}
|
2023-12-15 00:04:05 +01:00
|
|
|
|
2023-08-22 20:49:02 +02:00
|
|
|
if (!link) return { error: 'ErrorCouldntFetch' };
|
2023-02-12 08:40:49 +01:00
|
|
|
|
2024-05-16 17:28:42 +02:00
|
|
|
let json = await fetch(`https://api-v2.soundcloud.com/resolve?url=${link}&client_id=${clientId}`)
|
|
|
|
.then(r => r.status === 200 ? r.json() : false)
|
|
|
|
.catch(() => {});
|
2024-04-30 09:38:01 +02:00
|
|
|
|
2023-08-22 20:49:02 +02:00
|
|
|
if (!json) return { error: 'ErrorCouldntFetch' };
|
2023-02-12 08:40:49 +01:00
|
|
|
|
2024-05-16 17:28:42 +02:00
|
|
|
if (!json.media.transcodings) return { error: 'ErrorEmptyDownload' };
|
2023-02-12 08:40:49 +01:00
|
|
|
|
2024-04-30 09:38:01 +02:00
|
|
|
let bestAudio = "opus",
|
2024-05-10 19:57:14 +02:00
|
|
|
selectedStream = json.media.transcodings.find(v => v.preset === "opus_0_0"),
|
|
|
|
mp3Media = json.media.transcodings.find(v => v.preset === "mp3_0_0");
|
2023-12-25 17:21:06 +01:00
|
|
|
|
2024-05-10 19:57:14 +02:00
|
|
|
// use mp3 if present if user prefers it or if opus isn't available
|
|
|
|
if (mp3Media && (obj.format === "mp3" || !selectedStream)) {
|
|
|
|
selectedStream = mp3Media;
|
2024-04-30 09:38:01 +02:00
|
|
|
bestAudio = "mp3"
|
2023-12-25 17:21:06 +01:00
|
|
|
}
|
2024-04-30 09:38:01 +02:00
|
|
|
|
|
|
|
let fileUrlBase = selectedStream.url;
|
2023-12-25 17:21:06 +01:00
|
|
|
let fileUrl = `${fileUrlBase}${fileUrlBase.includes("?") ? "&" : "?"}client_id=${clientId}&track_authorization=${json.track_authorization}`;
|
2023-08-15 10:37:59 +02:00
|
|
|
|
2024-05-16 17:34:48 +02:00
|
|
|
if (!fileUrl.startsWith("https://api-v2.soundcloud.com/media/soundcloud:tracks:"))
|
2024-05-16 17:28:42 +02:00
|
|
|
return { error: 'ErrorEmptyDownload' };
|
2023-02-12 08:40:49 +01:00
|
|
|
|
2024-05-16 16:57:48 +02:00
|
|
|
if (json.duration > env.durationLimit * 1000)
|
|
|
|
return { error: ['ErrorLengthAudioConvert', env.durationLimit / 60] };
|
2023-02-12 08:40:49 +01:00
|
|
|
|
2024-05-16 17:28:42 +02:00
|
|
|
let file = await fetch(fileUrl)
|
|
|
|
.then(async r => (await r.json()).url)
|
|
|
|
.catch(() => {});
|
2023-02-12 08:40:49 +01:00
|
|
|
if (!file) return { error: 'ErrorCouldntFetch' };
|
|
|
|
|
2023-10-12 19:14:54 +02:00
|
|
|
let fileMetadata = {
|
2023-10-15 10:39:17 +02:00
|
|
|
title: cleanString(json.title.trim()),
|
|
|
|
artist: cleanString(json.user.username.trim()),
|
2023-10-12 19:14:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-12 08:40:49 +01:00
|
|
|
return {
|
|
|
|
urls: file,
|
2023-10-12 19:14:54 +02:00
|
|
|
filenameAttributes: {
|
|
|
|
service: "soundcloud",
|
|
|
|
id: json.id,
|
|
|
|
title: fileMetadata.title,
|
|
|
|
author: fileMetadata.artist
|
|
|
|
},
|
2024-04-30 09:22:29 +02:00
|
|
|
bestAudio,
|
2023-12-25 17:21:06 +01:00
|
|
|
fileMetadata
|
2023-02-12 08:40:49 +01:00
|
|
|
}
|
|
|
|
}
|