soundcloud-api-ts 1.0.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 ADDED
@@ -0,0 +1,915 @@
1
+ 'use strict';
2
+
3
+ // src/client/http.ts
4
+ var BASE_URL = "https://api.soundcloud.com";
5
+ async function scFetch(options, refreshCtx) {
6
+ const execute = async (tokenOverride) => {
7
+ const url = `${BASE_URL}${options.path}`;
8
+ const headers = {
9
+ Accept: "application/json"
10
+ };
11
+ const token = tokenOverride ?? options.token;
12
+ if (token) {
13
+ headers["Authorization"] = `OAuth ${token}`;
14
+ }
15
+ let fetchBody;
16
+ if (options.body) {
17
+ if (options.body instanceof URLSearchParams) {
18
+ fetchBody = options.body;
19
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
20
+ } else if (options.body instanceof FormData) {
21
+ fetchBody = options.body;
22
+ } else {
23
+ headers["Content-Type"] = options.contentType ?? "application/json";
24
+ fetchBody = JSON.stringify(options.body);
25
+ }
26
+ } else if (options.contentType) {
27
+ headers["Content-Type"] = options.contentType;
28
+ }
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;
39
+ }
40
+ }
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();
50
+ };
51
+ try {
52
+ return await execute();
53
+ } catch (err) {
54
+ if (refreshCtx?.onTokenRefresh && err instanceof Error && err.message.includes("401")) {
55
+ const newToken = await refreshCtx.onTokenRefresh();
56
+ refreshCtx.setToken(newToken.access_token, newToken.refresh_token);
57
+ return execute(newToken.access_token);
58
+ }
59
+ throw err;
60
+ }
61
+ }
62
+
63
+ // src/client/SoundCloudClient.ts
64
+ function resolveToken(tokenGetter, explicit) {
65
+ const t = explicit ?? tokenGetter();
66
+ if (!t) throw new Error("No access token available. Call client.setToken() or pass a token explicitly.");
67
+ return t;
68
+ }
69
+ exports.SoundCloudClient = class _SoundCloudClient {
70
+ config;
71
+ _accessToken;
72
+ _refreshToken;
73
+ auth;
74
+ me;
75
+ users;
76
+ tracks;
77
+ playlists;
78
+ search;
79
+ resolve;
80
+ likes;
81
+ reposts;
82
+ constructor(config) {
83
+ this.config = config;
84
+ const getToken = () => this._accessToken;
85
+ const refreshCtx = config.onTokenRefresh ? {
86
+ getToken,
87
+ onTokenRefresh: async () => {
88
+ const result = await config.onTokenRefresh(this);
89
+ return result;
90
+ },
91
+ setToken: (a, r) => this.setToken(a, r)
92
+ } : void 0;
93
+ this.auth = new _SoundCloudClient.Auth(this.config);
94
+ this.me = new _SoundCloudClient.Me(getToken, refreshCtx);
95
+ this.users = new _SoundCloudClient.Users(getToken, refreshCtx);
96
+ this.tracks = new _SoundCloudClient.Tracks(getToken, refreshCtx);
97
+ this.playlists = new _SoundCloudClient.Playlists(getToken, refreshCtx);
98
+ this.search = new _SoundCloudClient.Search(getToken, refreshCtx);
99
+ this.resolve = new _SoundCloudClient.Resolve(getToken, refreshCtx);
100
+ this.likes = new _SoundCloudClient.Likes(getToken, refreshCtx);
101
+ this.reposts = new _SoundCloudClient.Reposts(getToken, refreshCtx);
102
+ }
103
+ /** Store an access token (and optionally refresh token) on this client instance. */
104
+ setToken(accessToken, refreshToken) {
105
+ this._accessToken = accessToken;
106
+ if (refreshToken !== void 0) this._refreshToken = refreshToken;
107
+ }
108
+ /** Clear stored tokens. */
109
+ clearToken() {
110
+ this._accessToken = void 0;
111
+ this._refreshToken = void 0;
112
+ }
113
+ /** Get the currently stored access token, if any. */
114
+ get accessToken() {
115
+ return this._accessToken;
116
+ }
117
+ /** Get the currently stored refresh token, if any. */
118
+ get refreshToken() {
119
+ return this._refreshToken;
120
+ }
121
+ };
122
+ ((SoundCloudClient2) => {
123
+ class Auth {
124
+ constructor(config) {
125
+ this.config = config;
126
+ }
127
+ /** Build the authorization URL to redirect users to SoundCloud's login. */
128
+ getAuthorizationUrl(options) {
129
+ if (!this.config.redirectUri) throw new Error("redirectUri is required for getAuthorizationUrl");
130
+ const params = new URLSearchParams({
131
+ client_id: this.config.clientId,
132
+ redirect_uri: this.config.redirectUri,
133
+ response_type: "code"
134
+ });
135
+ if (options?.state) params.set("state", options.state);
136
+ if (options?.codeChallenge) {
137
+ params.set("code_challenge", options.codeChallenge);
138
+ params.set("code_challenge_method", "S256");
139
+ }
140
+ return `https://api.soundcloud.com/connect?${params}`;
141
+ }
142
+ /** POST /oauth2/token — client_credentials grant */
143
+ async getClientToken() {
144
+ return scFetch({
145
+ path: "/oauth2/token",
146
+ method: "POST",
147
+ body: new URLSearchParams({
148
+ grant_type: "client_credentials",
149
+ client_id: this.config.clientId,
150
+ client_secret: this.config.clientSecret
151
+ })
152
+ });
153
+ }
154
+ /** POST /oauth2/token — authorization_code grant */
155
+ async getUserToken(code, codeVerifier) {
156
+ const params = {
157
+ grant_type: "authorization_code",
158
+ client_id: this.config.clientId,
159
+ client_secret: this.config.clientSecret,
160
+ redirect_uri: this.config.redirectUri,
161
+ code
162
+ };
163
+ if (codeVerifier) params.code_verifier = codeVerifier;
164
+ return scFetch({
165
+ path: "/oauth2/token",
166
+ method: "POST",
167
+ body: new URLSearchParams(params)
168
+ });
169
+ }
170
+ /** POST /oauth2/token — refresh_token grant */
171
+ async refreshUserToken(refreshToken) {
172
+ return scFetch({
173
+ path: "/oauth2/token",
174
+ method: "POST",
175
+ body: new URLSearchParams({
176
+ grant_type: "refresh_token",
177
+ client_id: this.config.clientId,
178
+ client_secret: this.config.clientSecret,
179
+ redirect_uri: this.config.redirectUri,
180
+ refresh_token: refreshToken
181
+ })
182
+ });
183
+ }
184
+ /**
185
+ * POST /sign-out — invalidates session.
186
+ *
187
+ * **Note:** This hits `https://secure.soundcloud.com`, NOT the regular
188
+ * `api.soundcloud.com` host used by all other endpoints.
189
+ */
190
+ async signOut(accessToken) {
191
+ const res = await fetch("https://secure.soundcloud.com/sign-out", {
192
+ method: "POST",
193
+ headers: { "Content-Type": "application/json" },
194
+ body: JSON.stringify({ access_token: accessToken })
195
+ });
196
+ if (!res.ok) throw new Error(`Sign-out failed: ${res.status}`);
197
+ }
198
+ }
199
+ SoundCloudClient2.Auth = Auth;
200
+ class Me {
201
+ constructor(getToken, refreshCtx) {
202
+ this.getToken = getToken;
203
+ this.refreshCtx = refreshCtx;
204
+ }
205
+ fetch(opts) {
206
+ return scFetch(opts, this.refreshCtx);
207
+ }
208
+ /** GET /me */
209
+ async getMe(options) {
210
+ const t = resolveToken(this.getToken, options?.token);
211
+ return this.fetch({ path: "/me", method: "GET", token: t });
212
+ }
213
+ /** GET /me/activities */
214
+ async getActivities(limit, options) {
215
+ const t = resolveToken(this.getToken, options?.token);
216
+ return this.fetch({ path: `/me/activities?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
217
+ }
218
+ /** GET /me/activities/all/own */
219
+ async getActivitiesOwn(limit, options) {
220
+ const t = resolveToken(this.getToken, options?.token);
221
+ return this.fetch({ path: `/me/activities/all/own?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
222
+ }
223
+ /** GET /me/activities/tracks */
224
+ async getActivitiesTracks(limit, options) {
225
+ const t = resolveToken(this.getToken, options?.token);
226
+ return this.fetch({ path: `/me/activities/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
227
+ }
228
+ /** GET /me/likes/tracks */
229
+ async getLikesTracks(limit, options) {
230
+ const t = resolveToken(this.getToken, options?.token);
231
+ return this.fetch({ path: `/me/likes/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
232
+ }
233
+ /** GET /me/likes/playlists */
234
+ async getLikesPlaylists(limit, options) {
235
+ const t = resolveToken(this.getToken, options?.token);
236
+ return this.fetch({ path: `/me/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
237
+ }
238
+ /** GET /me/followings */
239
+ async getFollowings(limit, options) {
240
+ const t = resolveToken(this.getToken, options?.token);
241
+ return this.fetch({ path: `/me/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
242
+ }
243
+ /** GET /me/followings/tracks */
244
+ async getFollowingsTracks(limit, options) {
245
+ const t = resolveToken(this.getToken, options?.token);
246
+ return this.fetch({ path: `/me/followings/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
247
+ }
248
+ /** PUT /me/followings/:user_urn — follow a user */
249
+ async follow(userUrn, options) {
250
+ const t = resolveToken(this.getToken, options?.token);
251
+ return this.fetch({ path: `/me/followings/${userUrn}`, method: "PUT", token: t });
252
+ }
253
+ /** DELETE /me/followings/:user_urn — unfollow a user */
254
+ async unfollow(userUrn, options) {
255
+ const t = resolveToken(this.getToken, options?.token);
256
+ return this.fetch({ path: `/me/followings/${userUrn}`, method: "DELETE", token: t });
257
+ }
258
+ /** GET /me/followers */
259
+ async getFollowers(limit, options) {
260
+ const t = resolveToken(this.getToken, options?.token);
261
+ return this.fetch({ path: `/me/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
262
+ }
263
+ /** GET /me/playlists */
264
+ async getPlaylists(limit, options) {
265
+ const t = resolveToken(this.getToken, options?.token);
266
+ return this.fetch({ path: `/me/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
267
+ }
268
+ /** GET /me/tracks */
269
+ async getTracks(limit, options) {
270
+ const t = resolveToken(this.getToken, options?.token);
271
+ return this.fetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
272
+ }
273
+ }
274
+ SoundCloudClient2.Me = Me;
275
+ class Users {
276
+ constructor(getToken, refreshCtx) {
277
+ this.getToken = getToken;
278
+ this.refreshCtx = refreshCtx;
279
+ }
280
+ fetch(opts) {
281
+ return scFetch(opts, this.refreshCtx);
282
+ }
283
+ /** GET /users/:id */
284
+ async getUser(userId, options) {
285
+ const t = resolveToken(this.getToken, options?.token);
286
+ return this.fetch({ path: `/users/${userId}`, method: "GET", token: t });
287
+ }
288
+ /** GET /users/:id/followers */
289
+ async getFollowers(userId, limit, options) {
290
+ const t = resolveToken(this.getToken, options?.token);
291
+ return this.fetch({ path: `/users/${userId}/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
292
+ }
293
+ /** GET /users/:id/followings */
294
+ async getFollowings(userId, limit, options) {
295
+ const t = resolveToken(this.getToken, options?.token);
296
+ return this.fetch({ path: `/users/${userId}/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
297
+ }
298
+ /** GET /users/:id/tracks */
299
+ async getTracks(userId, limit, options) {
300
+ const t = resolveToken(this.getToken, options?.token);
301
+ return this.fetch({ path: `/users/${userId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
302
+ }
303
+ /** GET /users/:id/playlists */
304
+ async getPlaylists(userId, limit, options) {
305
+ const t = resolveToken(this.getToken, options?.token);
306
+ return this.fetch({ path: `/users/${userId}/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true&show_tracks=false`, method: "GET", token: t });
307
+ }
308
+ /** GET /users/:id/likes/tracks */
309
+ async getLikesTracks(userId, limit, cursor, options) {
310
+ const t = resolveToken(this.getToken, options?.token);
311
+ return this.fetch({ path: `/users/${userId}/likes/tracks?${limit ? `limit=${limit}&` : ""}${cursor ? `cursor=${cursor}&` : ""}linked_partitioning=true`, method: "GET", token: t });
312
+ }
313
+ /** GET /users/:id/likes/playlists */
314
+ async getLikesPlaylists(userId, limit, options) {
315
+ const t = resolveToken(this.getToken, options?.token);
316
+ return this.fetch({ path: `/users/${userId}/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
317
+ }
318
+ /** GET /users/:id/web-profiles */
319
+ async getWebProfiles(userId, options) {
320
+ const t = resolveToken(this.getToken, options?.token);
321
+ return this.fetch({ path: `/users/${userId}/web-profiles`, method: "GET", token: t });
322
+ }
323
+ }
324
+ SoundCloudClient2.Users = Users;
325
+ class Tracks {
326
+ constructor(getToken, refreshCtx) {
327
+ this.getToken = getToken;
328
+ this.refreshCtx = refreshCtx;
329
+ }
330
+ fetch(opts) {
331
+ return scFetch(opts, this.refreshCtx);
332
+ }
333
+ /** GET /tracks/:id */
334
+ async getTrack(trackId, options) {
335
+ const t = resolveToken(this.getToken, options?.token);
336
+ return this.fetch({ path: `/tracks/${trackId}`, method: "GET", token: t });
337
+ }
338
+ /** GET /tracks/:id/streams */
339
+ async getStreams(trackId, options) {
340
+ const t = resolveToken(this.getToken, options?.token);
341
+ return this.fetch({ path: `/tracks/${trackId}/streams`, method: "GET", token: t });
342
+ }
343
+ /** GET /tracks/:id/comments */
344
+ async getComments(trackId, limit, options) {
345
+ const t = resolveToken(this.getToken, options?.token);
346
+ return this.fetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token: t });
347
+ }
348
+ /** POST /tracks/:id/comments */
349
+ async createComment(trackId, body, timestamp, options) {
350
+ const t = resolveToken(this.getToken, options?.token);
351
+ return this.fetch({
352
+ path: `/tracks/${trackId}/comments`,
353
+ method: "POST",
354
+ token: t,
355
+ body: { comment: { body, ...timestamp !== void 0 ? { timestamp } : {} } }
356
+ });
357
+ }
358
+ /** GET /tracks/:id/favoriters */
359
+ async getLikes(trackId, limit, options) {
360
+ const t = resolveToken(this.getToken, options?.token);
361
+ return this.fetch({ path: `/tracks/${trackId}/favoriters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
362
+ }
363
+ /** GET /tracks/:id/reposters */
364
+ async getReposts(trackId, limit, options) {
365
+ const t = resolveToken(this.getToken, options?.token);
366
+ return this.fetch({ path: `/tracks/${trackId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
367
+ }
368
+ /** GET /tracks/:id/related */
369
+ async getRelated(trackId, limit, options) {
370
+ const t = resolveToken(this.getToken, options?.token);
371
+ return this.fetch({ path: `/tracks/${trackId}/related${limit ? `?limit=${limit}` : ""}`, method: "GET", token: t });
372
+ }
373
+ /** PUT /tracks/:id — update track metadata */
374
+ async update(trackId, params, options) {
375
+ const t = resolveToken(this.getToken, options?.token);
376
+ return this.fetch({ path: `/tracks/${trackId}`, method: "PUT", token: t, body: { track: params } });
377
+ }
378
+ /** DELETE /tracks/:id */
379
+ async delete(trackId, options) {
380
+ const t = resolveToken(this.getToken, options?.token);
381
+ return this.fetch({ path: `/tracks/${trackId}`, method: "DELETE", token: t });
382
+ }
383
+ }
384
+ SoundCloudClient2.Tracks = Tracks;
385
+ class Playlists {
386
+ constructor(getToken, refreshCtx) {
387
+ this.getToken = getToken;
388
+ this.refreshCtx = refreshCtx;
389
+ }
390
+ fetch(opts) {
391
+ return scFetch(opts, this.refreshCtx);
392
+ }
393
+ /** GET /playlists/:id */
394
+ async getPlaylist(playlistId, options) {
395
+ const t = resolveToken(this.getToken, options?.token);
396
+ return this.fetch({ path: `/playlists/${playlistId}`, method: "GET", token: t });
397
+ }
398
+ /** GET /playlists/:id/tracks */
399
+ async getTracks(playlistId, limit, offset, options) {
400
+ const t = resolveToken(this.getToken, options?.token);
401
+ return this.fetch({ path: `/playlists/${playlistId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true${offset ? `&offset=${offset}` : ""}`, method: "GET", token: t });
402
+ }
403
+ /** GET /playlists/:id/reposters */
404
+ async getReposts(playlistId, limit, options) {
405
+ const t = resolveToken(this.getToken, options?.token);
406
+ return this.fetch({ path: `/playlists/${playlistId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
407
+ }
408
+ /** POST /playlists — create a playlist */
409
+ async create(params, options) {
410
+ const t = resolveToken(this.getToken, options?.token);
411
+ return this.fetch({ path: "/playlists", method: "POST", token: t, body: { playlist: params } });
412
+ }
413
+ /** PUT /playlists/:id — update a playlist */
414
+ async update(playlistId, params, options) {
415
+ const t = resolveToken(this.getToken, options?.token);
416
+ return this.fetch({ path: `/playlists/${playlistId}`, method: "PUT", token: t, body: { playlist: params } });
417
+ }
418
+ /** DELETE /playlists/:id */
419
+ async delete(playlistId, options) {
420
+ const t = resolveToken(this.getToken, options?.token);
421
+ return this.fetch({ path: `/playlists/${playlistId}`, method: "DELETE", token: t });
422
+ }
423
+ }
424
+ SoundCloudClient2.Playlists = Playlists;
425
+ class Search {
426
+ constructor(getToken, refreshCtx) {
427
+ this.getToken = getToken;
428
+ this.refreshCtx = refreshCtx;
429
+ }
430
+ fetch(opts) {
431
+ return scFetch(opts, this.refreshCtx);
432
+ }
433
+ /** GET /tracks?q= */
434
+ async tracks(query, pageNumber, options) {
435
+ const t = resolveToken(this.getToken, options?.token);
436
+ return this.fetch({ path: `/tracks?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
437
+ }
438
+ /** GET /users?q= */
439
+ async users(query, pageNumber, options) {
440
+ const t = resolveToken(this.getToken, options?.token);
441
+ return this.fetch({ path: `/users?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
442
+ }
443
+ /** GET /playlists?q= */
444
+ async playlists(query, pageNumber, options) {
445
+ const t = resolveToken(this.getToken, options?.token);
446
+ return this.fetch({ path: `/playlists?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
447
+ }
448
+ }
449
+ SoundCloudClient2.Search = Search;
450
+ class Resolve {
451
+ constructor(getToken, refreshCtx) {
452
+ this.getToken = getToken;
453
+ this.refreshCtx = refreshCtx;
454
+ }
455
+ fetch(opts) {
456
+ return scFetch(opts, this.refreshCtx);
457
+ }
458
+ /** GET /resolve?url= */
459
+ async resolveUrl(url, options) {
460
+ const t = resolveToken(this.getToken, options?.token);
461
+ return this.fetch({ path: `/resolve?url=${encodeURIComponent(url)}`, method: "GET", token: t });
462
+ }
463
+ }
464
+ SoundCloudClient2.Resolve = Resolve;
465
+ class Likes {
466
+ constructor(getToken, refreshCtx) {
467
+ this.getToken = getToken;
468
+ this.refreshCtx = refreshCtx;
469
+ }
470
+ fetch(opts) {
471
+ return scFetch(opts, this.refreshCtx);
472
+ }
473
+ /** POST /likes/tracks/:id */
474
+ async likeTrack(trackId, options) {
475
+ const t = resolveToken(this.getToken, options?.token);
476
+ try {
477
+ await this.fetch({ path: `/likes/tracks/${trackId}`, method: "POST", token: t });
478
+ return true;
479
+ } catch {
480
+ return false;
481
+ }
482
+ }
483
+ /** DELETE /likes/tracks/:id */
484
+ async unlikeTrack(trackId, options) {
485
+ const t = resolveToken(this.getToken, options?.token);
486
+ try {
487
+ await this.fetch({ path: `/likes/tracks/${trackId}`, method: "DELETE", token: t });
488
+ return true;
489
+ } catch {
490
+ return false;
491
+ }
492
+ }
493
+ /** POST /likes/playlists/:id */
494
+ async likePlaylist(playlistId, options) {
495
+ const t = resolveToken(this.getToken, options?.token);
496
+ try {
497
+ await this.fetch({ path: `/likes/playlists/${playlistId}`, method: "POST", token: t });
498
+ return true;
499
+ } catch {
500
+ return false;
501
+ }
502
+ }
503
+ /** DELETE /likes/playlists/:id */
504
+ async unlikePlaylist(playlistId, options) {
505
+ const t = resolveToken(this.getToken, options?.token);
506
+ try {
507
+ await this.fetch({ path: `/likes/playlists/${playlistId}`, method: "DELETE", token: t });
508
+ return true;
509
+ } catch {
510
+ return false;
511
+ }
512
+ }
513
+ }
514
+ SoundCloudClient2.Likes = Likes;
515
+ class Reposts {
516
+ constructor(getToken, refreshCtx) {
517
+ this.getToken = getToken;
518
+ this.refreshCtx = refreshCtx;
519
+ }
520
+ fetch(opts) {
521
+ return scFetch(opts, this.refreshCtx);
522
+ }
523
+ /** POST /reposts/tracks/:id */
524
+ async repostTrack(trackId, options) {
525
+ const t = resolveToken(this.getToken, options?.token);
526
+ try {
527
+ await this.fetch({ path: `/reposts/tracks/${trackId}`, method: "POST", token: t });
528
+ return true;
529
+ } catch {
530
+ return false;
531
+ }
532
+ }
533
+ /** DELETE /reposts/tracks/:id */
534
+ async unrepostTrack(trackId, options) {
535
+ const t = resolveToken(this.getToken, options?.token);
536
+ try {
537
+ await this.fetch({ path: `/reposts/tracks/${trackId}`, method: "DELETE", token: t });
538
+ return true;
539
+ } catch {
540
+ return false;
541
+ }
542
+ }
543
+ /** POST /reposts/playlists/:id */
544
+ async repostPlaylist(playlistId, options) {
545
+ const t = resolveToken(this.getToken, options?.token);
546
+ try {
547
+ await this.fetch({ path: `/reposts/playlists/${playlistId}`, method: "POST", token: t });
548
+ return true;
549
+ } catch {
550
+ return false;
551
+ }
552
+ }
553
+ /** DELETE /reposts/playlists/:id */
554
+ async unrepostPlaylist(playlistId, options) {
555
+ const t = resolveToken(this.getToken, options?.token);
556
+ try {
557
+ await this.fetch({ path: `/reposts/playlists/${playlistId}`, method: "DELETE", token: t });
558
+ return true;
559
+ } catch {
560
+ return false;
561
+ }
562
+ }
563
+ }
564
+ SoundCloudClient2.Reposts = Reposts;
565
+ })(exports.SoundCloudClient || (exports.SoundCloudClient = {}));
566
+
567
+ // src/auth/getClientToken.ts
568
+ var getClientToken = (clientId, clientSecret) => {
569
+ return scFetch({
570
+ path: "/oauth2/token",
571
+ method: "POST",
572
+ body: new URLSearchParams({
573
+ grant_type: "client_credentials",
574
+ client_id: clientId,
575
+ client_secret: clientSecret
576
+ })
577
+ });
578
+ };
579
+
580
+ // src/auth/getUserToken.ts
581
+ var getUserToken = (clientId, clientSecret, redirectUri, code, codeVerifier) => {
582
+ const params = {
583
+ grant_type: "authorization_code",
584
+ client_id: clientId,
585
+ client_secret: clientSecret,
586
+ redirect_uri: redirectUri,
587
+ code
588
+ };
589
+ if (codeVerifier) params.code_verifier = codeVerifier;
590
+ return scFetch({
591
+ path: "/oauth2/token",
592
+ method: "POST",
593
+ body: new URLSearchParams(params)
594
+ });
595
+ };
596
+
597
+ // src/auth/refreshUserToken.ts
598
+ var refreshUserToken = (clientId, clientSecret, redirectUri, refreshToken) => {
599
+ return scFetch({
600
+ path: "/oauth2/token",
601
+ method: "POST",
602
+ body: new URLSearchParams({
603
+ grant_type: "refresh_token",
604
+ client_id: clientId,
605
+ client_secret: clientSecret,
606
+ redirect_uri: redirectUri,
607
+ refresh_token: refreshToken
608
+ })
609
+ });
610
+ };
611
+
612
+ // src/auth/signOut.ts
613
+ var signOut = async (accessToken) => {
614
+ const res = await fetch("https://secure.soundcloud.com/sign-out", {
615
+ method: "POST",
616
+ headers: { "Content-Type": "application/json" },
617
+ body: JSON.stringify({ access_token: accessToken })
618
+ });
619
+ if (!res.ok) throw new Error(`Sign-out failed: ${res.status}`);
620
+ };
621
+
622
+ // src/auth/getAuthorizationUrl.ts
623
+ function getAuthorizationUrl(clientId, redirectUri, options) {
624
+ const params = new URLSearchParams({
625
+ client_id: clientId,
626
+ redirect_uri: redirectUri,
627
+ response_type: "code"
628
+ });
629
+ if (options?.state) params.set("state", options.state);
630
+ if (options?.codeChallenge) {
631
+ params.set("code_challenge", options.codeChallenge);
632
+ params.set("code_challenge_method", "S256");
633
+ }
634
+ return `https://api.soundcloud.com/connect?${params}`;
635
+ }
636
+
637
+ // src/auth/pkce.ts
638
+ function generateCodeVerifier() {
639
+ const bytes = new Uint8Array(32);
640
+ globalThis.crypto.getRandomValues(bytes);
641
+ return base64url(bytes);
642
+ }
643
+ async function generateCodeChallenge(verifier) {
644
+ const data = new TextEncoder().encode(verifier);
645
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", data);
646
+ return base64url(new Uint8Array(digest));
647
+ }
648
+ function base64url(bytes) {
649
+ let binary = "";
650
+ for (const b of bytes) binary += String.fromCharCode(b);
651
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
652
+ }
653
+
654
+ // src/users/getMe.ts
655
+ var getMe = (token) => scFetch({ path: "/me", method: "GET", token });
656
+
657
+ // src/users/getUser.ts
658
+ var getUser = (token, userId) => scFetch({ path: `/users/${userId}`, method: "GET", token });
659
+
660
+ // src/users/getFollowers.ts
661
+ var getFollowers = (token, userId, limit) => scFetch({ path: `/users/${userId}/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
662
+
663
+ // src/users/getFollowings.ts
664
+ var getFollowings = (token, userId, limit) => scFetch({ path: `/users/${userId}/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
665
+
666
+ // src/users/getTracks.ts
667
+ var getUserTracks = (token, userId, limit) => scFetch({ path: `/users/${userId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
668
+
669
+ // src/users/getPlaylists.ts
670
+ var getUserPlaylists = (token, userId, limit) => scFetch({ path: `/users/${userId}/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true&show_tracks=false`, method: "GET", token });
671
+
672
+ // src/users/getLikesTracks.ts
673
+ var getUserLikesTracks = (token, userId, limit, cursor) => scFetch({ path: `/users/${userId}/likes/tracks?${limit ? `limit=${limit}&` : ""}${cursor ? `cursor=${cursor}&` : ""}linked_partitioning=true`, method: "GET", token });
674
+
675
+ // src/users/getLikesPlaylists.ts
676
+ var getUserLikesPlaylists = (token, userId, limit) => scFetch({ path: `/users/${userId}/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
677
+
678
+ // src/users/getWebProfiles.ts
679
+ var getUserWebProfiles = (token, userId) => scFetch({ path: `/users/${userId}/web-profiles`, method: "GET", token });
680
+
681
+ // src/tracks/getTrack.ts
682
+ var getTrack = (token, trackId) => scFetch({ path: `/tracks/${trackId}`, method: "GET", token });
683
+
684
+ // src/tracks/getComments.ts
685
+ var getTrackComments = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token });
686
+
687
+ // src/tracks/createComment.ts
688
+ var createTrackComment = (token, trackId, body, timestamp) => scFetch({
689
+ path: `/tracks/${trackId}/comments`,
690
+ method: "POST",
691
+ token,
692
+ body: { comment: { body, ...timestamp !== void 0 ? { timestamp } : {} } }
693
+ });
694
+
695
+ // src/tracks/getLikes.ts
696
+ var getTrackLikes = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/favoriters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
697
+
698
+ // src/tracks/getReposts.ts
699
+ var getTrackReposts = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
700
+
701
+ // src/tracks/getRelated.ts
702
+ var getRelatedTracks = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/related${limit ? `?limit=${limit}` : ""}`, method: "GET", token });
703
+
704
+ // src/tracks/getStreams.ts
705
+ var getTrackStreams = (token, trackId) => scFetch({ path: `/tracks/${trackId}/streams`, method: "GET", token });
706
+
707
+ // src/tracks/likeTrack.ts
708
+ var likeTrack = async (token, trackId) => {
709
+ try {
710
+ await scFetch({ path: `/likes/tracks/${trackId}`, method: "POST", token });
711
+ return true;
712
+ } catch {
713
+ return false;
714
+ }
715
+ };
716
+
717
+ // src/tracks/unlikeTrack.ts
718
+ var unlikeTrack = async (token, trackId) => {
719
+ try {
720
+ await scFetch({ path: `/likes/tracks/${trackId}`, method: "DELETE", token });
721
+ return true;
722
+ } catch {
723
+ return false;
724
+ }
725
+ };
726
+
727
+ // src/tracks/updateTrack.ts
728
+ var updateTrack = (token, trackId, params) => scFetch({
729
+ path: `/tracks/${trackId}`,
730
+ method: "PUT",
731
+ token,
732
+ body: { track: params }
733
+ });
734
+
735
+ // src/tracks/deleteTrack.ts
736
+ var deleteTrack = (token, trackId) => scFetch({ path: `/tracks/${trackId}`, method: "DELETE", token });
737
+
738
+ // src/playlists/getPlaylist.ts
739
+ var getPlaylist = (token, playlistId) => scFetch({ path: `/playlists/${playlistId}`, method: "GET", token });
740
+
741
+ // src/playlists/getTracks.ts
742
+ var getPlaylistTracks = (token, playlistId, limit, offset) => scFetch({ path: `/playlists/${playlistId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true${offset ? `&offset=${offset}` : ""}`, method: "GET", token });
743
+
744
+ // src/playlists/getReposts.ts
745
+ var getPlaylistReposts = (token, playlistId, limit) => scFetch({ path: `/playlists/${playlistId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
746
+
747
+ // src/playlists/createPlaylist.ts
748
+ var createPlaylist = (token, params) => scFetch({
749
+ path: "/playlists",
750
+ method: "POST",
751
+ token,
752
+ body: { playlist: params }
753
+ });
754
+
755
+ // src/playlists/updatePlaylist.ts
756
+ var updatePlaylist = (token, playlistId, params) => scFetch({
757
+ path: `/playlists/${playlistId}`,
758
+ method: "PUT",
759
+ token,
760
+ body: { playlist: params }
761
+ });
762
+
763
+ // src/playlists/deletePlaylist.ts
764
+ var deletePlaylist = (token, playlistId) => scFetch({ path: `/playlists/${playlistId}`, method: "DELETE", token });
765
+
766
+ // src/search/searchTracks.ts
767
+ var searchTracks = (token, query, pageNumber) => scFetch({ path: `/tracks?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token });
768
+
769
+ // src/search/searchUsers.ts
770
+ var searchUsers = (token, query, pageNumber) => scFetch({ path: `/users?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token });
771
+
772
+ // src/search/searchPlaylists.ts
773
+ var searchPlaylists = (token, query, pageNumber) => scFetch({ path: `/playlists?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token });
774
+
775
+ // src/resolve/resolveUrl.ts
776
+ var resolveUrl = (token, url) => scFetch({ path: `/resolve?url=${encodeURIComponent(url)}`, method: "GET", token });
777
+
778
+ // src/me/activities.ts
779
+ var getMeActivities = (token, limit) => scFetch({ path: `/me/activities?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
780
+ var getMeActivitiesOwn = (token, limit) => scFetch({ path: `/me/activities/all/own?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
781
+ var getMeActivitiesTracks = (token, limit) => scFetch({ path: `/me/activities/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
782
+
783
+ // src/me/likes.ts
784
+ var getMeLikesTracks = (token, limit) => scFetch({ path: `/me/likes/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
785
+ var getMeLikesPlaylists = (token, limit) => scFetch({ path: `/me/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
786
+
787
+ // src/me/followings.ts
788
+ var getMeFollowings = (token, limit) => scFetch({ path: `/me/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
789
+ var getMeFollowingsTracks = (token, limit) => scFetch({ path: `/me/followings/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
790
+ var followUser = (token, userUrn) => scFetch({ path: `/me/followings/${userUrn}`, method: "PUT", token });
791
+ var unfollowUser = (token, userUrn) => scFetch({ path: `/me/followings/${userUrn}`, method: "DELETE", token });
792
+
793
+ // src/me/followers.ts
794
+ var getMeFollowers = (token, limit) => scFetch({ path: `/me/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
795
+
796
+ // src/me/playlists.ts
797
+ var getMePlaylists = (token, limit) => scFetch({ path: `/me/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
798
+
799
+ // src/me/tracks.ts
800
+ var getMeTracks = (token, limit) => scFetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
801
+
802
+ // src/likes/index.ts
803
+ var likePlaylist = async (token, playlistId) => {
804
+ try {
805
+ await scFetch({ path: `/likes/playlists/${playlistId}`, method: "POST", token });
806
+ return true;
807
+ } catch {
808
+ return false;
809
+ }
810
+ };
811
+ var unlikePlaylist = async (token, playlistId) => {
812
+ try {
813
+ await scFetch({ path: `/likes/playlists/${playlistId}`, method: "DELETE", token });
814
+ return true;
815
+ } catch {
816
+ return false;
817
+ }
818
+ };
819
+
820
+ // src/reposts/index.ts
821
+ var repostTrack = async (token, trackId) => {
822
+ try {
823
+ await scFetch({ path: `/reposts/tracks/${trackId}`, method: "POST", token });
824
+ return true;
825
+ } catch {
826
+ return false;
827
+ }
828
+ };
829
+ var unrepostTrack = async (token, trackId) => {
830
+ try {
831
+ await scFetch({ path: `/reposts/tracks/${trackId}`, method: "DELETE", token });
832
+ return true;
833
+ } catch {
834
+ return false;
835
+ }
836
+ };
837
+ var repostPlaylist = async (token, playlistId) => {
838
+ try {
839
+ await scFetch({ path: `/reposts/playlists/${playlistId}`, method: "POST", token });
840
+ return true;
841
+ } catch {
842
+ return false;
843
+ }
844
+ };
845
+ var unrepostPlaylist = async (token, playlistId) => {
846
+ try {
847
+ await scFetch({ path: `/reposts/playlists/${playlistId}`, method: "DELETE", token });
848
+ return true;
849
+ } catch {
850
+ return false;
851
+ }
852
+ };
853
+
854
+ // src/utils/widget.ts
855
+ 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`;
856
+
857
+ exports.createPlaylist = createPlaylist;
858
+ exports.createTrackComment = createTrackComment;
859
+ exports.deletePlaylist = deletePlaylist;
860
+ exports.deleteTrack = deleteTrack;
861
+ exports.followUser = followUser;
862
+ exports.generateCodeChallenge = generateCodeChallenge;
863
+ exports.generateCodeVerifier = generateCodeVerifier;
864
+ exports.getAuthorizationUrl = getAuthorizationUrl;
865
+ exports.getClientToken = getClientToken;
866
+ exports.getFollowers = getFollowers;
867
+ exports.getFollowings = getFollowings;
868
+ exports.getMe = getMe;
869
+ exports.getMeActivities = getMeActivities;
870
+ exports.getMeActivitiesOwn = getMeActivitiesOwn;
871
+ exports.getMeActivitiesTracks = getMeActivitiesTracks;
872
+ exports.getMeFollowers = getMeFollowers;
873
+ exports.getMeFollowings = getMeFollowings;
874
+ exports.getMeFollowingsTracks = getMeFollowingsTracks;
875
+ exports.getMeLikesPlaylists = getMeLikesPlaylists;
876
+ exports.getMeLikesTracks = getMeLikesTracks;
877
+ exports.getMePlaylists = getMePlaylists;
878
+ exports.getMeTracks = getMeTracks;
879
+ exports.getPlaylist = getPlaylist;
880
+ exports.getPlaylistReposts = getPlaylistReposts;
881
+ exports.getPlaylistTracks = getPlaylistTracks;
882
+ exports.getRelatedTracks = getRelatedTracks;
883
+ exports.getSoundCloudWidgetUrl = getSoundCloudWidgetUrl;
884
+ exports.getTrack = getTrack;
885
+ exports.getTrackComments = getTrackComments;
886
+ exports.getTrackLikes = getTrackLikes;
887
+ exports.getTrackReposts = getTrackReposts;
888
+ exports.getTrackStreams = getTrackStreams;
889
+ exports.getUser = getUser;
890
+ exports.getUserLikesPlaylists = getUserLikesPlaylists;
891
+ exports.getUserLikesTracks = getUserLikesTracks;
892
+ exports.getUserPlaylists = getUserPlaylists;
893
+ exports.getUserToken = getUserToken;
894
+ exports.getUserTracks = getUserTracks;
895
+ exports.getUserWebProfiles = getUserWebProfiles;
896
+ exports.likePlaylist = likePlaylist;
897
+ exports.likeTrack = likeTrack;
898
+ exports.refreshUserToken = refreshUserToken;
899
+ exports.repostPlaylist = repostPlaylist;
900
+ exports.repostTrack = repostTrack;
901
+ exports.resolveUrl = resolveUrl;
902
+ exports.scFetch = scFetch;
903
+ exports.searchPlaylists = searchPlaylists;
904
+ exports.searchTracks = searchTracks;
905
+ exports.searchUsers = searchUsers;
906
+ exports.signOut = signOut;
907
+ exports.unfollowUser = unfollowUser;
908
+ exports.unlikePlaylist = unlikePlaylist;
909
+ exports.unlikeTrack = unlikeTrack;
910
+ exports.unrepostPlaylist = unrepostPlaylist;
911
+ exports.unrepostTrack = unrepostTrack;
912
+ exports.updatePlaylist = updatePlaylist;
913
+ exports.updateTrack = updateTrack;
914
+ //# sourceMappingURL=index.js.map
915
+ //# sourceMappingURL=index.js.map