soundcloud-api-ts 1.2.0 → 1.4.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/dist/index.js CHANGED
@@ -1,8 +1,36 @@
1
1
  'use strict';
2
2
 
3
+ var chunkDZRZLIAH_js = require('./chunk-DZRZLIAH.js');
4
+
3
5
  // src/client/http.ts
4
6
  var BASE_URL = "https://api.soundcloud.com";
7
+ var DEFAULT_RETRY = { maxRetries: 3, retryBaseDelay: 1e3 };
8
+ function delay(ms) {
9
+ return new Promise((resolve) => setTimeout(resolve, ms));
10
+ }
11
+ function isRetryable(status) {
12
+ return status === 429 || status >= 500 && status <= 599;
13
+ }
14
+ function getRetryDelay(response, attempt, config) {
15
+ if (response.status === 429) {
16
+ const retryAfter = response.headers.get("retry-after");
17
+ if (retryAfter) {
18
+ const seconds = Number(retryAfter);
19
+ if (!Number.isNaN(seconds)) return seconds * 1e3;
20
+ }
21
+ }
22
+ const base = config.retryBaseDelay * Math.pow(2, attempt);
23
+ return base + Math.random() * base * 0.1;
24
+ }
25
+ async function parseErrorBody(response) {
26
+ try {
27
+ return await response.json();
28
+ } catch {
29
+ return void 0;
30
+ }
31
+ }
5
32
  async function scFetch(options, refreshCtx) {
33
+ const retryConfig = refreshCtx?.retry ?? DEFAULT_RETRY;
6
34
  const execute = async (tokenOverride) => {
7
35
  const url = `${BASE_URL}${options.path}`;
8
36
  const headers = {
@@ -26,32 +54,44 @@ async function scFetch(options, refreshCtx) {
26
54
  } else if (options.contentType) {
27
55
  headers["Content-Type"] = options.contentType;
28
56
  }
29
- const response = await fetch(url, {
30
- method: options.method,
31
- headers,
32
- body: fetchBody,
33
- redirect: "manual"
34
- });
35
- if (response.status === 302) {
36
- const location = response.headers.get("location");
37
- if (location) {
38
- return location;
57
+ let lastResponse;
58
+ for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
59
+ const response = await fetch(url, {
60
+ method: options.method,
61
+ headers,
62
+ body: fetchBody,
63
+ redirect: "manual"
64
+ });
65
+ if (response.status === 302) {
66
+ const location = response.headers.get("location");
67
+ if (location) return location;
68
+ }
69
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
70
+ return void 0;
71
+ }
72
+ if (response.ok) {
73
+ return response.json();
74
+ }
75
+ if (!isRetryable(response.status)) {
76
+ const body2 = await parseErrorBody(response);
77
+ throw new chunkDZRZLIAH_js.SoundCloudError(response.status, response.statusText, body2);
78
+ }
79
+ lastResponse = response;
80
+ if (attempt < retryConfig.maxRetries) {
81
+ const delayMs = getRetryDelay(response, attempt, retryConfig);
82
+ retryConfig.onDebug?.(
83
+ `Retry ${attempt + 1}/${retryConfig.maxRetries} after ${Math.round(delayMs)}ms (status ${response.status})`
84
+ );
85
+ await delay(delayMs);
39
86
  }
40
87
  }
41
- if (response.status === 204 || response.headers.get("content-length") === "0") {
42
- return void 0;
43
- }
44
- if (!response.ok) {
45
- throw new Error(
46
- `SoundCloud API error: ${response.status} ${response.statusText}`
47
- );
48
- }
49
- return response.json();
88
+ const body = await parseErrorBody(lastResponse);
89
+ throw new chunkDZRZLIAH_js.SoundCloudError(lastResponse.status, lastResponse.statusText, body);
50
90
  };
51
91
  try {
52
92
  return await execute();
53
93
  } catch (err) {
54
- if (refreshCtx?.onTokenRefresh && err instanceof Error && err.message.includes("401")) {
94
+ if (refreshCtx?.onTokenRefresh && err instanceof chunkDZRZLIAH_js.SoundCloudError && err.status === 401) {
55
95
  const newToken = await refreshCtx.onTokenRefresh();
56
96
  refreshCtx.setToken(newToken.access_token, newToken.refresh_token);
57
97
  return execute(newToken.access_token);
@@ -59,21 +99,38 @@ async function scFetch(options, refreshCtx) {
59
99
  throw err;
60
100
  }
61
101
  }
62
- async function scFetchUrl(url, token) {
102
+ async function scFetchUrl(url, token, retryConfig) {
103
+ const config = retryConfig ?? DEFAULT_RETRY;
63
104
  const headers = { Accept: "application/json" };
64
105
  if (token) headers["Authorization"] = `OAuth ${token}`;
65
- const response = await fetch(url, { method: "GET", headers, redirect: "manual" });
66
- if (response.status === 302) {
67
- const location = response.headers.get("location");
68
- if (location) return location;
69
- }
70
- if (response.status === 204 || response.headers.get("content-length") === "0") {
71
- return void 0;
72
- }
73
- if (!response.ok) {
74
- throw new Error(`SoundCloud API error: ${response.status} ${response.statusText}`);
106
+ let lastResponse;
107
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
108
+ const response = await fetch(url, { method: "GET", headers, redirect: "manual" });
109
+ if (response.status === 302) {
110
+ const location = response.headers.get("location");
111
+ if (location) return location;
112
+ }
113
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
114
+ return void 0;
115
+ }
116
+ if (response.ok) {
117
+ return response.json();
118
+ }
119
+ if (!isRetryable(response.status)) {
120
+ const body2 = await parseErrorBody(response);
121
+ throw new chunkDZRZLIAH_js.SoundCloudError(response.status, response.statusText, body2);
122
+ }
123
+ lastResponse = response;
124
+ if (attempt < config.maxRetries) {
125
+ const delayMs = getRetryDelay(response, attempt, config);
126
+ config.onDebug?.(
127
+ `Retry ${attempt + 1}/${config.maxRetries} after ${Math.round(delayMs)}ms (status ${response.status})`
128
+ );
129
+ await delay(delayMs);
130
+ }
75
131
  }
76
- return response.json();
132
+ const body = await parseErrorBody(lastResponse);
133
+ throw new chunkDZRZLIAH_js.SoundCloudError(lastResponse.status, lastResponse.statusText, body);
77
134
  }
78
135
 
79
136
  // src/client/paginate.ts
@@ -114,26 +171,50 @@ exports.SoundCloudClient = class _SoundCloudClient {
114
171
  config;
115
172
  _accessToken;
116
173
  _refreshToken;
174
+ /** Authentication methods (OAuth token grants, sign out) */
117
175
  auth;
176
+ /** Authenticated user endpoints (/me) */
118
177
  me;
178
+ /** User profile endpoints (/users) */
119
179
  users;
180
+ /** Track endpoints (/tracks) */
120
181
  tracks;
182
+ /** Playlist endpoints (/playlists) */
121
183
  playlists;
184
+ /** Search endpoints */
122
185
  search;
186
+ /** URL resolution endpoint (/resolve) */
123
187
  resolve;
188
+ /** Like/unlike actions (/likes) */
124
189
  likes;
190
+ /** Repost/unrepost actions (/reposts) */
125
191
  reposts;
192
+ /**
193
+ * Creates a new SoundCloudClient instance.
194
+ *
195
+ * @param config - Client configuration including OAuth credentials and optional settings
196
+ */
126
197
  constructor(config) {
127
198
  this.config = config;
128
199
  const getToken = () => this._accessToken;
200
+ const retryConfig = {
201
+ maxRetries: config.maxRetries ?? 3,
202
+ retryBaseDelay: config.retryBaseDelay ?? 1e3,
203
+ onDebug: config.onDebug
204
+ };
129
205
  const refreshCtx = config.onTokenRefresh ? {
130
206
  getToken,
131
207
  onTokenRefresh: async () => {
132
208
  const result = await config.onTokenRefresh(this);
133
209
  return result;
134
210
  },
135
- setToken: (a, r) => this.setToken(a, r)
136
- } : void 0;
211
+ setToken: (a, r) => this.setToken(a, r),
212
+ retry: retryConfig
213
+ } : {
214
+ getToken,
215
+ setToken: (a, r) => this.setToken(a, r),
216
+ retry: retryConfig
217
+ };
137
218
  this.auth = new _SoundCloudClient.Auth(this.config);
138
219
  this.me = new _SoundCloudClient.Me(getToken, refreshCtx);
139
220
  this.users = new _SoundCloudClient.Users(getToken, refreshCtx);
@@ -144,29 +225,38 @@ exports.SoundCloudClient = class _SoundCloudClient {
144
225
  this.likes = new _SoundCloudClient.Likes(getToken, refreshCtx);
145
226
  this.reposts = new _SoundCloudClient.Reposts(getToken, refreshCtx);
146
227
  }
147
- /** Store an access token (and optionally refresh token) on this client instance. */
228
+ /**
229
+ * Store an access token (and optionally refresh token) on this client instance.
230
+ *
231
+ * @param accessToken - The OAuth access token to store
232
+ * @param refreshToken - Optional refresh token for automatic token renewal
233
+ */
148
234
  setToken(accessToken, refreshToken) {
149
235
  this._accessToken = accessToken;
150
236
  if (refreshToken !== void 0) this._refreshToken = refreshToken;
151
237
  }
152
- /** Clear stored tokens. */
238
+ /** Clear all stored tokens from this client instance. */
153
239
  clearToken() {
154
240
  this._accessToken = void 0;
155
241
  this._refreshToken = void 0;
156
242
  }
157
- /** Get the currently stored access token, if any. */
243
+ /** Get the currently stored access token, or `undefined` if none is set. */
158
244
  get accessToken() {
159
245
  return this._accessToken;
160
246
  }
161
- /** Get the currently stored refresh token, if any. */
247
+ /** Get the currently stored refresh token, or `undefined` if none is set. */
162
248
  get refreshToken() {
163
249
  return this._refreshToken;
164
250
  }
165
251
  /**
166
252
  * Async generator that follows `next_href` automatically, yielding each page's `collection`.
167
253
  *
254
+ * @param firstPage - Function that fetches the first page
255
+ * @returns An async generator yielding arrays of items (one per page)
256
+ *
257
+ * @example
168
258
  * ```ts
169
- * for await (const page of sc.paginate(() => sc.search.tracks("lofi"))) {
259
+ * for await (const page of sc.paginate(() => sc.search.tracks('lofi'))) {
170
260
  * console.log(page); // SoundCloudTrack[]
171
261
  * }
172
262
  * ```
@@ -178,9 +268,13 @@ exports.SoundCloudClient = class _SoundCloudClient {
178
268
  /**
179
269
  * Async generator that yields individual items across all pages.
180
270
  *
271
+ * @param firstPage - Function that fetches the first page
272
+ * @returns An async generator yielding individual items
273
+ *
274
+ * @example
181
275
  * ```ts
182
- * for await (const track of sc.paginateItems(() => sc.search.tracks("lofi"))) {
183
- * console.log(track); // single SoundCloudTrack
276
+ * for await (const track of sc.paginateItems(() => sc.search.tracks('lofi'))) {
277
+ * console.log(track.title); // single SoundCloudTrack
184
278
  * }
185
279
  * ```
186
280
  */
@@ -191,8 +285,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
191
285
  /**
192
286
  * Collects all pages into a single flat array.
193
287
  *
288
+ * @param firstPage - Function that fetches the first page
289
+ * @param options - Optional configuration
290
+ * @param options.maxItems - Maximum number of items to collect
291
+ * @returns A promise resolving to a flat array of all items
292
+ *
293
+ * @example
194
294
  * ```ts
195
- * const allTracks = await sc.fetchAll(() => sc.search.tracks("lofi"), { maxItems: 100 });
295
+ * const allTracks = await sc.fetchAll(() => sc.search.tracks('lofi'), { maxItems: 100 });
296
+ * console.log(allTracks.length);
196
297
  * ```
197
298
  */
198
299
  fetchAll(firstPage, options) {
@@ -205,7 +306,23 @@ exports.SoundCloudClient = class _SoundCloudClient {
205
306
  constructor(config) {
206
307
  this.config = config;
207
308
  }
208
- /** Build the authorization URL to redirect users to SoundCloud's login. */
309
+ /**
310
+ * Build the authorization URL to redirect users to SoundCloud's OAuth login page.
311
+ *
312
+ * @param options - Optional parameters for the authorization request
313
+ * @param options.state - Opaque state value for CSRF protection
314
+ * @param options.codeChallenge - PKCE S256 code challenge for enhanced security
315
+ * @returns The full authorization URL to redirect the user to
316
+ * @throws {Error} If `redirectUri` was not provided in the client config
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * const url = sc.auth.getAuthorizationUrl({ state: 'random-state' });
321
+ * // Redirect user to `url`
322
+ * ```
323
+ *
324
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/oauth2
325
+ */
209
326
  getAuthorizationUrl(options) {
210
327
  if (!this.config.redirectUri) throw new Error("redirectUri is required for getAuthorizationUrl");
211
328
  const params = new URLSearchParams({
@@ -220,7 +337,20 @@ exports.SoundCloudClient = class _SoundCloudClient {
220
337
  }
221
338
  return `https://api.soundcloud.com/connect?${params}`;
222
339
  }
223
- /** POST /oauth2/token — client_credentials grant */
340
+ /**
341
+ * Exchange client credentials for an access token (machine-to-machine auth).
342
+ *
343
+ * @returns The OAuth token response
344
+ * @throws {SoundCloudError} When authentication fails
345
+ *
346
+ * @example
347
+ * ```ts
348
+ * const token = await sc.auth.getClientToken();
349
+ * sc.setToken(token.access_token);
350
+ * ```
351
+ *
352
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/oauth2/post_oauth2_token
353
+ */
224
354
  async getClientToken() {
225
355
  return scFetch({
226
356
  path: "/oauth2/token",
@@ -232,7 +362,22 @@ exports.SoundCloudClient = class _SoundCloudClient {
232
362
  })
233
363
  });
234
364
  }
235
- /** POST /oauth2/token — authorization_code grant */
365
+ /**
366
+ * Exchange an authorization code for user tokens (authorization_code grant).
367
+ *
368
+ * @param code - The authorization code received from the OAuth callback
369
+ * @param codeVerifier - PKCE code verifier if a code challenge was used
370
+ * @returns The OAuth token response including access and refresh tokens
371
+ * @throws {SoundCloudError} When the code is invalid or expired
372
+ *
373
+ * @example
374
+ * ```ts
375
+ * const token = await sc.auth.getUserToken(code, codeVerifier);
376
+ * sc.setToken(token.access_token, token.refresh_token);
377
+ * ```
378
+ *
379
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/oauth2/post_oauth2_token
380
+ */
236
381
  async getUserToken(code, codeVerifier) {
237
382
  const params = {
238
383
  grant_type: "authorization_code",
@@ -248,7 +393,21 @@ exports.SoundCloudClient = class _SoundCloudClient {
248
393
  body: new URLSearchParams(params)
249
394
  });
250
395
  }
251
- /** POST /oauth2/token — refresh_token grant */
396
+ /**
397
+ * Refresh an expired access token using a refresh token.
398
+ *
399
+ * @param refreshToken - The refresh token from a previous token response
400
+ * @returns A new OAuth token response with fresh access and refresh tokens
401
+ * @throws {SoundCloudError} When the refresh token is invalid or expired
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * const newToken = await sc.auth.refreshUserToken(sc.refreshToken!);
406
+ * sc.setToken(newToken.access_token, newToken.refresh_token);
407
+ * ```
408
+ *
409
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/oauth2/post_oauth2_token
410
+ */
252
411
  async refreshUserToken(refreshToken) {
253
412
  return scFetch({
254
413
  path: "/oauth2/token",
@@ -263,10 +422,19 @@ exports.SoundCloudClient = class _SoundCloudClient {
263
422
  });
264
423
  }
265
424
  /**
266
- * POST /sign-out invalidates session.
425
+ * Invalidate the session associated with an access token.
267
426
  *
268
427
  * **Note:** This hits `https://secure.soundcloud.com`, NOT the regular
269
428
  * `api.soundcloud.com` host used by all other endpoints.
429
+ *
430
+ * @param accessToken - The access token to invalidate
431
+ * @throws {Error} When the sign-out request fails
432
+ *
433
+ * @example
434
+ * ```ts
435
+ * await sc.auth.signOut(sc.accessToken!);
436
+ * sc.clearToken();
437
+ * ```
270
438
  */
271
439
  async signOut(accessToken) {
272
440
  const res = await fetch("https://secure.soundcloud.com/sign-out", {
@@ -286,67 +454,209 @@ exports.SoundCloudClient = class _SoundCloudClient {
286
454
  fetch(opts) {
287
455
  return scFetch(opts, this.refreshCtx);
288
456
  }
289
- /** GET /me */
457
+ /**
458
+ * Get the authenticated user's profile.
459
+ *
460
+ * @param options - Optional token override
461
+ * @returns The authenticated user's full profile
462
+ * @throws {SoundCloudError} When the API returns an error
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * const me = await sc.me.getMe();
467
+ * console.log(me.username, me.followers_count);
468
+ * ```
469
+ *
470
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me
471
+ */
290
472
  async getMe(options) {
291
473
  const t = resolveToken(this.getToken, options?.token);
292
474
  return this.fetch({ path: "/me", method: "GET", token: t });
293
475
  }
294
- /** GET /me/activities */
476
+ /**
477
+ * Get the authenticated user's activity feed.
478
+ *
479
+ * @param limit - Maximum number of activities per page
480
+ * @param options - Optional token override
481
+ * @returns Paginated activities response with `future_href` for polling
482
+ * @throws {SoundCloudError} When the API returns an error
483
+ *
484
+ * @example
485
+ * ```ts
486
+ * const activities = await sc.me.getActivities(25);
487
+ * activities.collection.forEach(a => console.log(a.type, a.created_at));
488
+ * ```
489
+ *
490
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_activities
491
+ */
295
492
  async getActivities(limit, options) {
296
493
  const t = resolveToken(this.getToken, options?.token);
297
494
  return this.fetch({ path: `/me/activities?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
298
495
  }
299
- /** GET /me/activities/all/own */
496
+ /**
497
+ * Get the authenticated user's own activities (uploads, reposts).
498
+ *
499
+ * @param limit - Maximum number of activities per page
500
+ * @param options - Optional token override
501
+ * @returns Paginated activities response
502
+ * @throws {SoundCloudError} When the API returns an error
503
+ *
504
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_activities_all_own
505
+ */
300
506
  async getActivitiesOwn(limit, options) {
301
507
  const t = resolveToken(this.getToken, options?.token);
302
508
  return this.fetch({ path: `/me/activities/all/own?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
303
509
  }
304
- /** GET /me/activities/tracks */
510
+ /**
511
+ * Get track-related activities in the authenticated user's feed.
512
+ *
513
+ * @param limit - Maximum number of activities per page
514
+ * @param options - Optional token override
515
+ * @returns Paginated activities response filtered to track activities
516
+ * @throws {SoundCloudError} When the API returns an error
517
+ *
518
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_activities_tracks
519
+ */
305
520
  async getActivitiesTracks(limit, options) {
306
521
  const t = resolveToken(this.getToken, options?.token);
307
522
  return this.fetch({ path: `/me/activities/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
308
523
  }
309
- /** GET /me/likes/tracks */
524
+ /**
525
+ * Get tracks liked by the authenticated user.
526
+ *
527
+ * @param limit - Maximum number of tracks per page
528
+ * @param options - Optional token override
529
+ * @returns Paginated list of liked tracks
530
+ * @throws {SoundCloudError} When the API returns an error
531
+ *
532
+ * @example
533
+ * ```ts
534
+ * const likes = await sc.me.getLikesTracks(50);
535
+ * likes.collection.forEach(t => console.log(t.title));
536
+ * ```
537
+ *
538
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_likes_tracks
539
+ */
310
540
  async getLikesTracks(limit, options) {
311
541
  const t = resolveToken(this.getToken, options?.token);
312
542
  return this.fetch({ path: `/me/likes/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
313
543
  }
314
- /** GET /me/likes/playlists */
544
+ /**
545
+ * Get playlists liked by the authenticated user.
546
+ *
547
+ * @param limit - Maximum number of playlists per page
548
+ * @param options - Optional token override
549
+ * @returns Paginated list of liked playlists
550
+ * @throws {SoundCloudError} When the API returns an error
551
+ *
552
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_likes_playlists
553
+ */
315
554
  async getLikesPlaylists(limit, options) {
316
555
  const t = resolveToken(this.getToken, options?.token);
317
556
  return this.fetch({ path: `/me/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
318
557
  }
319
- /** GET /me/followings */
558
+ /**
559
+ * Get users the authenticated user is following.
560
+ *
561
+ * @param limit - Maximum number of users per page
562
+ * @param options - Optional token override
563
+ * @returns Paginated list of followed users
564
+ * @throws {SoundCloudError} When the API returns an error
565
+ *
566
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_followings
567
+ */
320
568
  async getFollowings(limit, options) {
321
569
  const t = resolveToken(this.getToken, options?.token);
322
570
  return this.fetch({ path: `/me/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
323
571
  }
324
- /** GET /me/followings/tracks */
572
+ /**
573
+ * Get recent tracks from users the authenticated user is following.
574
+ *
575
+ * @param limit - Maximum number of tracks per page
576
+ * @param options - Optional token override
577
+ * @returns Paginated list of tracks from followed users
578
+ * @throws {SoundCloudError} When the API returns an error
579
+ *
580
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_followings_tracks
581
+ */
325
582
  async getFollowingsTracks(limit, options) {
326
583
  const t = resolveToken(this.getToken, options?.token);
327
584
  return this.fetch({ path: `/me/followings/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
328
585
  }
329
- /** PUT /me/followings/:user_urn — follow a user */
586
+ /**
587
+ * Follow a user.
588
+ *
589
+ * @param userUrn - The user's ID or URN to follow
590
+ * @param options - Optional token override
591
+ * @throws {SoundCloudError} When the API returns an error
592
+ *
593
+ * @example
594
+ * ```ts
595
+ * await sc.me.follow(123456);
596
+ * ```
597
+ *
598
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/put_me_followings__user_id_
599
+ */
330
600
  async follow(userUrn, options) {
331
601
  const t = resolveToken(this.getToken, options?.token);
332
602
  return this.fetch({ path: `/me/followings/${userUrn}`, method: "PUT", token: t });
333
603
  }
334
- /** DELETE /me/followings/:user_urn — unfollow a user */
604
+ /**
605
+ * Unfollow a user.
606
+ *
607
+ * @param userUrn - The user's ID or URN to unfollow
608
+ * @param options - Optional token override
609
+ * @throws {SoundCloudError} When the API returns an error
610
+ *
611
+ * @example
612
+ * ```ts
613
+ * await sc.me.unfollow(123456);
614
+ * ```
615
+ *
616
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/delete_me_followings__user_id_
617
+ */
335
618
  async unfollow(userUrn, options) {
336
619
  const t = resolveToken(this.getToken, options?.token);
337
620
  return this.fetch({ path: `/me/followings/${userUrn}`, method: "DELETE", token: t });
338
621
  }
339
- /** GET /me/followers */
622
+ /**
623
+ * Get the authenticated user's followers.
624
+ *
625
+ * @param limit - Maximum number of users per page
626
+ * @param options - Optional token override
627
+ * @returns Paginated list of follower users
628
+ * @throws {SoundCloudError} When the API returns an error
629
+ *
630
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_followers
631
+ */
340
632
  async getFollowers(limit, options) {
341
633
  const t = resolveToken(this.getToken, options?.token);
342
634
  return this.fetch({ path: `/me/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
343
635
  }
344
- /** GET /me/playlists */
636
+ /**
637
+ * Get the authenticated user's playlists.
638
+ *
639
+ * @param limit - Maximum number of playlists per page
640
+ * @param options - Optional token override
641
+ * @returns Paginated list of playlists
642
+ * @throws {SoundCloudError} When the API returns an error
643
+ *
644
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_playlists
645
+ */
345
646
  async getPlaylists(limit, options) {
346
647
  const t = resolveToken(this.getToken, options?.token);
347
648
  return this.fetch({ path: `/me/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
348
649
  }
349
- /** GET /me/tracks */
650
+ /**
651
+ * Get the authenticated user's tracks.
652
+ *
653
+ * @param limit - Maximum number of tracks per page
654
+ * @param options - Optional token override
655
+ * @returns Paginated list of tracks
656
+ * @throws {SoundCloudError} When the API returns an error
657
+ *
658
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/me/get_me_tracks
659
+ */
350
660
  async getTracks(limit, options) {
351
661
  const t = resolveToken(this.getToken, options?.token);
352
662
  return this.fetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
@@ -361,42 +671,133 @@ exports.SoundCloudClient = class _SoundCloudClient {
361
671
  fetch(opts) {
362
672
  return scFetch(opts, this.refreshCtx);
363
673
  }
364
- /** GET /users/:id */
674
+ /**
675
+ * Get a user's profile by ID.
676
+ *
677
+ * @param userId - The user's numeric ID or URN
678
+ * @param options - Optional token override
679
+ * @returns The user's public profile
680
+ * @throws {SoundCloudError} When the user is not found or the API returns an error
681
+ *
682
+ * @example
683
+ * ```ts
684
+ * const user = await sc.users.getUser(123456);
685
+ * console.log(user.username, user.followers_count);
686
+ * ```
687
+ *
688
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id_
689
+ */
365
690
  async getUser(userId, options) {
366
691
  const t = resolveToken(this.getToken, options?.token);
367
692
  return this.fetch({ path: `/users/${userId}`, method: "GET", token: t });
368
693
  }
369
- /** GET /users/:id/followers */
694
+ /**
695
+ * Get a user's followers.
696
+ *
697
+ * @param userId - The user's numeric ID or URN
698
+ * @param limit - Maximum number of followers per page
699
+ * @param options - Optional token override
700
+ * @returns Paginated list of follower users
701
+ * @throws {SoundCloudError} When the API returns an error
702
+ *
703
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__followers
704
+ */
370
705
  async getFollowers(userId, limit, options) {
371
706
  const t = resolveToken(this.getToken, options?.token);
372
707
  return this.fetch({ path: `/users/${userId}/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
373
708
  }
374
- /** GET /users/:id/followings */
709
+ /**
710
+ * Get users that a user is following.
711
+ *
712
+ * @param userId - The user's numeric ID or URN
713
+ * @param limit - Maximum number of users per page
714
+ * @param options - Optional token override
715
+ * @returns Paginated list of followed users
716
+ * @throws {SoundCloudError} When the API returns an error
717
+ *
718
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__followings
719
+ */
375
720
  async getFollowings(userId, limit, options) {
376
721
  const t = resolveToken(this.getToken, options?.token);
377
722
  return this.fetch({ path: `/users/${userId}/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
378
723
  }
379
- /** GET /users/:id/tracks */
724
+ /**
725
+ * Get a user's public tracks.
726
+ *
727
+ * @param userId - The user's numeric ID or URN
728
+ * @param limit - Maximum number of tracks per page
729
+ * @param options - Optional token override
730
+ * @returns Paginated list of tracks
731
+ * @throws {SoundCloudError} When the API returns an error
732
+ *
733
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__tracks
734
+ */
380
735
  async getTracks(userId, limit, options) {
381
736
  const t = resolveToken(this.getToken, options?.token);
382
737
  return this.fetch({ path: `/users/${userId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
383
738
  }
384
- /** GET /users/:id/playlists */
739
+ /**
740
+ * Get a user's public playlists.
741
+ *
742
+ * @param userId - The user's numeric ID or URN
743
+ * @param limit - Maximum number of playlists per page
744
+ * @param options - Optional token override
745
+ * @returns Paginated list of playlists (without full track data)
746
+ * @throws {SoundCloudError} When the API returns an error
747
+ *
748
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__playlists
749
+ */
385
750
  async getPlaylists(userId, limit, options) {
386
751
  const t = resolveToken(this.getToken, options?.token);
387
752
  return this.fetch({ path: `/users/${userId}/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true&show_tracks=false`, method: "GET", token: t });
388
753
  }
389
- /** GET /users/:id/likes/tracks */
754
+ /**
755
+ * Get tracks liked by a user.
756
+ *
757
+ * @param userId - The user's numeric ID or URN
758
+ * @param limit - Maximum number of tracks per page
759
+ * @param cursor - Pagination cursor from a previous response's `next_href`
760
+ * @param options - Optional token override
761
+ * @returns Paginated list of liked tracks
762
+ * @throws {SoundCloudError} When the API returns an error
763
+ *
764
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__likes_tracks
765
+ */
390
766
  async getLikesTracks(userId, limit, cursor, options) {
391
767
  const t = resolveToken(this.getToken, options?.token);
392
768
  return this.fetch({ path: `/users/${userId}/likes/tracks?${limit ? `limit=${limit}&` : ""}${cursor ? `cursor=${cursor}&` : ""}linked_partitioning=true`, method: "GET", token: t });
393
769
  }
394
- /** GET /users/:id/likes/playlists */
770
+ /**
771
+ * Get playlists liked by a user.
772
+ *
773
+ * @param userId - The user's numeric ID or URN
774
+ * @param limit - Maximum number of playlists per page
775
+ * @param options - Optional token override
776
+ * @returns Paginated list of liked playlists
777
+ * @throws {SoundCloudError} When the API returns an error
778
+ *
779
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__likes_playlists
780
+ */
395
781
  async getLikesPlaylists(userId, limit, options) {
396
782
  const t = resolveToken(this.getToken, options?.token);
397
783
  return this.fetch({ path: `/users/${userId}/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
398
784
  }
399
- /** GET /users/:id/web-profiles */
785
+ /**
786
+ * Get a user's external web profile links (Twitter, Instagram, etc.).
787
+ *
788
+ * @param userId - The user's numeric ID or URN
789
+ * @param options - Optional token override
790
+ * @returns Array of web profile objects
791
+ * @throws {SoundCloudError} When the API returns an error
792
+ *
793
+ * @example
794
+ * ```ts
795
+ * const profiles = await sc.users.getWebProfiles(123456);
796
+ * profiles.forEach(p => console.log(p.service, p.url));
797
+ * ```
798
+ *
799
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users__user_id__web_profiles
800
+ */
400
801
  async getWebProfiles(userId, options) {
401
802
  const t = resolveToken(this.getToken, options?.token);
402
803
  return this.fetch({ path: `/users/${userId}/web-profiles`, method: "GET", token: t });
@@ -411,22 +812,79 @@ exports.SoundCloudClient = class _SoundCloudClient {
411
812
  fetch(opts) {
412
813
  return scFetch(opts, this.refreshCtx);
413
814
  }
414
- /** GET /tracks/:id */
815
+ /**
816
+ * Get a track by ID.
817
+ *
818
+ * @param trackId - The track's numeric ID or URN
819
+ * @param options - Optional token override
820
+ * @returns The track object with full metadata
821
+ * @throws {SoundCloudError} When the track is not found or the API returns an error
822
+ *
823
+ * @example
824
+ * ```ts
825
+ * const track = await sc.tracks.getTrack(123456);
826
+ * console.log(track.title, track.duration);
827
+ * ```
828
+ *
829
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id_
830
+ */
415
831
  async getTrack(trackId, options) {
416
832
  const t = resolveToken(this.getToken, options?.token);
417
833
  return this.fetch({ path: `/tracks/${trackId}`, method: "GET", token: t });
418
834
  }
419
- /** GET /tracks/:id/streams */
835
+ /**
836
+ * Get stream URLs for a track.
837
+ *
838
+ * @param trackId - The track's numeric ID or URN
839
+ * @param options - Optional token override
840
+ * @returns Object containing available stream URLs (HLS, MP3, preview)
841
+ * @throws {SoundCloudError} When the track is not found or not streamable
842
+ *
843
+ * @example
844
+ * ```ts
845
+ * const streams = await sc.tracks.getStreams(123456);
846
+ * console.log(streams.hls_mp3_128_url);
847
+ * ```
848
+ *
849
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id__streams
850
+ */
420
851
  async getStreams(trackId, options) {
421
852
  const t = resolveToken(this.getToken, options?.token);
422
853
  return this.fetch({ path: `/tracks/${trackId}/streams`, method: "GET", token: t });
423
854
  }
424
- /** GET /tracks/:id/comments */
855
+ /**
856
+ * Get comments on a track.
857
+ *
858
+ * @param trackId - The track's numeric ID or URN
859
+ * @param limit - Maximum number of comments per page
860
+ * @param options - Optional token override
861
+ * @returns Paginated list of comments
862
+ * @throws {SoundCloudError} When the API returns an error
863
+ *
864
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id__comments
865
+ */
425
866
  async getComments(trackId, limit, options) {
426
867
  const t = resolveToken(this.getToken, options?.token);
427
868
  return this.fetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token: t });
428
869
  }
429
- /** POST /tracks/:id/comments */
870
+ /**
871
+ * Post a comment on a track.
872
+ *
873
+ * @param trackId - The track's numeric ID or URN
874
+ * @param body - The comment text
875
+ * @param timestamp - Position in the track in milliseconds where the comment is placed
876
+ * @param options - Optional token override
877
+ * @returns The created comment object
878
+ * @throws {SoundCloudError} When the API returns an error
879
+ *
880
+ * @example
881
+ * ```ts
882
+ * const comment = await sc.tracks.createComment(123456, 'Great track!', 30000);
883
+ * console.log(comment.id);
884
+ * ```
885
+ *
886
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/post_tracks__track_id__comments
887
+ */
430
888
  async createComment(trackId, body, timestamp, options) {
431
889
  const t = resolveToken(this.getToken, options?.token);
432
890
  return this.fetch({
@@ -436,27 +894,92 @@ exports.SoundCloudClient = class _SoundCloudClient {
436
894
  body: { comment: { body, ...timestamp !== void 0 ? { timestamp } : {} } }
437
895
  });
438
896
  }
439
- /** GET /tracks/:id/favoriters */
897
+ /**
898
+ * Get users who have liked (favorited) a track.
899
+ *
900
+ * @param trackId - The track's numeric ID or URN
901
+ * @param limit - Maximum number of users per page
902
+ * @param options - Optional token override
903
+ * @returns Paginated list of users who liked the track
904
+ * @throws {SoundCloudError} When the API returns an error
905
+ *
906
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id__favoriters
907
+ */
440
908
  async getLikes(trackId, limit, options) {
441
909
  const t = resolveToken(this.getToken, options?.token);
442
910
  return this.fetch({ path: `/tracks/${trackId}/favoriters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
443
911
  }
444
- /** GET /tracks/:id/reposters */
912
+ /**
913
+ * Get users who have reposted a track.
914
+ *
915
+ * @param trackId - The track's numeric ID or URN
916
+ * @param limit - Maximum number of users per page
917
+ * @param options - Optional token override
918
+ * @returns Paginated list of users who reposted the track
919
+ * @throws {SoundCloudError} When the API returns an error
920
+ *
921
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id__reposters
922
+ */
445
923
  async getReposts(trackId, limit, options) {
446
924
  const t = resolveToken(this.getToken, options?.token);
447
925
  return this.fetch({ path: `/tracks/${trackId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
448
926
  }
449
- /** GET /tracks/:id/related */
927
+ /**
928
+ * Get tracks related to a given track.
929
+ *
930
+ * @param trackId - The track's numeric ID or URN
931
+ * @param limit - Maximum number of related tracks to return
932
+ * @param options - Optional token override
933
+ * @returns Array of related tracks
934
+ * @throws {SoundCloudError} When the API returns an error
935
+ *
936
+ * @example
937
+ * ```ts
938
+ * const related = await sc.tracks.getRelated(123456, 5);
939
+ * related.forEach(t => console.log(t.title));
940
+ * ```
941
+ *
942
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks__track_id__related
943
+ */
450
944
  async getRelated(trackId, limit, options) {
451
945
  const t = resolveToken(this.getToken, options?.token);
452
946
  return this.fetch({ path: `/tracks/${trackId}/related${limit ? `?limit=${limit}` : ""}`, method: "GET", token: t });
453
947
  }
454
- /** PUT /tracks/:id — update track metadata */
948
+ /**
949
+ * Update a track's metadata.
950
+ *
951
+ * @param trackId - The track's numeric ID or URN
952
+ * @param params - Fields to update (title, description, genre, etc.)
953
+ * @param options - Optional token override
954
+ * @returns The updated track object
955
+ * @throws {SoundCloudError} When the API returns an error
956
+ *
957
+ * @example
958
+ * ```ts
959
+ * const updated = await sc.tracks.update(123456, { title: 'New Title', genre: 'Electronic' });
960
+ * console.log(updated.title);
961
+ * ```
962
+ *
963
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/put_tracks__track_id_
964
+ */
455
965
  async update(trackId, params, options) {
456
966
  const t = resolveToken(this.getToken, options?.token);
457
967
  return this.fetch({ path: `/tracks/${trackId}`, method: "PUT", token: t, body: { track: params } });
458
968
  }
459
- /** DELETE /tracks/:id */
969
+ /**
970
+ * Delete a track.
971
+ *
972
+ * @param trackId - The track's numeric ID or URN
973
+ * @param options - Optional token override
974
+ * @throws {SoundCloudError} When the API returns an error
975
+ *
976
+ * @example
977
+ * ```ts
978
+ * await sc.tracks.delete(123456);
979
+ * ```
980
+ *
981
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/delete_tracks__track_id_
982
+ */
460
983
  async delete(trackId, options) {
461
984
  const t = resolveToken(this.getToken, options?.token);
462
985
  return this.fetch({ path: `/tracks/${trackId}`, method: "DELETE", token: t });
@@ -471,32 +994,114 @@ exports.SoundCloudClient = class _SoundCloudClient {
471
994
  fetch(opts) {
472
995
  return scFetch(opts, this.refreshCtx);
473
996
  }
474
- /** GET /playlists/:id */
997
+ /**
998
+ * Get a playlist by ID.
999
+ *
1000
+ * @param playlistId - The playlist's numeric ID or URN
1001
+ * @param options - Optional token override
1002
+ * @returns The playlist object with track data
1003
+ * @throws {SoundCloudError} When the playlist is not found or the API returns an error
1004
+ *
1005
+ * @example
1006
+ * ```ts
1007
+ * const playlist = await sc.playlists.getPlaylist(123456);
1008
+ * console.log(playlist.title, playlist.track_count);
1009
+ * ```
1010
+ *
1011
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/get_playlists__playlist_id_
1012
+ */
475
1013
  async getPlaylist(playlistId, options) {
476
1014
  const t = resolveToken(this.getToken, options?.token);
477
1015
  return this.fetch({ path: `/playlists/${playlistId}`, method: "GET", token: t });
478
1016
  }
479
- /** GET /playlists/:id/tracks */
1017
+ /**
1018
+ * Get tracks in a playlist.
1019
+ *
1020
+ * @param playlistId - The playlist's numeric ID or URN
1021
+ * @param limit - Maximum number of tracks per page
1022
+ * @param offset - Number of tracks to skip (for offset-based pagination)
1023
+ * @param options - Optional token override
1024
+ * @returns Paginated list of tracks
1025
+ * @throws {SoundCloudError} When the API returns an error
1026
+ *
1027
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/get_playlists__playlist_id__tracks
1028
+ */
480
1029
  async getTracks(playlistId, limit, offset, options) {
481
1030
  const t = resolveToken(this.getToken, options?.token);
482
1031
  return this.fetch({ path: `/playlists/${playlistId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true${offset ? `&offset=${offset}` : ""}`, method: "GET", token: t });
483
1032
  }
484
- /** GET /playlists/:id/reposters */
1033
+ /**
1034
+ * Get users who have reposted a playlist.
1035
+ *
1036
+ * @param playlistId - The playlist's numeric ID or URN
1037
+ * @param limit - Maximum number of users per page
1038
+ * @param options - Optional token override
1039
+ * @returns Paginated list of users who reposted the playlist
1040
+ * @throws {SoundCloudError} When the API returns an error
1041
+ *
1042
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/get_playlists__playlist_id__reposters
1043
+ */
485
1044
  async getReposts(playlistId, limit, options) {
486
1045
  const t = resolveToken(this.getToken, options?.token);
487
1046
  return this.fetch({ path: `/playlists/${playlistId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
488
1047
  }
489
- /** POST /playlists — create a playlist */
1048
+ /**
1049
+ * Create a new playlist.
1050
+ *
1051
+ * @param params - Playlist creation parameters (title is required)
1052
+ * @param options - Optional token override
1053
+ * @returns The created playlist object
1054
+ * @throws {SoundCloudError} When the API returns an error
1055
+ *
1056
+ * @example
1057
+ * ```ts
1058
+ * const playlist = await sc.playlists.create({
1059
+ * title: 'My Favorites',
1060
+ * sharing: 'public',
1061
+ * tracks: [{ urn: 'soundcloud:tracks:123' }],
1062
+ * });
1063
+ * ```
1064
+ *
1065
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/post_playlists
1066
+ */
490
1067
  async create(params, options) {
491
1068
  const t = resolveToken(this.getToken, options?.token);
492
1069
  return this.fetch({ path: "/playlists", method: "POST", token: t, body: { playlist: params } });
493
1070
  }
494
- /** PUT /playlists/:id — update a playlist */
1071
+ /**
1072
+ * Update a playlist's metadata or track list.
1073
+ *
1074
+ * @param playlistId - The playlist's numeric ID or URN
1075
+ * @param params - Fields to update
1076
+ * @param options - Optional token override
1077
+ * @returns The updated playlist object
1078
+ * @throws {SoundCloudError} When the API returns an error
1079
+ *
1080
+ * @example
1081
+ * ```ts
1082
+ * const updated = await sc.playlists.update(123456, { title: 'Updated Title' });
1083
+ * ```
1084
+ *
1085
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/put_playlists__playlist_id_
1086
+ */
495
1087
  async update(playlistId, params, options) {
496
1088
  const t = resolveToken(this.getToken, options?.token);
497
1089
  return this.fetch({ path: `/playlists/${playlistId}`, method: "PUT", token: t, body: { playlist: params } });
498
1090
  }
499
- /** DELETE /playlists/:id */
1091
+ /**
1092
+ * Delete a playlist.
1093
+ *
1094
+ * @param playlistId - The playlist's numeric ID or URN
1095
+ * @param options - Optional token override
1096
+ * @throws {SoundCloudError} When the API returns an error
1097
+ *
1098
+ * @example
1099
+ * ```ts
1100
+ * await sc.playlists.delete(123456);
1101
+ * ```
1102
+ *
1103
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/delete_playlists__playlist_id_
1104
+ */
500
1105
  async delete(playlistId, options) {
501
1106
  const t = resolveToken(this.getToken, options?.token);
502
1107
  return this.fetch({ path: `/playlists/${playlistId}`, method: "DELETE", token: t });
@@ -511,17 +1116,65 @@ exports.SoundCloudClient = class _SoundCloudClient {
511
1116
  fetch(opts) {
512
1117
  return scFetch(opts, this.refreshCtx);
513
1118
  }
514
- /** GET /tracks?q= */
1119
+ /**
1120
+ * Search for tracks by query string.
1121
+ *
1122
+ * @param query - Search query text
1123
+ * @param pageNumber - Zero-based page number (10 results per page)
1124
+ * @param options - Optional token override
1125
+ * @returns Paginated list of matching tracks
1126
+ * @throws {SoundCloudError} When the API returns an error
1127
+ *
1128
+ * @example
1129
+ * ```ts
1130
+ * const results = await sc.search.tracks('lofi hip hop');
1131
+ * results.collection.forEach(t => console.log(t.title));
1132
+ * ```
1133
+ *
1134
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/tracks/get_tracks
1135
+ */
515
1136
  async tracks(query, pageNumber, options) {
516
1137
  const t = resolveToken(this.getToken, options?.token);
517
1138
  return this.fetch({ path: `/tracks?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
518
1139
  }
519
- /** GET /users?q= */
1140
+ /**
1141
+ * Search for users by query string.
1142
+ *
1143
+ * @param query - Search query text
1144
+ * @param pageNumber - Zero-based page number (10 results per page)
1145
+ * @param options - Optional token override
1146
+ * @returns Paginated list of matching users
1147
+ * @throws {SoundCloudError} When the API returns an error
1148
+ *
1149
+ * @example
1150
+ * ```ts
1151
+ * const results = await sc.search.users('deadmau5');
1152
+ * results.collection.forEach(u => console.log(u.username));
1153
+ * ```
1154
+ *
1155
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/users/get_users
1156
+ */
520
1157
  async users(query, pageNumber, options) {
521
1158
  const t = resolveToken(this.getToken, options?.token);
522
1159
  return this.fetch({ path: `/users?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
523
1160
  }
524
- /** GET /playlists?q= */
1161
+ /**
1162
+ * Search for playlists by query string.
1163
+ *
1164
+ * @param query - Search query text
1165
+ * @param pageNumber - Zero-based page number (10 results per page)
1166
+ * @param options - Optional token override
1167
+ * @returns Paginated list of matching playlists
1168
+ * @throws {SoundCloudError} When the API returns an error
1169
+ *
1170
+ * @example
1171
+ * ```ts
1172
+ * const results = await sc.search.playlists('chill vibes');
1173
+ * results.collection.forEach(p => console.log(p.title));
1174
+ * ```
1175
+ *
1176
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/playlists/get_playlists
1177
+ */
525
1178
  async playlists(query, pageNumber, options) {
526
1179
  const t = resolveToken(this.getToken, options?.token);
527
1180
  return this.fetch({ path: `/playlists?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
@@ -536,7 +1189,22 @@ exports.SoundCloudClient = class _SoundCloudClient {
536
1189
  fetch(opts) {
537
1190
  return scFetch(opts, this.refreshCtx);
538
1191
  }
539
- /** GET /resolve?url= */
1192
+ /**
1193
+ * Resolve a SoundCloud URL to its API resource URL.
1194
+ *
1195
+ * @param url - A SoundCloud URL (e.g. "https://soundcloud.com/artist/track-name")
1196
+ * @param options - Optional token override
1197
+ * @returns The resolved API resource URL (via 302 redirect)
1198
+ * @throws {SoundCloudError} When the URL cannot be resolved
1199
+ *
1200
+ * @example
1201
+ * ```ts
1202
+ * const apiUrl = await sc.resolve.resolveUrl('https://soundcloud.com/deadmau5/strobe');
1203
+ * console.log(apiUrl); // "https://api.soundcloud.com/tracks/..."
1204
+ * ```
1205
+ *
1206
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/resolve/get_resolve
1207
+ */
540
1208
  async resolveUrl(url, options) {
541
1209
  const t = resolveToken(this.getToken, options?.token);
542
1210
  return this.fetch({ path: `/resolve?url=${encodeURIComponent(url)}`, method: "GET", token: t });
@@ -551,7 +1219,20 @@ exports.SoundCloudClient = class _SoundCloudClient {
551
1219
  fetch(opts) {
552
1220
  return scFetch(opts, this.refreshCtx);
553
1221
  }
554
- /** POST /likes/tracks/:id */
1222
+ /**
1223
+ * Like a track.
1224
+ *
1225
+ * @param trackId - The track's numeric ID or URN
1226
+ * @param options - Optional token override
1227
+ * @returns `true` if the like was successful, `false` on failure
1228
+ *
1229
+ * @example
1230
+ * ```ts
1231
+ * const success = await sc.likes.likeTrack(123456);
1232
+ * ```
1233
+ *
1234
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/likes/post_likes_tracks__track_id_
1235
+ */
555
1236
  async likeTrack(trackId, options) {
556
1237
  const t = resolveToken(this.getToken, options?.token);
557
1238
  try {
@@ -561,7 +1242,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
561
1242
  return false;
562
1243
  }
563
1244
  }
564
- /** DELETE /likes/tracks/:id */
1245
+ /**
1246
+ * Unlike a track.
1247
+ *
1248
+ * @param trackId - The track's numeric ID or URN
1249
+ * @param options - Optional token override
1250
+ * @returns `true` if the unlike was successful, `false` on failure
1251
+ *
1252
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/likes/delete_likes_tracks__track_id_
1253
+ */
565
1254
  async unlikeTrack(trackId, options) {
566
1255
  const t = resolveToken(this.getToken, options?.token);
567
1256
  try {
@@ -571,7 +1260,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
571
1260
  return false;
572
1261
  }
573
1262
  }
574
- /** POST /likes/playlists/:id */
1263
+ /**
1264
+ * Like a playlist.
1265
+ *
1266
+ * @param playlistId - The playlist's numeric ID or URN
1267
+ * @param options - Optional token override
1268
+ * @returns `true` if the like was successful, `false` on failure
1269
+ *
1270
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/likes/post_likes_playlists__playlist_id_
1271
+ */
575
1272
  async likePlaylist(playlistId, options) {
576
1273
  const t = resolveToken(this.getToken, options?.token);
577
1274
  try {
@@ -581,7 +1278,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
581
1278
  return false;
582
1279
  }
583
1280
  }
584
- /** DELETE /likes/playlists/:id */
1281
+ /**
1282
+ * Unlike a playlist.
1283
+ *
1284
+ * @param playlistId - The playlist's numeric ID or URN
1285
+ * @param options - Optional token override
1286
+ * @returns `true` if the unlike was successful, `false` on failure
1287
+ *
1288
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/likes/delete_likes_playlists__playlist_id_
1289
+ */
585
1290
  async unlikePlaylist(playlistId, options) {
586
1291
  const t = resolveToken(this.getToken, options?.token);
587
1292
  try {
@@ -601,7 +1306,20 @@ exports.SoundCloudClient = class _SoundCloudClient {
601
1306
  fetch(opts) {
602
1307
  return scFetch(opts, this.refreshCtx);
603
1308
  }
604
- /** POST /reposts/tracks/:id */
1309
+ /**
1310
+ * Repost a track to your profile.
1311
+ *
1312
+ * @param trackId - The track's numeric ID or URN
1313
+ * @param options - Optional token override
1314
+ * @returns `true` if the repost was successful, `false` on failure
1315
+ *
1316
+ * @example
1317
+ * ```ts
1318
+ * const success = await sc.reposts.repostTrack(123456);
1319
+ * ```
1320
+ *
1321
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/reposts/post_reposts_tracks__track_id_
1322
+ */
605
1323
  async repostTrack(trackId, options) {
606
1324
  const t = resolveToken(this.getToken, options?.token);
607
1325
  try {
@@ -611,7 +1329,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
611
1329
  return false;
612
1330
  }
613
1331
  }
614
- /** DELETE /reposts/tracks/:id */
1332
+ /**
1333
+ * Remove a track repost from your profile.
1334
+ *
1335
+ * @param trackId - The track's numeric ID or URN
1336
+ * @param options - Optional token override
1337
+ * @returns `true` if the unrepost was successful, `false` on failure
1338
+ *
1339
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/reposts/delete_reposts_tracks__track_id_
1340
+ */
615
1341
  async unrepostTrack(trackId, options) {
616
1342
  const t = resolveToken(this.getToken, options?.token);
617
1343
  try {
@@ -621,7 +1347,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
621
1347
  return false;
622
1348
  }
623
1349
  }
624
- /** POST /reposts/playlists/:id */
1350
+ /**
1351
+ * Repost a playlist to your profile.
1352
+ *
1353
+ * @param playlistId - The playlist's numeric ID or URN
1354
+ * @param options - Optional token override
1355
+ * @returns `true` if the repost was successful, `false` on failure
1356
+ *
1357
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/reposts/post_reposts_playlists__playlist_id_
1358
+ */
625
1359
  async repostPlaylist(playlistId, options) {
626
1360
  const t = resolveToken(this.getToken, options?.token);
627
1361
  try {
@@ -631,7 +1365,15 @@ exports.SoundCloudClient = class _SoundCloudClient {
631
1365
  return false;
632
1366
  }
633
1367
  }
634
- /** DELETE /reposts/playlists/:id */
1368
+ /**
1369
+ * Remove a playlist repost from your profile.
1370
+ *
1371
+ * @param playlistId - The playlist's numeric ID or URN
1372
+ * @param options - Optional token override
1373
+ * @returns `true` if the unrepost was successful, `false` on failure
1374
+ *
1375
+ * @see https://developers.soundcloud.com/docs/api/explorer/open-api#/reposts/delete_reposts_playlists__playlist_id_
1376
+ */
635
1377
  async unrepostPlaylist(playlistId, options) {
636
1378
  const t = resolveToken(this.getToken, options?.token);
637
1379
  try {
@@ -935,6 +1677,10 @@ var unrepostPlaylist = async (token, playlistId) => {
935
1677
  // src/utils/widget.ts
936
1678
  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`;
937
1679
 
1680
+ Object.defineProperty(exports, "SoundCloudError", {
1681
+ enumerable: true,
1682
+ get: function () { return chunkDZRZLIAH_js.SoundCloudError; }
1683
+ });
938
1684
  exports.createPlaylist = createPlaylist;
939
1685
  exports.createTrackComment = createTrackComment;
940
1686
  exports.deletePlaylist = deletePlaylist;