import { existsSync, readFileSync } from 'node:fs'; import { StreamType } from '../YouTube/stream'; import { request } from '../Request'; import { SoundCloudPlaylist, SoundCloudTrack, SoundCloudTrackFormat, SoundCloudStream } from './classes'; let soundData: SoundDataOptions; if (existsSync('.data/soundcloud.data')) { soundData = JSON.parse(readFileSync('.data/soundcloud.data').toString()); } interface SoundDataOptions { client_id: string; } const pattern = /^(?:(https?):\/\/)?(?:(?:www|m)\.)?(api\.soundcloud\.com|soundcloud\.com|snd\.sc)\/(.*)$/; /** * Gets info from a soundcloud url. * * ```ts * let sound = await play.soundcloud('soundcloud url') * * // sound.type === "track" | "playlist" | "user" * * if (sound.type === "track") { * spot = spot as play.SoundCloudTrack * // Code with SoundCloud track class. * } * ``` * @param url soundcloud url * @returns A {@link SoundCloudTrack} or {@link SoundCloudPlaylist} */ export async function soundcloud(url: string): Promise { if (!soundData) throw new Error('SoundCloud Data is missing\nDid you forgot to do authorization ?'); if (!url.match(pattern)) throw new Error('This is not a SoundCloud URL'); const data = await request( `https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${soundData.client_id}` ).catch((err: Error) => err); if (data instanceof Error) throw data; const json_data = JSON.parse(data); if (json_data.kind !== 'track' && json_data.kind !== 'playlist') throw new Error('This url is out of scope for play-dl.'); if (json_data.kind === 'track') return new SoundCloudTrack(json_data); else return new SoundCloudPlaylist(json_data, soundData.client_id); } /** * Type of SoundCloud */ export type SoundCloud = SoundCloudTrack | SoundCloudPlaylist; /** * Function for searching in SoundCloud * @param query query to search * @param type 'tracks' | 'playlists' | 'albums' * @param limit max no. of results * @returns Array of SoundCloud type. */ export async function so_search( query: string, type: 'tracks' | 'playlists' | 'albums', limit: number = 10 ): Promise { const response = await request( `https://api-v2.soundcloud.com/search/${type}?q=${query}&client_id=${soundData.client_id}&limit=${limit}` ); const results: (SoundCloudPlaylist | SoundCloudTrack)[] = []; const json_data = JSON.parse(response); json_data.collection.forEach((x: any) => { if (type === 'tracks') results.push(new SoundCloudTrack(x)); else results.push(new SoundCloudPlaylist(x, soundData.client_id)); }); return results; } /** * Main Function for creating a Stream of soundcloud * @param url soundcloud url * @param quality Quality to select from * @returns SoundCloud Stream */ export async function stream(url: string, quality?: number): Promise { const data = await soundcloud(url); if (data instanceof SoundCloudPlaylist) throw new Error("Streams can't be created from Playlist url"); const HLSformats = parseHlsFormats(data.formats); if (typeof quality !== 'number') quality = HLSformats.length - 1; else if (quality <= 0) quality = 0; else if (quality >= HLSformats.length) quality = HLSformats.length - 1; const req_url = HLSformats[quality].url + '?client_id=' + soundData.client_id; const s_data = JSON.parse(await request(req_url)); const type = HLSformats[quality].format.mime_type.startsWith('audio/ogg') ? StreamType.OggOpus : StreamType.Arbitrary; return new SoundCloudStream(s_data.url, type); } /** * Gets Free SoundCloud Client ID. * * Use this in beginning of your code to add SoundCloud support. * * ```ts * play.getFreeClientID().then((clientID) => play.setToken({ * soundcloud : { * client_id : clientID * } * })) * ``` * @returns client ID */ export async function getFreeClientID(): Promise { const data = await request('https://soundcloud.com/'); const splitted = data.split('