soundcloud-api-ts 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -25
- package/dist/{chunk-JLRQJWU5.mjs → chunk-JH7TLL2C.mjs} +55 -3
- package/dist/chunk-JH7TLL2C.mjs.map +1 -0
- package/dist/{chunk-D7AF372V.js → chunk-VBDIRSOG.js} +56 -2
- package/dist/chunk-VBDIRSOG.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/cli.mjs +1 -1
- package/dist/index-DX6Anc1-.d.mts +582 -0
- package/dist/index-DX6Anc1-.d.ts +582 -0
- package/dist/index.d.mts +148 -3
- package/dist/index.d.ts +148 -3
- package/dist/index.js +74 -66
- package/dist/index.mjs +1 -1
- package/dist/types/index.d.mts +1 -555
- package/dist/types/index.d.ts +1 -555
- package/package.json +2 -2
- package/dist/chunk-D7AF372V.js.map +0 -1
- package/dist/chunk-JLRQJWU5.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -11,28 +11,26 @@
|
|
|
11
11
|
[]()
|
|
12
12
|
[](https://twin-paws.github.io/soundcloud-api-ts/)
|
|
13
13
|
[](https://github.com/twin-paws/soundcloud-api-ts)
|
|
14
|
+
[](tools/coverage-baseline.json)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Zero dependencies, native `fetch`, built-in OAuth 2.1 + PKCE, automatic retry, and an interactive CLI.
|
|
18
|
-
|
|
19
|
-
This package is intended to be the recommended option for developers looking for a TypeScript SoundCloud API client.
|
|
16
|
+
The TypeScript SoundCloud API client built on the official API. Zero runtime dependencies, native `fetch`, OAuth 2.1 + PKCE, production-grade retry and deduplication, pluggable cache, raw escape hatch, and an interactive CLI.
|
|
20
17
|
|
|
21
18
|
## Why This Package?
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
Most TypeScript SoundCloud clients fall into one of two categories: unmaintained wrappers that predate OAuth 2.1, or scrapers that harvest undocumented `api-v2` client IDs from browser dev tools and break whenever SoundCloud ships a frontend update. soundcloud-api-ts is neither.
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
22
|
+
It is built on SoundCloud's **official documented API** with registered app credentials, OAuth 2.1 + PKCE, and a production-grade HTTP layer — all with zero runtime dependencies.
|
|
23
|
+
|
|
24
|
+
- **Official API only** — `api.soundcloud.com` + `secure.soundcloud.com` OAuth. No `api-v2` scraping, no harvested client IDs, no terms violations.
|
|
25
|
+
- **TypeScript-first** — full types ship in the package. No `@types/*` installs, no casting to `any`.
|
|
26
|
+
- **Zero dependencies** — native `fetch`, nothing in `node_modules` at runtime. 4.5 KB min+gz.
|
|
27
|
+
- **Production HTTP layer** — exponential backoff on 429/5xx, `Retry-After` header respected, in-flight GET deduplication, pluggable cache interface, `onRetry` hook.
|
|
28
|
+
- **Runtime portable** — inject your own `fetch` and `AbortController` for Cloudflare Workers, Bun, Deno, and Edge runtimes.
|
|
29
|
+
- **Raw escape hatch** — `sc.raw.get('/any/endpoint/{id}', { id })` calls anything in the spec, not just wrapped endpoints. Never blocked by a missing wrapper.
|
|
30
|
+
- **Full auth support** — client credentials flow for server-to-server, authorization code + PKCE for user-context operations, auto token refresh on 401.
|
|
31
|
+
- **Pagination built-in** — async iterators and `fetchAll` helpers across all paginated endpoints.
|
|
32
|
+
- **Interactive CLI** — `sc-cli tracks <id>`, `sc-cli search <query>`, `sc-cli me` from your terminal.
|
|
33
|
+
- **LLM-friendly** — ships `llms.txt`, `llms-full.txt`, and `AGENTS.md` for AI coding agents.
|
|
36
34
|
|
|
37
35
|
## Comparison
|
|
38
36
|
|
|
@@ -40,22 +38,23 @@ Unlike legacy JavaScript SoundCloud SDKs and community wrappers that require sep
|
|
|
40
38
|
| --- | --- | --- | --- |
|
|
41
39
|
| TypeScript | ✅ Native | ✅ | ✅ |
|
|
42
40
|
| Dependencies | **0** | 1 | 3 (lodash, cookie, undici) |
|
|
43
|
-
| Bundle size (min+gz) | **4.5 KB** | ❌ unbundlable
|
|
44
|
-
| Auth method | **Official OAuth 2.1** | ⚠️
|
|
41
|
+
| Bundle size (min+gz) | **4.5 KB** | ❌ unbundlable | 191 KB |
|
|
42
|
+
| Auth method | **Official OAuth 2.1** | ⚠️ Scrapes client ID | ⚠️ Scrapes client ID |
|
|
45
43
|
| PKCE support | ✅ | ❌ | ❌ |
|
|
46
|
-
| Auto token refresh | ✅
|
|
47
|
-
| Auto retry (429/5xx) | ✅
|
|
44
|
+
| Auto token refresh | ✅ | ❌ | ❌ |
|
|
45
|
+
| Auto retry (429/5xx) | ✅ + Retry-After | ❌ | ❌ |
|
|
46
|
+
| In-flight deduplication | ✅ | ❌ | ❌ |
|
|
47
|
+
| Pluggable cache interface | ✅ | ❌ | ❌ |
|
|
48
|
+
| Fetch injection (Workers/Bun/Deno) | ✅ | ❌ | ❌ |
|
|
49
|
+
| Raw escape hatch | ✅ `sc.raw` | ❌ | ❌ |
|
|
48
50
|
| CLI tool | ✅ `sc-cli` | ❌ | ❌ |
|
|
49
51
|
| Pagination helpers | ✅ async iterators | ❌ | ✅ |
|
|
50
52
|
| Typed errors | ✅ `SoundCloudError` | ❌ | ❌ |
|
|
51
53
|
| Test coverage | **100%** | — | — |
|
|
52
54
|
| API docs site | ✅ [TypeDoc](https://twin-paws.github.io/soundcloud-api-ts/) | ✅ | ❌ |
|
|
53
55
|
| LLM/AI-friendly | ✅ llms.txt + AGENTS.md | ❌ | ❌ |
|
|
54
|
-
| Maintained | ✅ 2026 | ✅ 2025 | ✅ 2026 |
|
|
55
56
|
|
|
56
|
-
> **Why does auth method matter?** `soundcloud.ts` and `soundcloud-fetch`
|
|
57
|
-
>
|
|
58
|
-
> `soundcloud-api-ts` uses the **official documented API** (`api.soundcloud.com`) with registered app credentials, OAuth 2.1 via `secure.soundcloud.com` as specified by SoundCloud, PKCE for public clients, and automatic token refresh.
|
|
57
|
+
> **Why does auth method matter?** `soundcloud.ts` and `soundcloud-fetch` scrape SoundCloud's undocumented `api-v2` and require harvesting a client ID from browser dev tools. This breaks whenever SoundCloud ships a frontend update, and the [API Terms of Use](https://developers.soundcloud.com/docs/api/terms-of-use) explicitly prohibit it: *"any attempt to circumvent this and obtain a new client ID and Security Code is strictly prohibited."*
|
|
59
58
|
|
|
60
59
|
**Coming from `soundcloud.ts`?** See the [Migration Guide](docs/MIGRATING.md) — most changes are find-and-replace.
|
|
61
60
|
|
|
@@ -182,6 +181,17 @@ await sc.auth.signOut(token.access_token);
|
|
|
182
181
|
sc.clearToken();
|
|
183
182
|
```
|
|
184
183
|
|
|
184
|
+
### Auth at a glance
|
|
185
|
+
|
|
186
|
+
| Endpoint category | Client Credentials | User Token |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| tracks, users, search, playlists, resolve | ✅ | ✅ |
|
|
189
|
+
| /me endpoints | ❌ | ✅ |
|
|
190
|
+
| likes, reposts | ❌ | ✅ |
|
|
191
|
+
| create/update/delete | ❌ | ✅ |
|
|
192
|
+
|
|
193
|
+
See [Auth Guide](docs/auth-guide.md) for full details, token provider patterns, and troubleshooting.
|
|
194
|
+
|
|
185
195
|
## Client Class
|
|
186
196
|
|
|
187
197
|
The `SoundCloudClient` class organizes all endpoints into namespaces. Token is resolved automatically when `setToken()` has been called. Override per-call via `{ token: "..." }` options object.
|
|
@@ -216,6 +226,7 @@ sc.me.unfollow(userUrn, options?)
|
|
|
216
226
|
sc.me.getFollowers(limit?, options?)
|
|
217
227
|
sc.me.getPlaylists(limit?, options?)
|
|
218
228
|
sc.me.getTracks(limit?, options?)
|
|
229
|
+
sc.me.getConnections(options?) // connected social accounts; may require app approval
|
|
219
230
|
|
|
220
231
|
// Users
|
|
221
232
|
sc.users.getUser(userId, options?)
|
|
@@ -229,6 +240,7 @@ sc.users.getWebProfiles(userId, options?)
|
|
|
229
240
|
|
|
230
241
|
// Tracks
|
|
231
242
|
sc.tracks.getTrack(trackId, options?)
|
|
243
|
+
sc.tracks.getTracks(ids[], options?) // batch fetch by IDs
|
|
232
244
|
sc.tracks.getStreams(trackId, options?)
|
|
233
245
|
sc.tracks.getComments(trackId, limit?, options?)
|
|
234
246
|
sc.tracks.createComment(trackId, body, timestamp?, options?)
|
|
@@ -872,6 +872,27 @@ var SoundCloudClient = class _SoundCloudClient {
|
|
|
872
872
|
const t = resolveToken(this.getToken, options?.token);
|
|
873
873
|
return this.fetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
874
874
|
}
|
|
875
|
+
/**
|
|
876
|
+
* List the authenticated user's connected external social accounts.
|
|
877
|
+
*
|
|
878
|
+
* @param options - Optional token override
|
|
879
|
+
* @returns Array of connection objects for linked social services (Twitter, Facebook, etc.)
|
|
880
|
+
* @throws {SoundCloudError} When the API returns an error
|
|
881
|
+
*
|
|
882
|
+
* @remarks This endpoint may require elevated API access or app approval.
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* ```ts
|
|
886
|
+
* const connections = await sc.me.getConnections();
|
|
887
|
+
* connections.forEach(c => console.log(c.service, c.display_name));
|
|
888
|
+
* ```
|
|
889
|
+
*
|
|
890
|
+
* @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_connections
|
|
891
|
+
*/
|
|
892
|
+
async getConnections(options) {
|
|
893
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
894
|
+
return this.fetch({ path: "/me/connections", method: "GET", token: t });
|
|
895
|
+
}
|
|
875
896
|
}
|
|
876
897
|
SoundCloudClient2.Me = Me;
|
|
877
898
|
class Users {
|
|
@@ -1043,6 +1064,26 @@ var SoundCloudClient = class _SoundCloudClient {
|
|
|
1043
1064
|
const t = resolveToken(this.getToken, options?.token);
|
|
1044
1065
|
return this.fetch({ path: `/tracks/${trackId}`, method: "GET", token: t });
|
|
1045
1066
|
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Fetch multiple tracks by their IDs in a single request.
|
|
1069
|
+
*
|
|
1070
|
+
* @param ids - Array of track IDs (numeric or string URNs)
|
|
1071
|
+
* @param options - Optional token override
|
|
1072
|
+
* @returns Array of track objects (may be shorter than `ids` if some tracks are unavailable)
|
|
1073
|
+
* @throws {SoundCloudError} When the API returns an error
|
|
1074
|
+
*
|
|
1075
|
+
* @example
|
|
1076
|
+
* ```ts
|
|
1077
|
+
* const tracks = await sc.tracks.getTracks([123456, 234567, 345678]);
|
|
1078
|
+
* tracks.forEach(t => console.log(t.title));
|
|
1079
|
+
* ```
|
|
1080
|
+
*
|
|
1081
|
+
* @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks
|
|
1082
|
+
*/
|
|
1083
|
+
async getTracks(ids, options) {
|
|
1084
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1085
|
+
return this.fetch({ path: `/tracks?ids=${ids.join(",")}`, method: "GET", token: t });
|
|
1086
|
+
}
|
|
1046
1087
|
/**
|
|
1047
1088
|
* Get stream URLs for a track.
|
|
1048
1089
|
*
|
|
@@ -1639,6 +1680,7 @@ var IMPLEMENTED_OPERATIONS = [
|
|
|
1639
1680
|
"get_me_followers",
|
|
1640
1681
|
"get_me_playlists",
|
|
1641
1682
|
"get_me_tracks",
|
|
1683
|
+
"get_me_connections",
|
|
1642
1684
|
// Users
|
|
1643
1685
|
"get_users_user_id",
|
|
1644
1686
|
"get_users_user_id_followers",
|
|
@@ -1800,6 +1842,13 @@ var getUserWebProfiles = (token, userId) => scFetch({ path: `/users/${userId}/we
|
|
|
1800
1842
|
// src/tracks/getTrack.ts
|
|
1801
1843
|
var getTrack = (token, trackId) => scFetch({ path: `/tracks/${trackId}`, method: "GET", token });
|
|
1802
1844
|
|
|
1845
|
+
// src/tracks/getTracks.ts
|
|
1846
|
+
var getTracks = (token, ids) => scFetch({
|
|
1847
|
+
path: `/tracks?ids=${ids.join(",")}`,
|
|
1848
|
+
method: "GET",
|
|
1849
|
+
token
|
|
1850
|
+
});
|
|
1851
|
+
|
|
1803
1852
|
// src/tracks/getComments.ts
|
|
1804
1853
|
var getTrackComments = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token });
|
|
1805
1854
|
|
|
@@ -1918,6 +1967,9 @@ var getMePlaylists = (token, limit) => scFetch({ path: `/me/playlists?${limit ?
|
|
|
1918
1967
|
// src/me/tracks.ts
|
|
1919
1968
|
var getMeTracks = (token, limit) => scFetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1920
1969
|
|
|
1970
|
+
// src/me/connections.ts
|
|
1971
|
+
var getMeConnections = (token) => scFetch({ path: "/me/connections", method: "GET", token });
|
|
1972
|
+
|
|
1921
1973
|
// src/likes/index.ts
|
|
1922
1974
|
var likePlaylist = async (token, playlistId) => {
|
|
1923
1975
|
try {
|
|
@@ -1973,6 +2025,6 @@ var unrepostPlaylist = async (token, playlistId) => {
|
|
|
1973
2025
|
// src/utils/widget.ts
|
|
1974
2026
|
var getSoundCloudWidgetUrl = (trackId) => `https%3A//api.soundcloud.com/tracks/${trackId}&show_teaser=false&color=%2300a99d&inverse=false&show_user=false&sharing=false&buying=false&liking=false&show_artwork=false&show_name=false`;
|
|
1975
2027
|
|
|
1976
|
-
export { IMPLEMENTED_OPERATIONS, InFlightDeduper, RawClient, SoundCloudClient, createPlaylist, createTrackComment, deletePlaylist, deleteTrack, fetchAll, followUser, generateCodeChallenge, generateCodeVerifier, getAuthorizationUrl, getClientToken, getFollowers, getFollowings, getMe, getMeActivities, getMeActivitiesOwn, getMeActivitiesTracks, getMeFollowers, getMeFollowings, getMeFollowingsTracks, getMeLikesPlaylists, getMeLikesTracks, getMePlaylists, getMeTracks, getPlaylist, getPlaylistReposts, getPlaylistTracks, getRelatedTracks, getSoundCloudWidgetUrl, getTrack, getTrackComments, getTrackLikes, getTrackReposts, getTrackStreams, getUser, getUserLikesPlaylists, getUserLikesTracks, getUserPlaylists, getUserToken, getUserTracks, getUserWebProfiles, likePlaylist, likeTrack, paginate, paginateItems, refreshUserToken, repostPlaylist, repostTrack, resolveUrl, scFetch, scFetchUrl, searchPlaylists, searchTracks, searchUsers, signOut, unfollowUser, unlikePlaylist, unlikeTrack, unrepostPlaylist, unrepostTrack, updatePlaylist, updateTrack };
|
|
1977
|
-
//# sourceMappingURL=chunk-
|
|
1978
|
-
//# sourceMappingURL=chunk-
|
|
2028
|
+
export { IMPLEMENTED_OPERATIONS, InFlightDeduper, RawClient, SoundCloudClient, createPlaylist, createTrackComment, deletePlaylist, deleteTrack, fetchAll, followUser, generateCodeChallenge, generateCodeVerifier, getAuthorizationUrl, getClientToken, getFollowers, getFollowings, getMe, getMeActivities, getMeActivitiesOwn, getMeActivitiesTracks, getMeConnections, getMeFollowers, getMeFollowings, getMeFollowingsTracks, getMeLikesPlaylists, getMeLikesTracks, getMePlaylists, getMeTracks, getPlaylist, getPlaylistReposts, getPlaylistTracks, getRelatedTracks, getSoundCloudWidgetUrl, getTrack, getTrackComments, getTrackLikes, getTrackReposts, getTrackStreams, getTracks, getUser, getUserLikesPlaylists, getUserLikesTracks, getUserPlaylists, getUserToken, getUserTracks, getUserWebProfiles, likePlaylist, likeTrack, paginate, paginateItems, refreshUserToken, repostPlaylist, repostTrack, resolveUrl, scFetch, scFetchUrl, searchPlaylists, searchTracks, searchUsers, signOut, unfollowUser, unlikePlaylist, unlikeTrack, unrepostPlaylist, unrepostTrack, updatePlaylist, updateTrack };
|
|
2029
|
+
//# sourceMappingURL=chunk-JH7TLL2C.mjs.map
|
|
2030
|
+
//# sourceMappingURL=chunk-JH7TLL2C.mjs.map
|