import type { Dispatcher } from "undici" import { Pool, request } from "undici" const apiURL = "https://api.soundcloud.com" const apiV2URL = "https://api-v2.soundcloud.com" const webURL = "https://soundcloud.com" export class API { public static headers: Record = { referer: "soundcloud.com", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36", } public api = new Pool(apiURL) public apiV2 = new Pool(apiV2URL) public web = new Pool(webURL) public proxy?: Pool constructor(public clientID?: string, public oauthToken?: string, proxy?: string) { if (oauthToken) API.headers.Authorization = `OAuth ${oauthToken}` if (proxy) this.proxy = new Pool(proxy) } get headers() { return API.headers } /** * Gets an endpoint from the Soundcloud API. */ public get = (endpoint: string, params?: Record) => { return this.makeGet(this.api, apiURL, endpoint, params) } /** * Gets an endpoint from the Soundcloud V2 API. */ public getV2 = (endpoint: string, params?: Record) => { return this.makeGet(this.apiV2, apiV2URL, endpoint, params) } /** * Some endpoints use the main website as the URL. */ public getWebsite = (endpoint: string, params?: Record) => { return this.makeGet(this.web, webURL, endpoint, params) } /** * Gets a URL, such as download, stream, attachment, etc. */ public getURL = (URI: string, params?: Record) => { if (this.proxy) return this.makeRequest(this.proxy, this.buildOptions(URI, "GET", params)) const options = { query: params || {}, headers: API.headers, maxRedirections: 5, } if (this.clientID) options.query.client_id = this.clientID if (this.oauthToken) options.query.oauth_token = this.oauthToken return request(URI, options).then(r => { if (r.statusCode.toString().startsWith("2")) { if (r.headers["content-type"] === "application/json") return r.body.json() return r.body.text() } throw new Error(`Status code ${r.statusCode}`) }) } private readonly makeGet = async (pool: Pool, origin: string, endpoint: string, params?: Record) => { if (!this.clientID) await this.getClientID() if (endpoint.startsWith("/")) endpoint = endpoint.slice(1) const options = this.buildOptions(`${this.proxy ? origin : ""}/${endpoint}`, "GET", params) try { return await this.makeRequest(this.proxy || pool, options) } catch { await this.getClientID(true) return this.makeRequest(this.proxy || pool, options) } } public post = async (endpoint: string, params?: Record) => { if (!this.clientID) await this.getClientID() if (endpoint.startsWith("/")) endpoint = endpoint.slice(1) const options = this.buildOptions(`${this.proxy ? origin : ""}/${endpoint}`, "POST", params) return this.makeRequest(this.proxy || this.api, options) } public getClientID = async (reset?: boolean) => { if (!this.oauthToken && (!this.clientID || reset)) { const response = await (this.proxy ? this.proxy.request(this.buildOptions(webURL)) : this.web.request(this.buildOptions("/"))).then(r => r.body.text() ) const urls = response.match( /(?!