Skip to content

fix(soundcloud): handle preview-only tracks with source fallback - #243

Open
AhmedMondo wants to merge 21 commits into
PerformanC:devfrom
AhmedMondo:fix/soundcloud-lavaplayer-flow
Open

AhmedMondo wants to merge 21 commits into
PerformanC:devfrom
AhmedMondo:fix/soundcloud-lavaplayer-flow

Conversation

@AhmedMondo

@AhmedMondo AhmedMondo commented Sep 21, 2026

Copy link
Copy Markdown

Changes

  • Updated SoundCloud playback resolution to use the original SoundCloud permalink through api-v2.soundcloud.com/resolve, matching Lavaplayer's public web API flow instead of resolving api.soundcloud.com/tracks/{id}.
  • Added support for SoundCloud's full_duration, monetization_model, policy, access, and snippet metadata so preview-only tracks can be distinguished from normally playable uploads.
  • Added detection for SUB_HIGH_TIER, SNIP, /preview/ transcoding URLs, and resolved 30-second /playlist/0/30/ streams.
  • Updated track duration handling to prefer full_duration over the 30-second preview duration.
  • Updated SoundCloud transcoding selection to follow Lavaplayer-style format preference while rejecting known preview streams instead of attempting to play them as full tracks.
  • Improved SoundCloud client_id handling by using browser-style request headers, refreshing the web client ID after 401 responses, and reducing the cached client ID lifetime to avoid stale credentials.
  • Added the internal SOUNDCLOUD_PREVIEW_ONLY failure cause so preview-only playback failures can be handled separately from normal SoundCloud errors.
  • Added configurable fallbackSources for preview-only SoundCloud tracks. The fallback flow searches by ISRC first, then artist + title, uses the existing getBestMatch() logic, and resolves the matched track through the fallback source.
  • Added support for returning the fallback track through newTrack, allowing the existing player pipeline to fetch and play the replacement source without changing normal SoundCloud playback behavior.
  • Normal SoundCloud tracks with full public transcodings continue to play directly from SoundCloud; fallback is only triggered when SoundCloud does not provide a full playable stream.

Why

  • Some SoundCloud tracks, especially licensed tracks using SUB_HIGH_TIER with policy: SNIP, expose only 30-second preview transcodings through the public SoundCloud web API.
  • These tracks were previously not playable in NodeLink. They would either resolve to a 30-second preview stream or fail playback with errors such as Track only has preview URL.
  • SoundCloud-exclusive and normally playable uploads already work correctly and should continue using SoundCloud directly, so fallback must only be used for tracks where SoundCloud explicitly provides preview-only playback.
  • The fallback mechanism allows NodeLink to recover these otherwise unplayable tracks through another configured source while keeping the originally requested SoundCloud track metadata and normal playback flow intact.
  • The updated resolver also exposes the real track duration through full_duration. For example, a track may report duration: 30000 while its actual full_duration is several minutes.

Checkmarks

  • I have tested the changes locally with both normal SoundCloud uploads and preview-only SUB_HIGH_TIER / SNIP tracks.
  • I have tested a SoundCloud-exclusive track and confirmed it continues to play directly from SoundCloud without triggering fallback.
  • I have tested a preview-only track and confirmed NodeLink detects the preview restriction and successfully plays a matched track through a configured fallback source.
  • I have verified the fallback uses the existing source manager and getBestMatch() flow instead of introducing source-specific playback logic.
  • I have tested the changes on a real Discord bot.

Additional info

The fallback is intentionally limited to SoundCloud tracks that resolve as preview-only. It is not used for normally playable SoundCloud tracks.

An example preview-only response from SoundCloud looks like:

duration: 30000
full_duration: 219350
monetization_model: SUB_HIGH_TIER
policy: SNIP

with only:

/preview/hls
/preview/progressive

available as transcodings.

Previously, these tracks could not be played successfully by NodeLink. With this change, NodeLink reports SOUNDCLOUD_PREVIEW_ONLY, searches the configured fallback sources, selects the closest match using existing matching logic, and continues playback through that source.

The fallback source order can be configured through:

soundcloud: {
  enabled: true,
  clientId: '',
  fallbackSources: ['youtube', 'deezer', 'jiosaavn', 'audiomack']
}

@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown

All Contributors have signed the CLA. The PR is now allowed to be merged.
Posted by the CLA Assistant Lite bot.

@AhmedMondo

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

performanc-bot added a commit to PerformanC/CLA-Signatures that referenced this pull request Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant