Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 42 additions & 12 deletions packages/soundcloud/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,50 @@ export class SoundCloudPlugin extends ExtractorPlugin {
return related.filter(t => t.title).map(t => new SoundCloudSong(this, t));
}

async getStreamURL<T>(song: SoundCloudSong<T>) {
if (!song.url) {
throw new DisTubeError("SOUNDCLOUD_PLUGIN_INVALID_SONG", "Cannot get stream url from invalid song.");
}
const stream = await this.soundcloud.util.streamLink(song.url);
if (!stream) {
throw new DisTubeError(
"SOUNDCLOUD_PLUGIN_RATE_LIMITED",
"Reached SoundCloud rate limits\nSee more: https://developers.soundcloud.com/docs/api/rate-limits#play-requests",
);
}
return stream;
async getStreamURL(song) {
if (!song.url) {
throw new import_distube.DisTubeError("SOUNDCLOUD_PLUGIN_INVALID_SONG", "Cannot get stream url from invalid song.");
}

let stream = await this.soundcloud.util.streamLink(song.url).catch(() => null);

if (stream) return stream;

const track = await this.soundcloud.resolve.get(song.url, true).catch(() => null);

if (!track || !track.media || !track.media.transcodings?.length) {
throw new import_distube.DisTubeError("SOUNDCLOUD_PLUGIN_STREAM_ERROR", "Invalid or restricted SoundCloud track.");
}


const progressive = track.media.transcodings.find(t => t.format.protocol === "progressive");
const transcoding = progressive || track.media.transcodings[0];

if (!transcoding?.url) {
throw new import_distube.DisTubeError("SOUNDCLOUD_PLUGIN_STREAM_ERROR", "No valid stream URL found.");
}

const clientId = await this.soundcloud.api.getClientId().catch(() => null);
if (!clientId) {
throw new import_distube.DisTubeError("SOUNDCLOUD_PLUGIN_NO_CLIENT_ID", "Missing client ID");
}

const res = await fetch(`${transcoding.url}?client_id=${clientId}`);
if (!res.ok) {
throw new import_distube.DisTubeError(
"SOUNDCLOUD_PLUGIN_RATE_LIMITED",
`Failed to fetch stream URL. HTTP ${res.status}`
);
}

const json = await res.json();
if (!json?.url) {
throw new import_distube.DisTubeError("SOUNDCLOUD_PLUGIN_STREAM_ERROR", "Stream URL missing in response.");
}

return json.url;
}

async searchSong<T>(query: string, options: ResolveOptions<T>) {
const songs = await this.search(query, SearchType.Track, 1, options);
return songs[0];
Expand Down