soundcloud-api-ts 1.5.0 → 1.6.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/chunk-LNMDNQSC.js +1742 -0
- package/dist/chunk-LNMDNQSC.js.map +1 -0
- package/dist/chunk-TZWEL7VA.mjs +1680 -0
- package/dist/chunk-TZWEL7VA.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +633 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +607 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.js +245 -1730
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1678
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,1742 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkNBKG62HR_js = require('./chunk-NBKG62HR.js');
|
|
4
|
+
|
|
5
|
+
// src/client/http.ts
|
|
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
|
+
}
|
|
32
|
+
async function scFetch(options, refreshCtx) {
|
|
33
|
+
const retryConfig = refreshCtx?.retry ?? DEFAULT_RETRY;
|
|
34
|
+
const execute = async (tokenOverride) => {
|
|
35
|
+
const url = `${BASE_URL}${options.path}`;
|
|
36
|
+
const headers = {
|
|
37
|
+
Accept: "application/json"
|
|
38
|
+
};
|
|
39
|
+
const token = tokenOverride ?? options.token;
|
|
40
|
+
if (token) {
|
|
41
|
+
headers["Authorization"] = `OAuth ${token}`;
|
|
42
|
+
}
|
|
43
|
+
let fetchBody;
|
|
44
|
+
if (options.body) {
|
|
45
|
+
if (options.body instanceof URLSearchParams) {
|
|
46
|
+
fetchBody = options.body;
|
|
47
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
48
|
+
} else if (options.body instanceof FormData) {
|
|
49
|
+
fetchBody = options.body;
|
|
50
|
+
} else {
|
|
51
|
+
headers["Content-Type"] = options.contentType ?? "application/json";
|
|
52
|
+
fetchBody = JSON.stringify(options.body);
|
|
53
|
+
}
|
|
54
|
+
} else if (options.contentType) {
|
|
55
|
+
headers["Content-Type"] = options.contentType;
|
|
56
|
+
}
|
|
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 chunkNBKG62HR_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);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const body = await parseErrorBody(lastResponse);
|
|
89
|
+
throw new chunkNBKG62HR_js.SoundCloudError(lastResponse.status, lastResponse.statusText, body);
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
return await execute();
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (refreshCtx?.onTokenRefresh && err instanceof chunkNBKG62HR_js.SoundCloudError && err.status === 401) {
|
|
95
|
+
const newToken = await refreshCtx.onTokenRefresh();
|
|
96
|
+
refreshCtx.setToken(newToken.access_token, newToken.refresh_token);
|
|
97
|
+
return execute(newToken.access_token);
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function scFetchUrl(url, token, retryConfig) {
|
|
103
|
+
const config = retryConfig ?? DEFAULT_RETRY;
|
|
104
|
+
const headers = { Accept: "application/json" };
|
|
105
|
+
if (token) headers["Authorization"] = `OAuth ${token}`;
|
|
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 chunkNBKG62HR_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
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const body = await parseErrorBody(lastResponse);
|
|
133
|
+
throw new chunkNBKG62HR_js.SoundCloudError(lastResponse.status, lastResponse.statusText, body);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/client/paginate.ts
|
|
137
|
+
async function* paginate(firstPage, fetchNext) {
|
|
138
|
+
let page = await firstPage();
|
|
139
|
+
yield page.collection;
|
|
140
|
+
while (page.next_href) {
|
|
141
|
+
page = await fetchNext(page.next_href);
|
|
142
|
+
yield page.collection;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function* paginateItems(firstPage, fetchNext) {
|
|
146
|
+
for await (const page of paginate(firstPage, fetchNext)) {
|
|
147
|
+
for (const item of page) {
|
|
148
|
+
yield item;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function fetchAll(firstPage, fetchNext, options) {
|
|
153
|
+
const result = [];
|
|
154
|
+
const max = options?.maxItems ?? Infinity;
|
|
155
|
+
for await (const page of paginate(firstPage, fetchNext)) {
|
|
156
|
+
for (const item of page) {
|
|
157
|
+
result.push(item);
|
|
158
|
+
if (result.length >= max) return result;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/client/SoundCloudClient.ts
|
|
165
|
+
function resolveToken(tokenGetter, explicit) {
|
|
166
|
+
const t = explicit ?? tokenGetter();
|
|
167
|
+
if (!t) throw new Error("No access token available. Call client.setToken() or pass a token explicitly.");
|
|
168
|
+
return t;
|
|
169
|
+
}
|
|
170
|
+
exports.SoundCloudClient = class _SoundCloudClient {
|
|
171
|
+
config;
|
|
172
|
+
_accessToken;
|
|
173
|
+
_refreshToken;
|
|
174
|
+
/** Authentication methods (OAuth token grants, sign out) */
|
|
175
|
+
auth;
|
|
176
|
+
/** Authenticated user endpoints (/me) */
|
|
177
|
+
me;
|
|
178
|
+
/** User profile endpoints (/users) */
|
|
179
|
+
users;
|
|
180
|
+
/** Track endpoints (/tracks) */
|
|
181
|
+
tracks;
|
|
182
|
+
/** Playlist endpoints (/playlists) */
|
|
183
|
+
playlists;
|
|
184
|
+
/** Search endpoints */
|
|
185
|
+
search;
|
|
186
|
+
/** URL resolution endpoint (/resolve) */
|
|
187
|
+
resolve;
|
|
188
|
+
/** Like/unlike actions (/likes) */
|
|
189
|
+
likes;
|
|
190
|
+
/** Repost/unrepost actions (/reposts) */
|
|
191
|
+
reposts;
|
|
192
|
+
/**
|
|
193
|
+
* Creates a new SoundCloudClient instance.
|
|
194
|
+
*
|
|
195
|
+
* @param config - Client configuration including OAuth credentials and optional settings
|
|
196
|
+
*/
|
|
197
|
+
constructor(config) {
|
|
198
|
+
this.config = config;
|
|
199
|
+
const getToken = () => this._accessToken;
|
|
200
|
+
const retryConfig = {
|
|
201
|
+
maxRetries: config.maxRetries ?? 3,
|
|
202
|
+
retryBaseDelay: config.retryBaseDelay ?? 1e3,
|
|
203
|
+
onDebug: config.onDebug
|
|
204
|
+
};
|
|
205
|
+
const refreshCtx = config.onTokenRefresh ? {
|
|
206
|
+
getToken,
|
|
207
|
+
onTokenRefresh: async () => {
|
|
208
|
+
const result = await config.onTokenRefresh(this);
|
|
209
|
+
return result;
|
|
210
|
+
},
|
|
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
|
+
};
|
|
218
|
+
this.auth = new _SoundCloudClient.Auth(this.config);
|
|
219
|
+
this.me = new _SoundCloudClient.Me(getToken, refreshCtx);
|
|
220
|
+
this.users = new _SoundCloudClient.Users(getToken, refreshCtx);
|
|
221
|
+
this.tracks = new _SoundCloudClient.Tracks(getToken, refreshCtx);
|
|
222
|
+
this.playlists = new _SoundCloudClient.Playlists(getToken, refreshCtx);
|
|
223
|
+
this.search = new _SoundCloudClient.Search(getToken, refreshCtx);
|
|
224
|
+
this.resolve = new _SoundCloudClient.Resolve(getToken, refreshCtx);
|
|
225
|
+
this.likes = new _SoundCloudClient.Likes(getToken, refreshCtx);
|
|
226
|
+
this.reposts = new _SoundCloudClient.Reposts(getToken, refreshCtx);
|
|
227
|
+
}
|
|
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
|
+
*/
|
|
234
|
+
setToken(accessToken, refreshToken) {
|
|
235
|
+
this._accessToken = accessToken;
|
|
236
|
+
if (refreshToken !== void 0) this._refreshToken = refreshToken;
|
|
237
|
+
}
|
|
238
|
+
/** Clear all stored tokens from this client instance. */
|
|
239
|
+
clearToken() {
|
|
240
|
+
this._accessToken = void 0;
|
|
241
|
+
this._refreshToken = void 0;
|
|
242
|
+
}
|
|
243
|
+
/** Get the currently stored access token, or `undefined` if none is set. */
|
|
244
|
+
get accessToken() {
|
|
245
|
+
return this._accessToken;
|
|
246
|
+
}
|
|
247
|
+
/** Get the currently stored refresh token, or `undefined` if none is set. */
|
|
248
|
+
get refreshToken() {
|
|
249
|
+
return this._refreshToken;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Async generator that follows `next_href` automatically, yielding each page's `collection`.
|
|
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
|
|
258
|
+
* ```ts
|
|
259
|
+
* for await (const page of sc.paginate(() => sc.search.tracks('lofi'))) {
|
|
260
|
+
* console.log(page); // SoundCloudTrack[]
|
|
261
|
+
* }
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
paginate(firstPage) {
|
|
265
|
+
const token = this._accessToken;
|
|
266
|
+
return paginate(firstPage, (url) => scFetchUrl(url, token));
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Async generator that yields individual items across all pages.
|
|
270
|
+
*
|
|
271
|
+
* @param firstPage - Function that fetches the first page
|
|
272
|
+
* @returns An async generator yielding individual items
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```ts
|
|
276
|
+
* for await (const track of sc.paginateItems(() => sc.search.tracks('lofi'))) {
|
|
277
|
+
* console.log(track.title); // single SoundCloudTrack
|
|
278
|
+
* }
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
paginateItems(firstPage) {
|
|
282
|
+
const token = this._accessToken;
|
|
283
|
+
return paginateItems(firstPage, (url) => scFetchUrl(url, token));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Collects all pages into a single flat array.
|
|
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
|
|
294
|
+
* ```ts
|
|
295
|
+
* const allTracks = await sc.fetchAll(() => sc.search.tracks('lofi'), { maxItems: 100 });
|
|
296
|
+
* console.log(allTracks.length);
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
fetchAll(firstPage, options) {
|
|
300
|
+
const token = this._accessToken;
|
|
301
|
+
return fetchAll(firstPage, (url) => scFetchUrl(url, token), options);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
((SoundCloudClient2) => {
|
|
305
|
+
class Auth {
|
|
306
|
+
constructor(config) {
|
|
307
|
+
this.config = config;
|
|
308
|
+
}
|
|
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
|
+
*/
|
|
326
|
+
getAuthorizationUrl(options) {
|
|
327
|
+
if (!this.config.redirectUri) throw new Error("redirectUri is required for getAuthorizationUrl");
|
|
328
|
+
const params = new URLSearchParams({
|
|
329
|
+
client_id: this.config.clientId,
|
|
330
|
+
redirect_uri: this.config.redirectUri,
|
|
331
|
+
response_type: "code"
|
|
332
|
+
});
|
|
333
|
+
if (options?.state) params.set("state", options.state);
|
|
334
|
+
if (options?.codeChallenge) {
|
|
335
|
+
params.set("code_challenge", options.codeChallenge);
|
|
336
|
+
params.set("code_challenge_method", "S256");
|
|
337
|
+
}
|
|
338
|
+
return `https://api.soundcloud.com/connect?${params}`;
|
|
339
|
+
}
|
|
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
|
+
*/
|
|
354
|
+
async getClientToken() {
|
|
355
|
+
return scFetch({
|
|
356
|
+
path: "/oauth2/token",
|
|
357
|
+
method: "POST",
|
|
358
|
+
body: new URLSearchParams({
|
|
359
|
+
grant_type: "client_credentials",
|
|
360
|
+
client_id: this.config.clientId,
|
|
361
|
+
client_secret: this.config.clientSecret
|
|
362
|
+
})
|
|
363
|
+
});
|
|
364
|
+
}
|
|
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
|
+
*/
|
|
381
|
+
async getUserToken(code, codeVerifier) {
|
|
382
|
+
const params = {
|
|
383
|
+
grant_type: "authorization_code",
|
|
384
|
+
client_id: this.config.clientId,
|
|
385
|
+
client_secret: this.config.clientSecret,
|
|
386
|
+
redirect_uri: this.config.redirectUri,
|
|
387
|
+
code
|
|
388
|
+
};
|
|
389
|
+
if (codeVerifier) params.code_verifier = codeVerifier;
|
|
390
|
+
return scFetch({
|
|
391
|
+
path: "/oauth2/token",
|
|
392
|
+
method: "POST",
|
|
393
|
+
body: new URLSearchParams(params)
|
|
394
|
+
});
|
|
395
|
+
}
|
|
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
|
+
*/
|
|
411
|
+
async refreshUserToken(refreshToken) {
|
|
412
|
+
return scFetch({
|
|
413
|
+
path: "/oauth2/token",
|
|
414
|
+
method: "POST",
|
|
415
|
+
body: new URLSearchParams({
|
|
416
|
+
grant_type: "refresh_token",
|
|
417
|
+
client_id: this.config.clientId,
|
|
418
|
+
client_secret: this.config.clientSecret,
|
|
419
|
+
redirect_uri: this.config.redirectUri,
|
|
420
|
+
refresh_token: refreshToken
|
|
421
|
+
})
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Invalidate the session associated with an access token.
|
|
426
|
+
*
|
|
427
|
+
* **Note:** This hits `https://secure.soundcloud.com`, NOT the regular
|
|
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
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
async signOut(accessToken) {
|
|
440
|
+
const res = await fetch("https://secure.soundcloud.com/sign-out", {
|
|
441
|
+
method: "POST",
|
|
442
|
+
headers: { "Content-Type": "application/json" },
|
|
443
|
+
body: JSON.stringify({ access_token: accessToken })
|
|
444
|
+
});
|
|
445
|
+
if (!res.ok) throw new Error(`Sign-out failed: ${res.status}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
SoundCloudClient2.Auth = Auth;
|
|
449
|
+
class Me {
|
|
450
|
+
constructor(getToken, refreshCtx) {
|
|
451
|
+
this.getToken = getToken;
|
|
452
|
+
this.refreshCtx = refreshCtx;
|
|
453
|
+
}
|
|
454
|
+
fetch(opts) {
|
|
455
|
+
return scFetch(opts, this.refreshCtx);
|
|
456
|
+
}
|
|
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
|
+
*/
|
|
472
|
+
async getMe(options) {
|
|
473
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
474
|
+
return this.fetch({ path: "/me", method: "GET", token: t });
|
|
475
|
+
}
|
|
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
|
+
*/
|
|
492
|
+
async getActivities(limit, options) {
|
|
493
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
494
|
+
return this.fetch({ path: `/me/activities?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
495
|
+
}
|
|
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
|
+
*/
|
|
506
|
+
async getActivitiesOwn(limit, options) {
|
|
507
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
508
|
+
return this.fetch({ path: `/me/activities/all/own?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
509
|
+
}
|
|
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
|
+
*/
|
|
520
|
+
async getActivitiesTracks(limit, options) {
|
|
521
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
522
|
+
return this.fetch({ path: `/me/activities/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
523
|
+
}
|
|
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
|
+
*/
|
|
540
|
+
async getLikesTracks(limit, options) {
|
|
541
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
542
|
+
return this.fetch({ path: `/me/likes/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
543
|
+
}
|
|
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
|
+
*/
|
|
554
|
+
async getLikesPlaylists(limit, options) {
|
|
555
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
556
|
+
return this.fetch({ path: `/me/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
557
|
+
}
|
|
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
|
+
*/
|
|
568
|
+
async getFollowings(limit, options) {
|
|
569
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
570
|
+
return this.fetch({ path: `/me/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
571
|
+
}
|
|
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
|
+
*/
|
|
582
|
+
async getFollowingsTracks(limit, options) {
|
|
583
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
584
|
+
return this.fetch({ path: `/me/followings/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
585
|
+
}
|
|
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
|
+
*/
|
|
600
|
+
async follow(userUrn, options) {
|
|
601
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
602
|
+
return this.fetch({ path: `/me/followings/${userUrn}`, method: "PUT", token: t });
|
|
603
|
+
}
|
|
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
|
+
*/
|
|
618
|
+
async unfollow(userUrn, options) {
|
|
619
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
620
|
+
return this.fetch({ path: `/me/followings/${userUrn}`, method: "DELETE", token: t });
|
|
621
|
+
}
|
|
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
|
+
*/
|
|
632
|
+
async getFollowers(limit, options) {
|
|
633
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
634
|
+
return this.fetch({ path: `/me/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
635
|
+
}
|
|
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
|
+
*/
|
|
646
|
+
async getPlaylists(limit, options) {
|
|
647
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
648
|
+
return this.fetch({ path: `/me/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
649
|
+
}
|
|
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
|
+
*/
|
|
660
|
+
async getTracks(limit, options) {
|
|
661
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
662
|
+
return this.fetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
SoundCloudClient2.Me = Me;
|
|
666
|
+
class Users {
|
|
667
|
+
constructor(getToken, refreshCtx) {
|
|
668
|
+
this.getToken = getToken;
|
|
669
|
+
this.refreshCtx = refreshCtx;
|
|
670
|
+
}
|
|
671
|
+
fetch(opts) {
|
|
672
|
+
return scFetch(opts, this.refreshCtx);
|
|
673
|
+
}
|
|
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
|
+
*/
|
|
690
|
+
async getUser(userId, options) {
|
|
691
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
692
|
+
return this.fetch({ path: `/users/${userId}`, method: "GET", token: t });
|
|
693
|
+
}
|
|
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
|
+
*/
|
|
705
|
+
async getFollowers(userId, limit, options) {
|
|
706
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
707
|
+
return this.fetch({ path: `/users/${userId}/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
708
|
+
}
|
|
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
|
+
*/
|
|
720
|
+
async getFollowings(userId, limit, options) {
|
|
721
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
722
|
+
return this.fetch({ path: `/users/${userId}/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
723
|
+
}
|
|
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
|
+
*/
|
|
735
|
+
async getTracks(userId, limit, options) {
|
|
736
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
737
|
+
return this.fetch({ path: `/users/${userId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
738
|
+
}
|
|
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
|
+
*/
|
|
750
|
+
async getPlaylists(userId, limit, options) {
|
|
751
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
752
|
+
return this.fetch({ path: `/users/${userId}/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true&show_tracks=false`, method: "GET", token: t });
|
|
753
|
+
}
|
|
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
|
+
*/
|
|
766
|
+
async getLikesTracks(userId, limit, cursor, options) {
|
|
767
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
768
|
+
return this.fetch({ path: `/users/${userId}/likes/tracks?${limit ? `limit=${limit}&` : ""}${cursor ? `cursor=${cursor}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
769
|
+
}
|
|
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
|
+
*/
|
|
781
|
+
async getLikesPlaylists(userId, limit, options) {
|
|
782
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
783
|
+
return this.fetch({ path: `/users/${userId}/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
784
|
+
}
|
|
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
|
+
*/
|
|
801
|
+
async getWebProfiles(userId, options) {
|
|
802
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
803
|
+
return this.fetch({ path: `/users/${userId}/web-profiles`, method: "GET", token: t });
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
SoundCloudClient2.Users = Users;
|
|
807
|
+
class Tracks {
|
|
808
|
+
constructor(getToken, refreshCtx) {
|
|
809
|
+
this.getToken = getToken;
|
|
810
|
+
this.refreshCtx = refreshCtx;
|
|
811
|
+
}
|
|
812
|
+
fetch(opts) {
|
|
813
|
+
return scFetch(opts, this.refreshCtx);
|
|
814
|
+
}
|
|
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
|
+
*/
|
|
831
|
+
async getTrack(trackId, options) {
|
|
832
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
833
|
+
return this.fetch({ path: `/tracks/${trackId}`, method: "GET", token: t });
|
|
834
|
+
}
|
|
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
|
+
*/
|
|
851
|
+
async getStreams(trackId, options) {
|
|
852
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
853
|
+
return this.fetch({ path: `/tracks/${trackId}/streams`, method: "GET", token: t });
|
|
854
|
+
}
|
|
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
|
+
*/
|
|
866
|
+
async getComments(trackId, limit, options) {
|
|
867
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
868
|
+
return this.fetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token: t });
|
|
869
|
+
}
|
|
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
|
+
*/
|
|
888
|
+
async createComment(trackId, body, timestamp, options) {
|
|
889
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
890
|
+
return this.fetch({
|
|
891
|
+
path: `/tracks/${trackId}/comments`,
|
|
892
|
+
method: "POST",
|
|
893
|
+
token: t,
|
|
894
|
+
body: { comment: { body, ...timestamp !== void 0 ? { timestamp } : {} } }
|
|
895
|
+
});
|
|
896
|
+
}
|
|
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
|
+
*/
|
|
908
|
+
async getLikes(trackId, limit, options) {
|
|
909
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
910
|
+
return this.fetch({ path: `/tracks/${trackId}/favoriters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
911
|
+
}
|
|
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
|
+
*/
|
|
923
|
+
async getReposts(trackId, limit, options) {
|
|
924
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
925
|
+
return this.fetch({ path: `/tracks/${trackId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
926
|
+
}
|
|
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
|
+
*/
|
|
944
|
+
async getRelated(trackId, limit, options) {
|
|
945
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
946
|
+
return this.fetch({ path: `/tracks/${trackId}/related${limit ? `?limit=${limit}` : ""}`, method: "GET", token: t });
|
|
947
|
+
}
|
|
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
|
+
*/
|
|
965
|
+
async update(trackId, params, options) {
|
|
966
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
967
|
+
return this.fetch({ path: `/tracks/${trackId}`, method: "PUT", token: t, body: { track: params } });
|
|
968
|
+
}
|
|
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
|
+
*/
|
|
983
|
+
async delete(trackId, options) {
|
|
984
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
985
|
+
return this.fetch({ path: `/tracks/${trackId}`, method: "DELETE", token: t });
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
SoundCloudClient2.Tracks = Tracks;
|
|
989
|
+
class Playlists {
|
|
990
|
+
constructor(getToken, refreshCtx) {
|
|
991
|
+
this.getToken = getToken;
|
|
992
|
+
this.refreshCtx = refreshCtx;
|
|
993
|
+
}
|
|
994
|
+
fetch(opts) {
|
|
995
|
+
return scFetch(opts, this.refreshCtx);
|
|
996
|
+
}
|
|
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
|
+
*/
|
|
1013
|
+
async getPlaylist(playlistId, options) {
|
|
1014
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1015
|
+
return this.fetch({ path: `/playlists/${playlistId}`, method: "GET", token: t });
|
|
1016
|
+
}
|
|
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
|
+
*/
|
|
1029
|
+
async getTracks(playlistId, limit, offset, options) {
|
|
1030
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1031
|
+
return this.fetch({ path: `/playlists/${playlistId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true${offset ? `&offset=${offset}` : ""}`, method: "GET", token: t });
|
|
1032
|
+
}
|
|
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
|
+
*/
|
|
1044
|
+
async getReposts(playlistId, limit, options) {
|
|
1045
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1046
|
+
return this.fetch({ path: `/playlists/${playlistId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token: t });
|
|
1047
|
+
}
|
|
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
|
+
*/
|
|
1067
|
+
async create(params, options) {
|
|
1068
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1069
|
+
return this.fetch({ path: "/playlists", method: "POST", token: t, body: { playlist: params } });
|
|
1070
|
+
}
|
|
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
|
+
*/
|
|
1087
|
+
async update(playlistId, params, options) {
|
|
1088
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1089
|
+
return this.fetch({ path: `/playlists/${playlistId}`, method: "PUT", token: t, body: { playlist: params } });
|
|
1090
|
+
}
|
|
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
|
+
*/
|
|
1105
|
+
async delete(playlistId, options) {
|
|
1106
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1107
|
+
return this.fetch({ path: `/playlists/${playlistId}`, method: "DELETE", token: t });
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
SoundCloudClient2.Playlists = Playlists;
|
|
1111
|
+
class Search {
|
|
1112
|
+
constructor(getToken, refreshCtx) {
|
|
1113
|
+
this.getToken = getToken;
|
|
1114
|
+
this.refreshCtx = refreshCtx;
|
|
1115
|
+
}
|
|
1116
|
+
fetch(opts) {
|
|
1117
|
+
return scFetch(opts, this.refreshCtx);
|
|
1118
|
+
}
|
|
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
|
+
*/
|
|
1136
|
+
async tracks(query, pageNumber, options) {
|
|
1137
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1138
|
+
return this.fetch({ path: `/tracks?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
|
|
1139
|
+
}
|
|
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
|
+
*/
|
|
1157
|
+
async users(query, pageNumber, options) {
|
|
1158
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1159
|
+
return this.fetch({ path: `/users?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
|
|
1160
|
+
}
|
|
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
|
+
*/
|
|
1178
|
+
async playlists(query, pageNumber, options) {
|
|
1179
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1180
|
+
return this.fetch({ path: `/playlists?q=${encodeURIComponent(query)}&linked_partitioning=true&limit=10${pageNumber && pageNumber > 0 ? `&offset=${10 * pageNumber}` : ""}`, method: "GET", token: t });
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
SoundCloudClient2.Search = Search;
|
|
1184
|
+
class Resolve {
|
|
1185
|
+
constructor(getToken, refreshCtx) {
|
|
1186
|
+
this.getToken = getToken;
|
|
1187
|
+
this.refreshCtx = refreshCtx;
|
|
1188
|
+
}
|
|
1189
|
+
fetch(opts) {
|
|
1190
|
+
return scFetch(opts, this.refreshCtx);
|
|
1191
|
+
}
|
|
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
|
+
*/
|
|
1208
|
+
async resolveUrl(url, options) {
|
|
1209
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1210
|
+
return this.fetch({ path: `/resolve?url=${encodeURIComponent(url)}`, method: "GET", token: t });
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
SoundCloudClient2.Resolve = Resolve;
|
|
1214
|
+
class Likes {
|
|
1215
|
+
constructor(getToken, refreshCtx) {
|
|
1216
|
+
this.getToken = getToken;
|
|
1217
|
+
this.refreshCtx = refreshCtx;
|
|
1218
|
+
}
|
|
1219
|
+
fetch(opts) {
|
|
1220
|
+
return scFetch(opts, this.refreshCtx);
|
|
1221
|
+
}
|
|
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
|
+
*/
|
|
1236
|
+
async likeTrack(trackId, options) {
|
|
1237
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1238
|
+
try {
|
|
1239
|
+
await this.fetch({ path: `/likes/tracks/${trackId}`, method: "POST", token: t });
|
|
1240
|
+
return true;
|
|
1241
|
+
} catch {
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
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
|
+
*/
|
|
1254
|
+
async unlikeTrack(trackId, options) {
|
|
1255
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1256
|
+
try {
|
|
1257
|
+
await this.fetch({ path: `/likes/tracks/${trackId}`, method: "DELETE", token: t });
|
|
1258
|
+
return true;
|
|
1259
|
+
} catch {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
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
|
+
*/
|
|
1272
|
+
async likePlaylist(playlistId, options) {
|
|
1273
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1274
|
+
try {
|
|
1275
|
+
await this.fetch({ path: `/likes/playlists/${playlistId}`, method: "POST", token: t });
|
|
1276
|
+
return true;
|
|
1277
|
+
} catch {
|
|
1278
|
+
return false;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
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
|
+
*/
|
|
1290
|
+
async unlikePlaylist(playlistId, options) {
|
|
1291
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1292
|
+
try {
|
|
1293
|
+
await this.fetch({ path: `/likes/playlists/${playlistId}`, method: "DELETE", token: t });
|
|
1294
|
+
return true;
|
|
1295
|
+
} catch {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
SoundCloudClient2.Likes = Likes;
|
|
1301
|
+
class Reposts {
|
|
1302
|
+
constructor(getToken, refreshCtx) {
|
|
1303
|
+
this.getToken = getToken;
|
|
1304
|
+
this.refreshCtx = refreshCtx;
|
|
1305
|
+
}
|
|
1306
|
+
fetch(opts) {
|
|
1307
|
+
return scFetch(opts, this.refreshCtx);
|
|
1308
|
+
}
|
|
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
|
+
*/
|
|
1323
|
+
async repostTrack(trackId, options) {
|
|
1324
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1325
|
+
try {
|
|
1326
|
+
await this.fetch({ path: `/reposts/tracks/${trackId}`, method: "POST", token: t });
|
|
1327
|
+
return true;
|
|
1328
|
+
} catch {
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
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
|
+
*/
|
|
1341
|
+
async unrepostTrack(trackId, options) {
|
|
1342
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1343
|
+
try {
|
|
1344
|
+
await this.fetch({ path: `/reposts/tracks/${trackId}`, method: "DELETE", token: t });
|
|
1345
|
+
return true;
|
|
1346
|
+
} catch {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
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
|
+
*/
|
|
1359
|
+
async repostPlaylist(playlistId, options) {
|
|
1360
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1361
|
+
try {
|
|
1362
|
+
await this.fetch({ path: `/reposts/playlists/${playlistId}`, method: "POST", token: t });
|
|
1363
|
+
return true;
|
|
1364
|
+
} catch {
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
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
|
+
*/
|
|
1377
|
+
async unrepostPlaylist(playlistId, options) {
|
|
1378
|
+
const t = resolveToken(this.getToken, options?.token);
|
|
1379
|
+
try {
|
|
1380
|
+
await this.fetch({ path: `/reposts/playlists/${playlistId}`, method: "DELETE", token: t });
|
|
1381
|
+
return true;
|
|
1382
|
+
} catch {
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
SoundCloudClient2.Reposts = Reposts;
|
|
1388
|
+
})(exports.SoundCloudClient || (exports.SoundCloudClient = {}));
|
|
1389
|
+
|
|
1390
|
+
// src/auth/getClientToken.ts
|
|
1391
|
+
var getClientToken = (clientId, clientSecret) => {
|
|
1392
|
+
return scFetch({
|
|
1393
|
+
path: "/oauth2/token",
|
|
1394
|
+
method: "POST",
|
|
1395
|
+
body: new URLSearchParams({
|
|
1396
|
+
grant_type: "client_credentials",
|
|
1397
|
+
client_id: clientId,
|
|
1398
|
+
client_secret: clientSecret
|
|
1399
|
+
})
|
|
1400
|
+
});
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
// src/auth/getUserToken.ts
|
|
1404
|
+
var getUserToken = (clientId, clientSecret, redirectUri, code, codeVerifier) => {
|
|
1405
|
+
const params = {
|
|
1406
|
+
grant_type: "authorization_code",
|
|
1407
|
+
client_id: clientId,
|
|
1408
|
+
client_secret: clientSecret,
|
|
1409
|
+
redirect_uri: redirectUri,
|
|
1410
|
+
code
|
|
1411
|
+
};
|
|
1412
|
+
if (codeVerifier) params.code_verifier = codeVerifier;
|
|
1413
|
+
return scFetch({
|
|
1414
|
+
path: "/oauth2/token",
|
|
1415
|
+
method: "POST",
|
|
1416
|
+
body: new URLSearchParams(params)
|
|
1417
|
+
});
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
// src/auth/refreshUserToken.ts
|
|
1421
|
+
var refreshUserToken = (clientId, clientSecret, redirectUri, refreshToken) => {
|
|
1422
|
+
return scFetch({
|
|
1423
|
+
path: "/oauth2/token",
|
|
1424
|
+
method: "POST",
|
|
1425
|
+
body: new URLSearchParams({
|
|
1426
|
+
grant_type: "refresh_token",
|
|
1427
|
+
client_id: clientId,
|
|
1428
|
+
client_secret: clientSecret,
|
|
1429
|
+
redirect_uri: redirectUri,
|
|
1430
|
+
refresh_token: refreshToken
|
|
1431
|
+
})
|
|
1432
|
+
});
|
|
1433
|
+
};
|
|
1434
|
+
|
|
1435
|
+
// src/auth/signOut.ts
|
|
1436
|
+
var signOut = async (accessToken) => {
|
|
1437
|
+
const res = await fetch("https://secure.soundcloud.com/sign-out", {
|
|
1438
|
+
method: "POST",
|
|
1439
|
+
headers: { "Content-Type": "application/json" },
|
|
1440
|
+
body: JSON.stringify({ access_token: accessToken })
|
|
1441
|
+
});
|
|
1442
|
+
if (!res.ok) throw new Error(`Sign-out failed: ${res.status}`);
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
// src/auth/getAuthorizationUrl.ts
|
|
1446
|
+
function getAuthorizationUrl(clientId, redirectUri, options) {
|
|
1447
|
+
const params = new URLSearchParams({
|
|
1448
|
+
client_id: clientId,
|
|
1449
|
+
redirect_uri: redirectUri,
|
|
1450
|
+
response_type: "code"
|
|
1451
|
+
});
|
|
1452
|
+
if (options?.state) params.set("state", options.state);
|
|
1453
|
+
if (options?.codeChallenge) {
|
|
1454
|
+
params.set("code_challenge", options.codeChallenge);
|
|
1455
|
+
params.set("code_challenge_method", "S256");
|
|
1456
|
+
}
|
|
1457
|
+
return `https://api.soundcloud.com/connect?${params}`;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// src/auth/pkce.ts
|
|
1461
|
+
function generateCodeVerifier() {
|
|
1462
|
+
const bytes = new Uint8Array(32);
|
|
1463
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1464
|
+
return base64url(bytes);
|
|
1465
|
+
}
|
|
1466
|
+
async function generateCodeChallenge(verifier) {
|
|
1467
|
+
const data = new TextEncoder().encode(verifier);
|
|
1468
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
1469
|
+
return base64url(new Uint8Array(digest));
|
|
1470
|
+
}
|
|
1471
|
+
function base64url(bytes) {
|
|
1472
|
+
let binary = "";
|
|
1473
|
+
for (const b of bytes) binary += String.fromCharCode(b);
|
|
1474
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/users/getMe.ts
|
|
1478
|
+
var getMe = (token) => scFetch({ path: "/me", method: "GET", token });
|
|
1479
|
+
|
|
1480
|
+
// src/users/getUser.ts
|
|
1481
|
+
var getUser = (token, userId) => scFetch({ path: `/users/${userId}`, method: "GET", token });
|
|
1482
|
+
|
|
1483
|
+
// src/users/getFollowers.ts
|
|
1484
|
+
var getFollowers = (token, userId, limit) => scFetch({ path: `/users/${userId}/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1485
|
+
|
|
1486
|
+
// src/users/getFollowings.ts
|
|
1487
|
+
var getFollowings = (token, userId, limit) => scFetch({ path: `/users/${userId}/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1488
|
+
|
|
1489
|
+
// src/users/getTracks.ts
|
|
1490
|
+
var getUserTracks = (token, userId, limit) => scFetch({ path: `/users/${userId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1491
|
+
|
|
1492
|
+
// src/users/getPlaylists.ts
|
|
1493
|
+
var getUserPlaylists = (token, userId, limit) => scFetch({ path: `/users/${userId}/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true&show_tracks=false`, method: "GET", token });
|
|
1494
|
+
|
|
1495
|
+
// src/users/getLikesTracks.ts
|
|
1496
|
+
var getUserLikesTracks = (token, userId, limit, cursor) => scFetch({ path: `/users/${userId}/likes/tracks?${limit ? `limit=${limit}&` : ""}${cursor ? `cursor=${cursor}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1497
|
+
|
|
1498
|
+
// src/users/getLikesPlaylists.ts
|
|
1499
|
+
var getUserLikesPlaylists = (token, userId, limit) => scFetch({ path: `/users/${userId}/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1500
|
+
|
|
1501
|
+
// src/users/getWebProfiles.ts
|
|
1502
|
+
var getUserWebProfiles = (token, userId) => scFetch({ path: `/users/${userId}/web-profiles`, method: "GET", token });
|
|
1503
|
+
|
|
1504
|
+
// src/tracks/getTrack.ts
|
|
1505
|
+
var getTrack = (token, trackId) => scFetch({ path: `/tracks/${trackId}`, method: "GET", token });
|
|
1506
|
+
|
|
1507
|
+
// src/tracks/getComments.ts
|
|
1508
|
+
var getTrackComments = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/comments?threaded=1&filter_replies=0${limit ? `&limit=${limit}` : ""}&linked_partitioning=true`, method: "GET", token });
|
|
1509
|
+
|
|
1510
|
+
// src/tracks/createComment.ts
|
|
1511
|
+
var createTrackComment = (token, trackId, body, timestamp) => scFetch({
|
|
1512
|
+
path: `/tracks/${trackId}/comments`,
|
|
1513
|
+
method: "POST",
|
|
1514
|
+
token,
|
|
1515
|
+
body: { comment: { body, ...timestamp !== void 0 ? { timestamp } : {} } }
|
|
1516
|
+
});
|
|
1517
|
+
|
|
1518
|
+
// src/tracks/getLikes.ts
|
|
1519
|
+
var getTrackLikes = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/favoriters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1520
|
+
|
|
1521
|
+
// src/tracks/getReposts.ts
|
|
1522
|
+
var getTrackReposts = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1523
|
+
|
|
1524
|
+
// src/tracks/getRelated.ts
|
|
1525
|
+
var getRelatedTracks = (token, trackId, limit) => scFetch({ path: `/tracks/${trackId}/related${limit ? `?limit=${limit}` : ""}`, method: "GET", token });
|
|
1526
|
+
|
|
1527
|
+
// src/tracks/getStreams.ts
|
|
1528
|
+
var getTrackStreams = (token, trackId) => scFetch({ path: `/tracks/${trackId}/streams`, method: "GET", token });
|
|
1529
|
+
|
|
1530
|
+
// src/tracks/likeTrack.ts
|
|
1531
|
+
var likeTrack = async (token, trackId) => {
|
|
1532
|
+
try {
|
|
1533
|
+
await scFetch({ path: `/likes/tracks/${trackId}`, method: "POST", token });
|
|
1534
|
+
return true;
|
|
1535
|
+
} catch {
|
|
1536
|
+
return false;
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
|
|
1540
|
+
// src/tracks/unlikeTrack.ts
|
|
1541
|
+
var unlikeTrack = async (token, trackId) => {
|
|
1542
|
+
try {
|
|
1543
|
+
await scFetch({ path: `/likes/tracks/${trackId}`, method: "DELETE", token });
|
|
1544
|
+
return true;
|
|
1545
|
+
} catch {
|
|
1546
|
+
return false;
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
// src/tracks/updateTrack.ts
|
|
1551
|
+
var updateTrack = (token, trackId, params) => scFetch({
|
|
1552
|
+
path: `/tracks/${trackId}`,
|
|
1553
|
+
method: "PUT",
|
|
1554
|
+
token,
|
|
1555
|
+
body: { track: params }
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
// src/tracks/deleteTrack.ts
|
|
1559
|
+
var deleteTrack = (token, trackId) => scFetch({ path: `/tracks/${trackId}`, method: "DELETE", token });
|
|
1560
|
+
|
|
1561
|
+
// src/playlists/getPlaylist.ts
|
|
1562
|
+
var getPlaylist = (token, playlistId) => scFetch({ path: `/playlists/${playlistId}`, method: "GET", token });
|
|
1563
|
+
|
|
1564
|
+
// src/playlists/getTracks.ts
|
|
1565
|
+
var getPlaylistTracks = (token, playlistId, limit, offset) => scFetch({ path: `/playlists/${playlistId}/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true${offset ? `&offset=${offset}` : ""}`, method: "GET", token });
|
|
1566
|
+
|
|
1567
|
+
// src/playlists/getReposts.ts
|
|
1568
|
+
var getPlaylistReposts = (token, playlistId, limit) => scFetch({ path: `/playlists/${playlistId}/reposters?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1569
|
+
|
|
1570
|
+
// src/playlists/createPlaylist.ts
|
|
1571
|
+
var createPlaylist = (token, params) => scFetch({
|
|
1572
|
+
path: "/playlists",
|
|
1573
|
+
method: "POST",
|
|
1574
|
+
token,
|
|
1575
|
+
body: { playlist: params }
|
|
1576
|
+
});
|
|
1577
|
+
|
|
1578
|
+
// src/playlists/updatePlaylist.ts
|
|
1579
|
+
var updatePlaylist = (token, playlistId, params) => scFetch({
|
|
1580
|
+
path: `/playlists/${playlistId}`,
|
|
1581
|
+
method: "PUT",
|
|
1582
|
+
token,
|
|
1583
|
+
body: { playlist: params }
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
// src/playlists/deletePlaylist.ts
|
|
1587
|
+
var deletePlaylist = (token, playlistId) => scFetch({ path: `/playlists/${playlistId}`, method: "DELETE", token });
|
|
1588
|
+
|
|
1589
|
+
// src/search/searchTracks.ts
|
|
1590
|
+
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 });
|
|
1591
|
+
|
|
1592
|
+
// src/search/searchUsers.ts
|
|
1593
|
+
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 });
|
|
1594
|
+
|
|
1595
|
+
// src/search/searchPlaylists.ts
|
|
1596
|
+
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 });
|
|
1597
|
+
|
|
1598
|
+
// src/resolve/resolveUrl.ts
|
|
1599
|
+
var resolveUrl = (token, url) => scFetch({ path: `/resolve?url=${encodeURIComponent(url)}`, method: "GET", token });
|
|
1600
|
+
|
|
1601
|
+
// src/me/activities.ts
|
|
1602
|
+
var getMeActivities = (token, limit) => scFetch({ path: `/me/activities?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1603
|
+
var getMeActivitiesOwn = (token, limit) => scFetch({ path: `/me/activities/all/own?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1604
|
+
var getMeActivitiesTracks = (token, limit) => scFetch({ path: `/me/activities/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1605
|
+
|
|
1606
|
+
// src/me/likes.ts
|
|
1607
|
+
var getMeLikesTracks = (token, limit) => scFetch({ path: `/me/likes/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1608
|
+
var getMeLikesPlaylists = (token, limit) => scFetch({ path: `/me/likes/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1609
|
+
|
|
1610
|
+
// src/me/followings.ts
|
|
1611
|
+
var getMeFollowings = (token, limit) => scFetch({ path: `/me/followings?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1612
|
+
var getMeFollowingsTracks = (token, limit) => scFetch({ path: `/me/followings/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1613
|
+
var followUser = (token, userUrn) => scFetch({ path: `/me/followings/${userUrn}`, method: "PUT", token });
|
|
1614
|
+
var unfollowUser = (token, userUrn) => scFetch({ path: `/me/followings/${userUrn}`, method: "DELETE", token });
|
|
1615
|
+
|
|
1616
|
+
// src/me/followers.ts
|
|
1617
|
+
var getMeFollowers = (token, limit) => scFetch({ path: `/me/followers?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1618
|
+
|
|
1619
|
+
// src/me/playlists.ts
|
|
1620
|
+
var getMePlaylists = (token, limit) => scFetch({ path: `/me/playlists?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1621
|
+
|
|
1622
|
+
// src/me/tracks.ts
|
|
1623
|
+
var getMeTracks = (token, limit) => scFetch({ path: `/me/tracks?${limit ? `limit=${limit}&` : ""}linked_partitioning=true`, method: "GET", token });
|
|
1624
|
+
|
|
1625
|
+
// src/likes/index.ts
|
|
1626
|
+
var likePlaylist = async (token, playlistId) => {
|
|
1627
|
+
try {
|
|
1628
|
+
await scFetch({ path: `/likes/playlists/${playlistId}`, method: "POST", token });
|
|
1629
|
+
return true;
|
|
1630
|
+
} catch {
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
var unlikePlaylist = async (token, playlistId) => {
|
|
1635
|
+
try {
|
|
1636
|
+
await scFetch({ path: `/likes/playlists/${playlistId}`, method: "DELETE", token });
|
|
1637
|
+
return true;
|
|
1638
|
+
} catch {
|
|
1639
|
+
return false;
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1643
|
+
// src/reposts/index.ts
|
|
1644
|
+
var repostTrack = async (token, trackId) => {
|
|
1645
|
+
try {
|
|
1646
|
+
await scFetch({ path: `/reposts/tracks/${trackId}`, method: "POST", token });
|
|
1647
|
+
return true;
|
|
1648
|
+
} catch {
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
var unrepostTrack = async (token, trackId) => {
|
|
1653
|
+
try {
|
|
1654
|
+
await scFetch({ path: `/reposts/tracks/${trackId}`, method: "DELETE", token });
|
|
1655
|
+
return true;
|
|
1656
|
+
} catch {
|
|
1657
|
+
return false;
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
var repostPlaylist = async (token, playlistId) => {
|
|
1661
|
+
try {
|
|
1662
|
+
await scFetch({ path: `/reposts/playlists/${playlistId}`, method: "POST", token });
|
|
1663
|
+
return true;
|
|
1664
|
+
} catch {
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
var unrepostPlaylist = async (token, playlistId) => {
|
|
1669
|
+
try {
|
|
1670
|
+
await scFetch({ path: `/reposts/playlists/${playlistId}`, method: "DELETE", token });
|
|
1671
|
+
return true;
|
|
1672
|
+
} catch {
|
|
1673
|
+
return false;
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
|
|
1677
|
+
// src/utils/widget.ts
|
|
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`;
|
|
1679
|
+
|
|
1680
|
+
exports.createPlaylist = createPlaylist;
|
|
1681
|
+
exports.createTrackComment = createTrackComment;
|
|
1682
|
+
exports.deletePlaylist = deletePlaylist;
|
|
1683
|
+
exports.deleteTrack = deleteTrack;
|
|
1684
|
+
exports.fetchAll = fetchAll;
|
|
1685
|
+
exports.followUser = followUser;
|
|
1686
|
+
exports.generateCodeChallenge = generateCodeChallenge;
|
|
1687
|
+
exports.generateCodeVerifier = generateCodeVerifier;
|
|
1688
|
+
exports.getAuthorizationUrl = getAuthorizationUrl;
|
|
1689
|
+
exports.getClientToken = getClientToken;
|
|
1690
|
+
exports.getFollowers = getFollowers;
|
|
1691
|
+
exports.getFollowings = getFollowings;
|
|
1692
|
+
exports.getMe = getMe;
|
|
1693
|
+
exports.getMeActivities = getMeActivities;
|
|
1694
|
+
exports.getMeActivitiesOwn = getMeActivitiesOwn;
|
|
1695
|
+
exports.getMeActivitiesTracks = getMeActivitiesTracks;
|
|
1696
|
+
exports.getMeFollowers = getMeFollowers;
|
|
1697
|
+
exports.getMeFollowings = getMeFollowings;
|
|
1698
|
+
exports.getMeFollowingsTracks = getMeFollowingsTracks;
|
|
1699
|
+
exports.getMeLikesPlaylists = getMeLikesPlaylists;
|
|
1700
|
+
exports.getMeLikesTracks = getMeLikesTracks;
|
|
1701
|
+
exports.getMePlaylists = getMePlaylists;
|
|
1702
|
+
exports.getMeTracks = getMeTracks;
|
|
1703
|
+
exports.getPlaylist = getPlaylist;
|
|
1704
|
+
exports.getPlaylistReposts = getPlaylistReposts;
|
|
1705
|
+
exports.getPlaylistTracks = getPlaylistTracks;
|
|
1706
|
+
exports.getRelatedTracks = getRelatedTracks;
|
|
1707
|
+
exports.getSoundCloudWidgetUrl = getSoundCloudWidgetUrl;
|
|
1708
|
+
exports.getTrack = getTrack;
|
|
1709
|
+
exports.getTrackComments = getTrackComments;
|
|
1710
|
+
exports.getTrackLikes = getTrackLikes;
|
|
1711
|
+
exports.getTrackReposts = getTrackReposts;
|
|
1712
|
+
exports.getTrackStreams = getTrackStreams;
|
|
1713
|
+
exports.getUser = getUser;
|
|
1714
|
+
exports.getUserLikesPlaylists = getUserLikesPlaylists;
|
|
1715
|
+
exports.getUserLikesTracks = getUserLikesTracks;
|
|
1716
|
+
exports.getUserPlaylists = getUserPlaylists;
|
|
1717
|
+
exports.getUserToken = getUserToken;
|
|
1718
|
+
exports.getUserTracks = getUserTracks;
|
|
1719
|
+
exports.getUserWebProfiles = getUserWebProfiles;
|
|
1720
|
+
exports.likePlaylist = likePlaylist;
|
|
1721
|
+
exports.likeTrack = likeTrack;
|
|
1722
|
+
exports.paginate = paginate;
|
|
1723
|
+
exports.paginateItems = paginateItems;
|
|
1724
|
+
exports.refreshUserToken = refreshUserToken;
|
|
1725
|
+
exports.repostPlaylist = repostPlaylist;
|
|
1726
|
+
exports.repostTrack = repostTrack;
|
|
1727
|
+
exports.resolveUrl = resolveUrl;
|
|
1728
|
+
exports.scFetch = scFetch;
|
|
1729
|
+
exports.scFetchUrl = scFetchUrl;
|
|
1730
|
+
exports.searchPlaylists = searchPlaylists;
|
|
1731
|
+
exports.searchTracks = searchTracks;
|
|
1732
|
+
exports.searchUsers = searchUsers;
|
|
1733
|
+
exports.signOut = signOut;
|
|
1734
|
+
exports.unfollowUser = unfollowUser;
|
|
1735
|
+
exports.unlikePlaylist = unlikePlaylist;
|
|
1736
|
+
exports.unlikeTrack = unlikeTrack;
|
|
1737
|
+
exports.unrepostPlaylist = unrepostPlaylist;
|
|
1738
|
+
exports.unrepostTrack = unrepostTrack;
|
|
1739
|
+
exports.updatePlaylist = updatePlaylist;
|
|
1740
|
+
exports.updateTrack = updateTrack;
|
|
1741
|
+
//# sourceMappingURL=chunk-LNMDNQSC.js.map
|
|
1742
|
+
//# sourceMappingURL=chunk-LNMDNQSC.js.map
|