soundcloud-api-ts-next 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -243
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
# soundcloud-api-ts-next
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/soundcloud-api-ts-next)
|
|
4
|
+
[](https://www.npmjs.com/package/soundcloud-api-ts-next)
|
|
5
|
+
[](https://github.com/twin-paws/soundcloud-api-ts-next/blob/main/LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
React hooks and Next.js API route handlers for the SoundCloud API. Client secrets stay on the server.
|
|
8
|
+
|
|
9
|
+
Built on [soundcloud-api-ts](https://github.com/twin-paws/soundcloud-api-ts).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
6
12
|
|
|
7
13
|
```bash
|
|
8
14
|
npm install soundcloud-api-ts-next
|
|
9
|
-
# or
|
|
10
|
-
pnpm add soundcloud-api-ts-next
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
### 1. Server Routes
|
|
16
|
-
|
|
17
|
-
Create an API route handler that proxies SoundCloud requests (keeps your credentials server-side).
|
|
17
|
+
## Quick Start
|
|
18
18
|
|
|
19
|
-
**
|
|
19
|
+
**1. Create API routes** — secrets stay server-side:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
+
// app/api/soundcloud/[...route]/route.ts
|
|
22
23
|
import { createSoundCloudRoutes } from "soundcloud-api-ts-next/server";
|
|
23
24
|
|
|
24
25
|
const sc = createSoundCloudRoutes({
|
|
25
26
|
clientId: process.env.SOUNDCLOUD_CLIENT_ID!,
|
|
26
27
|
clientSecret: process.env.SOUNDCLOUD_CLIENT_SECRET!,
|
|
27
|
-
redirectUri: process.env.SOUNDCLOUD_REDIRECT_URI, //
|
|
28
|
+
redirectUri: process.env.SOUNDCLOUD_REDIRECT_URI, // for OAuth
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
const handler = sc.handler();
|
|
@@ -33,25 +34,26 @@ export const POST = handler;
|
|
|
33
34
|
export const DELETE = handler;
|
|
34
35
|
```
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
<details>
|
|
38
|
+
<summary>Pages Router setup</summary>
|
|
37
39
|
|
|
38
40
|
```ts
|
|
41
|
+
// pages/api/soundcloud/[...route].ts
|
|
39
42
|
import { createSoundCloudRoutes } from "soundcloud-api-ts-next/server";
|
|
40
43
|
|
|
41
44
|
const sc = createSoundCloudRoutes({
|
|
42
45
|
clientId: process.env.SOUNDCLOUD_CLIENT_ID!,
|
|
43
46
|
clientSecret: process.env.SOUNDCLOUD_CLIENT_SECRET!,
|
|
44
|
-
redirectUri: process.env.SOUNDCLOUD_REDIRECT_URI,
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
export default sc.pagesHandler();
|
|
48
50
|
```
|
|
51
|
+
</details>
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Wrap your app with the `SoundCloudProvider`:
|
|
53
|
+
**2. Add the provider:**
|
|
53
54
|
|
|
54
55
|
```tsx
|
|
56
|
+
// app/layout.tsx
|
|
55
57
|
import { SoundCloudProvider } from "soundcloud-api-ts-next";
|
|
56
58
|
|
|
57
59
|
export default function Layout({ children }) {
|
|
@@ -63,29 +65,117 @@ export default function Layout({ children }) {
|
|
|
63
65
|
}
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
**3. Use hooks:**
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { useTrackSearch, usePlayer } from "soundcloud-api-ts-next";
|
|
72
|
+
|
|
73
|
+
function SearchPage() {
|
|
74
|
+
const { data: tracks, loading } = useTrackSearch("lofi beats");
|
|
75
|
+
|
|
76
|
+
if (loading) return <p>Searching...</p>;
|
|
77
|
+
|
|
78
|
+
return tracks?.map((track) => (
|
|
79
|
+
<div key={track.id}>
|
|
80
|
+
<p>{track.title} — {track.user.username}</p>
|
|
81
|
+
</div>
|
|
82
|
+
));
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Hooks
|
|
89
|
+
|
|
90
|
+
All hooks return `{ data, loading, error }`.
|
|
91
|
+
|
|
92
|
+
### Tracks
|
|
93
|
+
|
|
94
|
+
| Hook | Description |
|
|
95
|
+
|------|-------------|
|
|
96
|
+
| `useTrack(id)` | Single track |
|
|
97
|
+
| `useTrackSearch(query)` | Search tracks |
|
|
98
|
+
| `useTrackComments(id)` | Track comments |
|
|
99
|
+
| `useTrackLikes(id)` | Users who liked a track |
|
|
100
|
+
| `useRelatedTracks(id)` | Related tracks |
|
|
101
|
+
| `usePlayer(streamUrl)` | Audio player — `{ playing, progress, duration, play, pause, toggle, seek }` |
|
|
102
|
+
|
|
103
|
+
### Users
|
|
104
|
+
|
|
105
|
+
| Hook | Description |
|
|
106
|
+
|------|-------------|
|
|
107
|
+
| `useUser(id)` | Single user |
|
|
108
|
+
| `useUserSearch(query)` | Search users |
|
|
109
|
+
| `useUserTracks(id)` | User's tracks |
|
|
110
|
+
| `useUserPlaylists(id)` | User's playlists |
|
|
111
|
+
| `useUserLikes(id)` | User's liked tracks |
|
|
112
|
+
| `useUserFollowers(id)` | User's followers |
|
|
113
|
+
| `useUserFollowings(id)` | User's followings |
|
|
114
|
+
|
|
115
|
+
### Playlists
|
|
116
|
+
|
|
117
|
+
| Hook | Description |
|
|
118
|
+
|------|-------------|
|
|
119
|
+
| `usePlaylist(id)` | Single playlist |
|
|
120
|
+
| `usePlaylistSearch(query)` | Search playlists |
|
|
121
|
+
| `usePlaylistTracks(id)` | Playlist tracks |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Infinite Scroll
|
|
126
|
+
|
|
127
|
+
Cursor-based pagination with `loadMore()` and `reset()`. All return `InfiniteResult<T>`:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
{ data: T[], loading, error, hasMore, loadMore, reset }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { useInfiniteTrackSearch } from "soundcloud-api-ts-next";
|
|
135
|
+
|
|
136
|
+
function Feed() {
|
|
137
|
+
const { data, loading, hasMore, loadMore } = useInfiniteTrackSearch("dubstep");
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
{data.map((track) => <TrackCard key={track.id} track={track} />)}
|
|
142
|
+
{hasMore && <button onClick={loadMore} disabled={loading}>Load More</button>}
|
|
143
|
+
</>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Hook | Description |
|
|
149
|
+
|------|-------------|
|
|
150
|
+
| `useInfiniteTrackSearch(query)` | Paginated track search |
|
|
151
|
+
| `useInfiniteUserSearch(query)` | Paginated user search |
|
|
152
|
+
| `useInfinitePlaylistSearch(query)` | Paginated playlist search |
|
|
153
|
+
| `useInfiniteUserTracks(id)` | User's tracks |
|
|
154
|
+
| `useInfiniteUserPlaylists(id)` | User's playlists |
|
|
155
|
+
| `useInfiniteUserLikes(id)` | User's liked tracks |
|
|
156
|
+
| `useInfiniteUserFollowers(id)` | User's followers |
|
|
157
|
+
| `useInfiniteUserFollowings(id)` | User's followings |
|
|
158
|
+
| `useInfiniteTrackComments(id)` | Track comments |
|
|
159
|
+
| `useInfinitePlaylistTracks(id)` | Playlist tracks |
|
|
67
160
|
|
|
68
|
-
|
|
161
|
+
---
|
|
69
162
|
|
|
70
|
-
|
|
163
|
+
## Authentication
|
|
71
164
|
|
|
72
|
-
1
|
|
73
|
-
2. Set your SoundCloud app's redirect URI to match (e.g., `http://localhost:3000/callback`)
|
|
165
|
+
Full OAuth 2.1 with PKCE. No secrets on the client.
|
|
74
166
|
|
|
75
|
-
### Login
|
|
167
|
+
### Login
|
|
76
168
|
|
|
77
169
|
```tsx
|
|
78
170
|
import { useSCAuth } from "soundcloud-api-ts-next";
|
|
79
171
|
|
|
80
172
|
function LoginButton() {
|
|
81
|
-
const { isAuthenticated, user, login, logout
|
|
82
|
-
|
|
83
|
-
if (loading) return <p>Loading...</p>;
|
|
173
|
+
const { isAuthenticated, user, login, logout } = useSCAuth();
|
|
84
174
|
|
|
85
175
|
if (isAuthenticated) {
|
|
86
176
|
return (
|
|
87
177
|
<div>
|
|
88
|
-
<p>
|
|
178
|
+
<p>Welcome, {user?.username}</p>
|
|
89
179
|
<button onClick={logout}>Logout</button>
|
|
90
180
|
</div>
|
|
91
181
|
);
|
|
@@ -97,264 +187,117 @@ function LoginButton() {
|
|
|
97
187
|
|
|
98
188
|
### Callback Page
|
|
99
189
|
|
|
100
|
-
Create a callback page that handles the OAuth redirect:
|
|
101
|
-
|
|
102
190
|
```tsx
|
|
103
191
|
// app/callback/page.tsx
|
|
104
192
|
"use client";
|
|
105
|
-
|
|
106
193
|
import { useEffect } from "react";
|
|
107
194
|
import { useSearchParams, useRouter } from "next/navigation";
|
|
108
195
|
import { useSCAuth } from "soundcloud-api-ts-next";
|
|
109
196
|
|
|
110
|
-
export default function
|
|
111
|
-
const
|
|
197
|
+
export default function Callback() {
|
|
198
|
+
const params = useSearchParams();
|
|
112
199
|
const router = useRouter();
|
|
113
200
|
const { handleCallback } = useSCAuth();
|
|
114
201
|
|
|
115
202
|
useEffect(() => {
|
|
116
|
-
const code =
|
|
117
|
-
const state =
|
|
203
|
+
const code = params.get("code");
|
|
204
|
+
const state = params.get("state");
|
|
118
205
|
if (code && state) {
|
|
119
|
-
handleCallback(code, state).then(() =>
|
|
120
|
-
router.push("/");
|
|
121
|
-
});
|
|
206
|
+
handleCallback(code, state).then(() => router.push("/"));
|
|
122
207
|
}
|
|
123
|
-
}, [
|
|
208
|
+
}, [params]);
|
|
124
209
|
|
|
125
210
|
return <p>Authenticating...</p>;
|
|
126
211
|
}
|
|
127
212
|
```
|
|
128
213
|
|
|
129
|
-
### Authenticated
|
|
130
|
-
|
|
131
|
-
These hooks fetch data for the currently logged-in user. They require authentication and automatically pass the access token.
|
|
132
|
-
|
|
133
|
-
| Hook | Returns | Description |
|
|
134
|
-
|------|---------|-------------|
|
|
135
|
-
| `useMe()` | `HookResult<SoundCloudUser>` | Current user's profile |
|
|
136
|
-
| `useMeTracks()` | `HookResult<SoundCloudTrack[]>` | Current user's tracks |
|
|
137
|
-
| `useMeLikes()` | `HookResult<SoundCloudTrack[]>` | Current user's liked tracks |
|
|
138
|
-
| `useMePlaylists()` | `HookResult<SoundCloudPlaylist[]>` | Current user's playlists |
|
|
139
|
-
| `useMeFollowings()` | `HookResult<SoundCloudUser[]>` | Who current user follows |
|
|
140
|
-
| `useMeFollowers()` | `HookResult<SoundCloudUser[]>` | Current user's followers |
|
|
214
|
+
### Authenticated Hooks
|
|
141
215
|
|
|
142
|
-
|
|
143
|
-
import { useMe, useMeTracks, useMeLikes } from "soundcloud-api-ts-next";
|
|
144
|
-
|
|
145
|
-
function MyProfile() {
|
|
146
|
-
const { data: me } = useMe();
|
|
147
|
-
const { data: tracks } = useMeTracks();
|
|
148
|
-
const { data: likes } = useMeLikes();
|
|
216
|
+
Available after login. Automatically pass the user's access token.
|
|
149
217
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
```
|
|
218
|
+
| Hook | Description |
|
|
219
|
+
|------|-------------|
|
|
220
|
+
| `useMe()` | Current user profile |
|
|
221
|
+
| `useMeTracks()` | Your tracks |
|
|
222
|
+
| `useMeLikes()` | Your liked tracks |
|
|
223
|
+
| `useMePlaylists()` | Your playlists |
|
|
224
|
+
| `useMeFollowings()` | Who you follow |
|
|
225
|
+
| `useMeFollowers()` | Your followers |
|
|
160
226
|
|
|
161
|
-
###
|
|
227
|
+
### Actions
|
|
162
228
|
|
|
163
|
-
Mutation hooks for
|
|
229
|
+
Mutation hooks for authenticated users.
|
|
164
230
|
|
|
165
|
-
| Hook | Methods |
|
|
166
|
-
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `useRepost()` | `repostTrack(
|
|
231
|
+
| Hook | Methods |
|
|
232
|
+
|------|---------|
|
|
233
|
+
| `useLike()` | `likeTrack(id)`, `unlikeTrack(id)` |
|
|
234
|
+
| `useFollow()` | `follow(userId)`, `unfollow(userId)` |
|
|
235
|
+
| `useRepost()` | `repostTrack(id)`, `unrepostTrack(id)` |
|
|
170
236
|
|
|
171
237
|
```tsx
|
|
172
238
|
import { useLike, useFollow } from "soundcloud-api-ts-next";
|
|
173
239
|
|
|
174
240
|
function TrackActions({ trackId, artistId }) {
|
|
175
|
-
const { likeTrack
|
|
176
|
-
const { follow
|
|
241
|
+
const { likeTrack } = useLike();
|
|
242
|
+
const { follow } = useFollow();
|
|
177
243
|
|
|
178
244
|
return (
|
|
179
|
-
|
|
180
|
-
<button onClick={() => likeTrack(trackId)}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
<button onClick={() => follow(artistId)} disabled={followLoading}>
|
|
184
|
-
➕ Follow Artist
|
|
185
|
-
</button>
|
|
186
|
-
</div>
|
|
245
|
+
<>
|
|
246
|
+
<button onClick={() => likeTrack(trackId)}>❤️ Like</button>
|
|
247
|
+
<button onClick={() => follow(artistId)}>➕ Follow</button>
|
|
248
|
+
</>
|
|
187
249
|
);
|
|
188
250
|
}
|
|
189
251
|
```
|
|
190
252
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
All hooks return `{ data, loading, error }`.
|
|
194
|
-
|
|
195
|
-
### Tracks
|
|
196
|
-
|
|
197
|
-
| Hook | Arguments | Description |
|
|
198
|
-
|------|-----------|-------------|
|
|
199
|
-
| `useTrack(trackId)` | `string \| number \| undefined` | Fetch a single track |
|
|
200
|
-
| `useTrackSearch(query, options?)` | `string`, `{ limit? }` | Search tracks |
|
|
201
|
-
| `useTrackComments(trackId)` | `string \| number \| undefined` | Get track comments |
|
|
202
|
-
| `useTrackLikes(trackId)` | `string \| number \| undefined` | Get users who liked a track |
|
|
203
|
-
| `useRelatedTracks(trackId)` | `string \| number \| undefined` | Get related tracks |
|
|
204
|
-
|
|
205
|
-
### Users
|
|
206
|
-
|
|
207
|
-
| Hook | Arguments | Description |
|
|
208
|
-
|------|-----------|-------------|
|
|
209
|
-
| `useUser(userId)` | `string \| number \| undefined` | Fetch a single user |
|
|
210
|
-
| `useUserSearch(query)` | `string` | Search users |
|
|
211
|
-
| `useUserTracks(userId)` | `string \| number \| undefined` | Get a user's tracks |
|
|
212
|
-
| `useUserPlaylists(userId)` | `string \| number \| undefined` | Get a user's playlists |
|
|
213
|
-
| `useUserLikes(userId)` | `string \| number \| undefined` | Get a user's liked tracks |
|
|
214
|
-
| `useUserFollowers(userId)` | `string \| number \| undefined` | Get a user's followers |
|
|
215
|
-
| `useUserFollowings(userId)` | `string \| number \| undefined` | Get a user's followings |
|
|
216
|
-
|
|
217
|
-
### Playlists
|
|
218
|
-
|
|
219
|
-
| Hook | Arguments | Description |
|
|
220
|
-
|------|-----------|-------------|
|
|
221
|
-
| `usePlaylist(playlistId)` | `string \| number \| undefined` | Fetch a single playlist |
|
|
222
|
-
| `usePlaylistTracks(playlistId)` | `string \| number \| undefined` | Get tracks in a playlist |
|
|
223
|
-
| `usePlaylistSearch(query)` | `string` | Search playlists |
|
|
224
|
-
|
|
225
|
-
### Player
|
|
226
|
-
|
|
227
|
-
| Hook | Arguments | Description |
|
|
228
|
-
|------|-----------|-------------|
|
|
229
|
-
| `usePlayer(streamUrl)` | `string \| undefined` | Audio player with play/pause/seek |
|
|
253
|
+
---
|
|
230
254
|
|
|
231
255
|
## Server Routes
|
|
232
256
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
### Auth Routes
|
|
236
|
-
|
|
237
|
-
| Route | Method | Description |
|
|
238
|
-
|-------|--------|-------------|
|
|
239
|
-
| `/auth/login` | GET | Get SoundCloud OAuth URL (PKCE) |
|
|
240
|
-
| `/auth/callback?code=...&state=...` | GET | Exchange auth code for tokens |
|
|
241
|
-
| `/auth/refresh` | POST | Refresh access token |
|
|
242
|
-
| `/auth/logout` | POST | Sign out / revoke token |
|
|
243
|
-
|
|
244
|
-
### Me Routes (require `Authorization: Bearer <token>` header)
|
|
245
|
-
|
|
246
|
-
| Route | Method | Description |
|
|
247
|
-
|-------|--------|-------------|
|
|
248
|
-
| `/me` | GET | Current user profile |
|
|
249
|
-
| `/me/tracks` | GET | Current user's tracks |
|
|
250
|
-
| `/me/likes` | GET | Current user's liked tracks |
|
|
251
|
-
| `/me/playlists` | GET | Current user's playlists |
|
|
252
|
-
| `/me/followings` | GET | Who current user follows |
|
|
253
|
-
| `/me/followers` | GET | Current user's followers |
|
|
254
|
-
|
|
255
|
-
### Action Routes (require `Authorization: Bearer <token>` header)
|
|
256
|
-
|
|
257
|
-
| Route | Method | Description |
|
|
258
|
-
|-------|--------|-------------|
|
|
259
|
-
| `/me/follow/:userId` | POST | Follow a user |
|
|
260
|
-
| `/me/follow/:userId` | DELETE | Unfollow a user |
|
|
261
|
-
| `/tracks/:id/like` | POST | Like a track |
|
|
262
|
-
| `/tracks/:id/like` | DELETE | Unlike a track |
|
|
263
|
-
| `/tracks/:id/repost` | POST | Repost a track |
|
|
264
|
-
| `/tracks/:id/repost` | DELETE | Unrepost a track |
|
|
265
|
-
| `/playlists/:id/like` | POST/DELETE | Like/unlike a playlist |
|
|
266
|
-
| `/playlists/:id/repost` | POST/DELETE | Repost/unrepost a playlist |
|
|
267
|
-
|
|
268
|
-
### Search
|
|
269
|
-
|
|
270
|
-
| Route | Method | Description |
|
|
271
|
-
|-------|--------|-------------|
|
|
272
|
-
| `GET /search/tracks?q=...` | `searchTracks(q, page?)` | Search tracks |
|
|
273
|
-
| `GET /search/users?q=...` | `searchUsers(q)` | Search users |
|
|
274
|
-
| `GET /search/playlists?q=...` | `searchPlaylists(q)` | Search playlists |
|
|
275
|
-
|
|
276
|
-
### Tracks
|
|
277
|
-
|
|
278
|
-
| Route | Method | Description |
|
|
279
|
-
|-------|--------|-------------|
|
|
280
|
-
| `GET /tracks/:id` | `getTrack(id)` | Get track details |
|
|
281
|
-
| `GET /tracks/:id/stream` | `getTrackStreams(id)` | Get stream URLs |
|
|
282
|
-
| `GET /tracks/:id/comments` | `getTrackComments(id)` | Get track comments |
|
|
283
|
-
| `GET /tracks/:id/likes` | `getTrackLikes(id)` | Get track likes |
|
|
284
|
-
| `GET /tracks/:id/related` | `getRelatedTracks(id)` | Get related tracks |
|
|
285
|
-
|
|
286
|
-
### Users
|
|
287
|
-
|
|
288
|
-
| Route | Method | Description |
|
|
289
|
-
|-------|--------|-------------|
|
|
290
|
-
| `GET /users/:id` | `getUser(id)` | Get user details |
|
|
291
|
-
| `GET /users/:id/tracks` | `getUserTracks(id, limit?)` | Get user's tracks |
|
|
292
|
-
| `GET /users/:id/playlists` | `getUserPlaylists(id)` | Get user's playlists |
|
|
293
|
-
| `GET /users/:id/likes/tracks` | `getUserLikesTracks(id)` | Get user's liked tracks |
|
|
294
|
-
| `GET /users/:id/followers` | `getFollowers(id)` | Get user's followers |
|
|
295
|
-
| `GET /users/:id/followings` | `getFollowings(id)` | Get user's followings |
|
|
296
|
-
|
|
297
|
-
### Playlists
|
|
257
|
+
The catch-all handler exposes these routes automatically:
|
|
298
258
|
|
|
299
259
|
| Route | Method | Description |
|
|
300
260
|
|-------|--------|-------------|
|
|
301
|
-
|
|
|
302
|
-
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
|
330
|
-
|
|
|
331
|
-
| `
|
|
332
|
-
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
| `useInfiniteUserFollowers(userId)` | User's followers |
|
|
338
|
-
| `useInfiniteUserFollowings(userId)` | User's followings |
|
|
339
|
-
| `useInfiniteTrackComments(trackId)` | Track comments |
|
|
340
|
-
| `useInfinitePlaylistTracks(playlistId)` | Playlist tracks |
|
|
341
|
-
|
|
342
|
-
All hooks return `InfiniteResult<T>`:
|
|
343
|
-
|
|
344
|
-
```ts
|
|
345
|
-
interface InfiniteResult<T> {
|
|
346
|
-
data: T[]; // accumulated items across all pages
|
|
347
|
-
loading: boolean;
|
|
348
|
-
error: Error | null;
|
|
349
|
-
hasMore: boolean; // true if more pages exist
|
|
350
|
-
loadMore: () => void;
|
|
351
|
-
reset: () => void; // clear and refetch from page 1
|
|
352
|
-
}
|
|
353
|
-
```
|
|
261
|
+
| `/search/tracks?q=` | GET | Search tracks |
|
|
262
|
+
| `/search/users?q=` | GET | Search users |
|
|
263
|
+
| `/search/playlists?q=` | GET | Search playlists |
|
|
264
|
+
| `/tracks/:id` | GET | Track details |
|
|
265
|
+
| `/tracks/:id/stream` | GET | Stream URLs |
|
|
266
|
+
| `/tracks/:id/comments` | GET | Track comments |
|
|
267
|
+
| `/tracks/:id/likes` | GET | Track likes |
|
|
268
|
+
| `/tracks/:id/related` | GET | Related tracks |
|
|
269
|
+
| `/tracks/:id/like` | POST/DELETE | Like/unlike (auth) |
|
|
270
|
+
| `/tracks/:id/repost` | POST/DELETE | Repost/unrepost (auth) |
|
|
271
|
+
| `/users/:id` | GET | User details |
|
|
272
|
+
| `/users/:id/tracks` | GET | User tracks |
|
|
273
|
+
| `/users/:id/playlists` | GET | User playlists |
|
|
274
|
+
| `/users/:id/likes/tracks` | GET | User likes |
|
|
275
|
+
| `/users/:id/followers` | GET | User followers |
|
|
276
|
+
| `/users/:id/followings` | GET | User followings |
|
|
277
|
+
| `/playlists/:id` | GET | Playlist details |
|
|
278
|
+
| `/playlists/:id/tracks` | GET | Playlist tracks |
|
|
279
|
+
| `/playlists/:id/like` | POST/DELETE | Like/unlike (auth) |
|
|
280
|
+
| `/playlists/:id/repost` | POST/DELETE | Repost/unrepost (auth) |
|
|
281
|
+
| `/me` | GET | Current user (auth) |
|
|
282
|
+
| `/me/tracks` | GET | Your tracks (auth) |
|
|
283
|
+
| `/me/likes` | GET | Your likes (auth) |
|
|
284
|
+
| `/me/playlists` | GET | Your playlists (auth) |
|
|
285
|
+
| `/me/followings` | GET | Your followings (auth) |
|
|
286
|
+
| `/me/followers` | GET | Your followers (auth) |
|
|
287
|
+
| `/me/follow/:userId` | POST/DELETE | Follow/unfollow (auth) |
|
|
288
|
+
| `/auth/login` | GET | OAuth URL (PKCE) |
|
|
289
|
+
| `/auth/callback` | GET | Token exchange |
|
|
290
|
+
| `/auth/refresh` | POST | Refresh token |
|
|
291
|
+
| `/auth/logout` | POST | Sign out |
|
|
292
|
+
| `/next?url=` | GET | Pagination cursor |
|
|
293
|
+
|
|
294
|
+
Routes marked **(auth)** require `Authorization: Bearer <token>` header.
|
|
295
|
+
|
|
296
|
+
---
|
|
354
297
|
|
|
355
298
|
## Types
|
|
356
299
|
|
|
357
|
-
|
|
300
|
+
Re-exported from [soundcloud-api-ts](https://github.com/twin-paws/soundcloud-api-ts):
|
|
358
301
|
|
|
359
302
|
```ts
|
|
360
303
|
import type {
|
|
@@ -364,11 +307,21 @@ import type {
|
|
|
364
307
|
SoundCloudComment,
|
|
365
308
|
SoundCloudStreams,
|
|
366
309
|
SoundCloudToken,
|
|
367
|
-
AuthState,
|
|
368
|
-
MutationResult,
|
|
369
310
|
} from "soundcloud-api-ts-next";
|
|
370
311
|
```
|
|
371
312
|
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Requirements
|
|
316
|
+
|
|
317
|
+
- **Next.js** 13+ (App Router or Pages Router)
|
|
318
|
+
- **React** 18+
|
|
319
|
+
- **soundcloud-api-ts** installed automatically as a dependency
|
|
320
|
+
|
|
372
321
|
## License
|
|
373
322
|
|
|
374
323
|
MIT
|
|
324
|
+
|
|
325
|
+
## Related
|
|
326
|
+
|
|
327
|
+
- [soundcloud-api-ts](https://github.com/twin-paws/soundcloud-api-ts) — The TypeScript-first SoundCloud API client this package is built on
|