@darrenjaws/spotify-mcp 0.4.0 → 0.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/README.md +30 -95
- package/build/index.js +4 -0
- package/build/index.js.map +1 -1
- package/build/spotify/errors.d.ts +11 -0
- package/build/spotify/errors.d.ts.map +1 -0
- package/build/spotify/errors.js +61 -0
- package/build/spotify/errors.js.map +1 -0
- package/build/tools/playback.d.ts +1 -0
- package/build/tools/playback.d.ts.map +1 -1
- package/build/tools/playback.js +255 -124
- package/build/tools/playback.js.map +1 -1
- package/build/tools/playlists.d.ts.map +1 -1
- package/build/tools/playlists.js +84 -63
- package/build/tools/playlists.js.map +1 -1
- package/build/tools/search.d.ts.map +1 -1
- package/build/tools/search.js +53 -47
- package/build/tools/search.js.map +1 -1
- package/build/tools/user.d.ts.map +1 -1
- package/build/tools/user.js +84 -68
- package/build/tools/user.js.map +1 -1
- package/package.json +3 -2
- package/build/handlers/call-tool.d.ts +0 -6
- package/build/handlers/call-tool.d.ts.map +0 -1
- package/build/handlers/call-tool.js +0 -46
- package/build/handlers/call-tool.js.map +0 -1
- package/build/handlers/tools.d.ts +0 -296
- package/build/handlers/tools.d.ts.map +0 -1
- package/build/handlers/tools.js +0 -246
- package/build/handlers/tools.js.map +0 -1
package/build/tools/playback.js
CHANGED
|
@@ -1,169 +1,300 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Playback control tools
|
|
3
3
|
*/
|
|
4
|
+
import { spawnSync } from "child_process";
|
|
4
5
|
import { getAuthenticatedClient } from "../spotify/client.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
6
|
+
import { handleToolError } from "../spotify/errors.js";
|
|
7
|
+
async function ensureDevice(client, deviceId) {
|
|
8
|
+
// If caller specified a device_id, trust it
|
|
9
|
+
if (deviceId)
|
|
10
|
+
return null;
|
|
11
|
+
const response = await client.getMyDevices();
|
|
12
|
+
const devices = response.body?.devices ?? [];
|
|
13
|
+
const active = devices.find((d) => d.is_active);
|
|
14
|
+
if (active)
|
|
15
|
+
return null;
|
|
16
|
+
// No active device — return helpful error
|
|
17
|
+
if (devices.length > 0) {
|
|
18
|
+
const list = devices.map((d) => ` - ${d.name} (${d.type})`).join("\n");
|
|
19
|
+
return {
|
|
20
|
+
content: [{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: `No active Spotify device. Available devices:\n${list}\n\nOpen Spotify on one of these devices or use spotify_open to launch it.`,
|
|
23
|
+
}],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
return {
|
|
28
|
+
content: [{
|
|
29
|
+
type: "text",
|
|
30
|
+
text: "No Spotify devices found. Please open Spotify on a device and try again, or use spotify_open to launch it.",
|
|
31
|
+
}],
|
|
32
|
+
isError: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export async function play(args) {
|
|
36
|
+
try {
|
|
37
|
+
const client = await getAuthenticatedClient();
|
|
38
|
+
const deviceError = await ensureDevice(client, args.device_id);
|
|
39
|
+
if (deviceError)
|
|
40
|
+
return deviceError;
|
|
41
|
+
const options = {};
|
|
42
|
+
if (args.uri) {
|
|
43
|
+
// Determine if URI is a context (album, playlist, artist) or a track
|
|
44
|
+
if (args.uri.startsWith('spotify:album:') ||
|
|
45
|
+
args.uri.startsWith('spotify:playlist:') ||
|
|
46
|
+
args.uri.startsWith('spotify:artist:')) {
|
|
47
|
+
options.context_uri = args.uri;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Track or other URI - use uris array
|
|
51
|
+
options.uris = [args.uri];
|
|
52
|
+
}
|
|
32
53
|
}
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
if (args.uris) {
|
|
55
|
+
options.uris = args.uris;
|
|
35
56
|
}
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
if (args.device_id) {
|
|
58
|
+
options.device_id = args.device_id;
|
|
38
59
|
}
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
await client.play(options);
|
|
61
|
+
// Generate appropriate response message
|
|
62
|
+
let message = "Resumed playback";
|
|
63
|
+
if (args.uri) {
|
|
64
|
+
if (args.uri.startsWith('spotify:album:')) {
|
|
65
|
+
message = `Started playing album: ${args.uri}`;
|
|
66
|
+
}
|
|
67
|
+
else if (args.uri.startsWith('spotify:playlist:')) {
|
|
68
|
+
message = `Started playing playlist: ${args.uri}`;
|
|
69
|
+
}
|
|
70
|
+
else if (args.uri.startsWith('spotify:artist:')) {
|
|
71
|
+
message = `Started playing artist: ${args.uri}`;
|
|
72
|
+
}
|
|
73
|
+
else if (args.uri.startsWith('spotify:track:')) {
|
|
74
|
+
message = `Started playing track: ${args.uri}`;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
message = `Started playback: ${args.uri}`;
|
|
78
|
+
}
|
|
41
79
|
}
|
|
42
|
-
else {
|
|
43
|
-
message = `Started
|
|
80
|
+
else if (args.uris && args.uris.length > 0) {
|
|
81
|
+
message = `Started playing ${args.uris.length} track${args.uris.length > 1 ? 's' : ''}`;
|
|
44
82
|
}
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: message,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
45
91
|
}
|
|
46
|
-
|
|
47
|
-
|
|
92
|
+
catch (error) {
|
|
93
|
+
return handleToolError(error, "spotify_play");
|
|
48
94
|
}
|
|
49
|
-
return {
|
|
50
|
-
content: [
|
|
51
|
-
{
|
|
52
|
-
type: "text",
|
|
53
|
-
text: message,
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
95
|
}
|
|
58
96
|
export async function pause(args) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
97
|
+
try {
|
|
98
|
+
const client = await getAuthenticatedClient();
|
|
99
|
+
const deviceError = await ensureDevice(client, args.device_id);
|
|
100
|
+
if (deviceError)
|
|
101
|
+
return deviceError;
|
|
102
|
+
const options = {};
|
|
103
|
+
if (args.device_id) {
|
|
104
|
+
options.device_id = args.device_id;
|
|
105
|
+
}
|
|
106
|
+
await client.pause(options);
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: "Playback paused",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return handleToolError(error, "spotify_pause");
|
|
63
118
|
}
|
|
64
|
-
await client.pause(options);
|
|
65
|
-
return {
|
|
66
|
-
content: [
|
|
67
|
-
{
|
|
68
|
-
type: "text",
|
|
69
|
-
text: "Playback paused",
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
};
|
|
73
119
|
}
|
|
74
120
|
export async function next(args) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
121
|
+
try {
|
|
122
|
+
const client = await getAuthenticatedClient();
|
|
123
|
+
const deviceError = await ensureDevice(client, args.device_id);
|
|
124
|
+
if (deviceError)
|
|
125
|
+
return deviceError;
|
|
126
|
+
const options = {};
|
|
127
|
+
if (args.device_id) {
|
|
128
|
+
options.device_id = args.device_id;
|
|
129
|
+
}
|
|
130
|
+
await client.skipToNext(options);
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: "Skipped to next track",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return handleToolError(error, "spotify_next");
|
|
79
142
|
}
|
|
80
|
-
await client.skipToNext(options);
|
|
81
|
-
return {
|
|
82
|
-
content: [
|
|
83
|
-
{
|
|
84
|
-
type: "text",
|
|
85
|
-
text: "Skipped to next track",
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
};
|
|
89
143
|
}
|
|
90
144
|
export async function previous(args) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
145
|
+
try {
|
|
146
|
+
const client = await getAuthenticatedClient();
|
|
147
|
+
const deviceError = await ensureDevice(client, args.device_id);
|
|
148
|
+
if (deviceError)
|
|
149
|
+
return deviceError;
|
|
150
|
+
const options = {};
|
|
151
|
+
if (args.device_id) {
|
|
152
|
+
options.device_id = args.device_id;
|
|
153
|
+
}
|
|
154
|
+
await client.skipToPrevious(options);
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: "Skipped to previous track",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return handleToolError(error, "spotify_previous");
|
|
95
166
|
}
|
|
96
|
-
await client.skipToPrevious(options);
|
|
97
|
-
return {
|
|
98
|
-
content: [
|
|
99
|
-
{
|
|
100
|
-
type: "text",
|
|
101
|
-
text: "Skipped to previous track",
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
};
|
|
105
167
|
}
|
|
106
168
|
export async function setVolume(args) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
169
|
+
try {
|
|
170
|
+
const client = await getAuthenticatedClient();
|
|
171
|
+
const deviceError = await ensureDevice(client, args.device_id);
|
|
172
|
+
if (deviceError)
|
|
173
|
+
return deviceError;
|
|
174
|
+
const options = { volume_percent: args.volume_percent };
|
|
175
|
+
if (args.device_id) {
|
|
176
|
+
options.device_id = args.device_id;
|
|
177
|
+
}
|
|
178
|
+
await client.setVolume(options.volume_percent, options);
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: `Volume set to ${args.volume_percent}%`,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
return handleToolError(error, "spotify_set_volume");
|
|
111
190
|
}
|
|
112
|
-
await client.setVolume(options.volume_percent, options);
|
|
113
|
-
return {
|
|
114
|
-
content: [
|
|
115
|
-
{
|
|
116
|
-
type: "text",
|
|
117
|
-
text: `Volume set to ${args.volume_percent}%`,
|
|
118
|
-
},
|
|
119
|
-
],
|
|
120
|
-
};
|
|
121
191
|
}
|
|
122
192
|
export async function getPlaybackState() {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
193
|
+
try {
|
|
194
|
+
const client = await getAuthenticatedClient();
|
|
195
|
+
const state = await client.getMyCurrentPlaybackState();
|
|
196
|
+
if (!state.body || !state.body.item) {
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: "No active playback",
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const track = state.body.item;
|
|
207
|
+
const isPlaying = state.body.is_playing;
|
|
208
|
+
const device = state.body.device;
|
|
209
|
+
// Type guard for track
|
|
210
|
+
if (track.type !== "track") {
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: "text",
|
|
215
|
+
text: `Currently playing: ${track.name} (${track.type})`,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const artists = track.artists.map((a) => a.name).join(", ");
|
|
221
|
+
const album = track.album.name;
|
|
222
|
+
const progress = Math.floor((state.body.progress_ms || 0) / 1000);
|
|
223
|
+
const duration = Math.floor(track.duration_ms / 1000);
|
|
224
|
+
const systemVol = getSystemVolume();
|
|
225
|
+
const volumeLine = systemVol !== null
|
|
226
|
+
? `Volume: ${device.volume_percent}% (Spotify) / ${systemVol}% (System)`
|
|
227
|
+
: `Volume: ${device.volume_percent}%`;
|
|
228
|
+
const text = `${isPlaying ? "▶️ Playing" : "⏸️ Paused"}: ${track.name}
|
|
229
|
+
Artist: ${artists}
|
|
230
|
+
Album: ${album}
|
|
231
|
+
Progress: ${formatTime(progress)} / ${formatTime(duration)}
|
|
232
|
+
Device: ${device.name} (${device.type})
|
|
233
|
+
${volumeLine}`;
|
|
126
234
|
return {
|
|
127
235
|
content: [
|
|
128
236
|
{
|
|
129
237
|
type: "text",
|
|
130
|
-
text
|
|
238
|
+
text,
|
|
131
239
|
},
|
|
132
240
|
],
|
|
133
241
|
};
|
|
134
242
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
243
|
+
catch (error) {
|
|
244
|
+
return handleToolError(error, "spotify_get_playback_state");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
export async function getDevices() {
|
|
248
|
+
try {
|
|
249
|
+
const client = await getAuthenticatedClient();
|
|
250
|
+
const response = await client.getMyDevices();
|
|
251
|
+
const devices = response.body?.devices ?? [];
|
|
252
|
+
if (devices.length === 0) {
|
|
253
|
+
return {
|
|
254
|
+
content: [
|
|
255
|
+
{
|
|
256
|
+
type: "text",
|
|
257
|
+
text: "No Spotify devices found. Please open Spotify on a device and try again, or use spotify_open to launch it.",
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const formatted = devices.map((d, index) => {
|
|
263
|
+
const active = d.is_active ? " [Active]" : "";
|
|
264
|
+
const volume = d.volume_percent !== null && d.volume_percent !== undefined
|
|
265
|
+
? ` - Volume: ${d.volume_percent}%`
|
|
266
|
+
: "";
|
|
267
|
+
return `${index + 1}. ${d.name} (${d.type})${active}${volume}\n ID: ${d.id}`;
|
|
268
|
+
});
|
|
140
269
|
return {
|
|
141
270
|
content: [
|
|
142
271
|
{
|
|
143
272
|
type: "text",
|
|
144
|
-
text: `
|
|
273
|
+
text: `Available Spotify devices:\n\n${formatted.join("\n")}`,
|
|
145
274
|
},
|
|
146
275
|
],
|
|
147
276
|
};
|
|
148
277
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
278
|
+
catch (error) {
|
|
279
|
+
return handleToolError(error, "spotify_get_devices");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function getSystemVolume() {
|
|
283
|
+
try {
|
|
284
|
+
if (process.platform !== "darwin")
|
|
285
|
+
return null;
|
|
286
|
+
const result = spawnSync("osascript", ["-e", "output volume of (get volume settings)"], {
|
|
287
|
+
shell: false,
|
|
288
|
+
timeout: 2000,
|
|
289
|
+
});
|
|
290
|
+
if (result.status !== 0)
|
|
291
|
+
return null;
|
|
292
|
+
const vol = parseInt(result.stdout.toString().trim(), 10);
|
|
293
|
+
return isNaN(vol) ? null : vol;
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
167
298
|
}
|
|
168
299
|
function formatTime(seconds) {
|
|
169
300
|
const mins = Math.floor(seconds / 60);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playback.js","sourceRoot":"","sources":["../../src/tools/playback.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"playback.js","sourceRoot":"","sources":["../../src/tools/playback.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,KAAK,UAAU,YAAY,CAAC,MAAW,EAAE,QAAiB;IACxD,4CAA4C;IAC5C,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,QAAQ,GAAQ,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAClD,MAAM,OAAO,GAAU,QAAQ,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IAEpD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACrD,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IAExB,0CAA0C;IAC1C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,iDAAiD,IAAI,4EAA4E;iBACxI,CAAC;YACF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC;gBACR,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,4GAA4G;aACnH,CAAC;QACF,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAkB;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QAEpC,MAAM,OAAO,GAAQ,EAAE,CAAC;QAExB,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC;gBACrC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC;gBACxC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC3C,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,sCAAsC;gBACtC,OAAO,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC;QAED,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE3B,wCAAwC;QACxC,IAAI,OAAO,GAAG,kBAAkB,CAAC;QACjC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC1C,OAAO,GAAG,0BAA0B,IAAI,CAAC,GAAG,EAAE,CAAC;YACjD,CAAC;iBAAM,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACpD,OAAO,GAAG,6BAA6B,IAAI,CAAC,GAAG,EAAE,CAAC;YACpD,CAAC;iBAAM,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAClD,OAAO,GAAG,2BAA2B,IAAI,CAAC,GAAG,EAAE,CAAC;YAClD,CAAC;iBAAM,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACjD,OAAO,GAAG,0BAA0B,IAAI,CAAC,GAAG,EAAE,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,qBAAqB,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5C,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,OAAO,GAAG,mBAAmB,IAAI,CAAC,IAAI,CAAC,MAAM,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC1F,CAAC;QAED,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,OAAO;iBACd;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAA4B;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QAEpC,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC;QAED,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE5B,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,iBAAiB;iBACxB;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAA4B;IACrD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QAEpC,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC;QAED,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAEjC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,uBAAuB;iBAC9B;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAA4B;IACzD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QAEpC,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC;QAED,MAAM,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAErC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,2BAA2B;iBAClC;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QAEpC,MAAM,OAAO,GAAQ,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7D,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC;QAED,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QAExD,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,iBAAiB,IAAI,CAAC,cAAc,GAAG;iBAC9C;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,yBAAyB,EAAE,CAAC;QAEvD,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACpC,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,oBAAoB;qBAC3B;iBACF;aACF,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;QAEjC,uBAAuB;QACvB,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,sBAAsB,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,GAAG;qBACzD;iBACF;aACF,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;QAEtD,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,SAAS,KAAK,IAAI;YACnC,CAAC,CAAC,WAAW,MAAM,CAAC,cAAc,iBAAiB,SAAS,YAAY;YACxE,CAAC,CAAC,WAAW,MAAM,CAAC,cAAc,GAAG,CAAC;QAExC,MAAM,IAAI,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,KAAK,KAAK,CAAC,IAAI;UAC/D,OAAO;SACR,KAAK;YACF,UAAU,CAAC,QAAQ,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC;UAChD,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;EACnC,UAAU,EAAE,CAAC;QAEX,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI;iBACL;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAE9C,MAAM,QAAQ,GAAQ,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAClD,MAAM,OAAO,GAAU,QAAQ,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;QAEpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4GAA4G;qBACnH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,KAAa,EAAE,EAAE;YACtD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,MAAM,MAAM,GACV,CAAC,CAAC,cAAc,KAAK,IAAI,IAAI,CAAC,CAAC,cAAc,KAAK,SAAS;gBACzD,CAAC,CAAC,cAAc,CAAC,CAAC,cAAc,GAAG;gBACnC,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,MAAM,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC;QACjF,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,iCAAiC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;iBAC9D;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,eAAe,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC/C,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,wCAAwC,CAAC,EAAE;YACtF,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,OAAO,GAAG,EAAE,CAAC;IAC1B,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playlists.d.ts","sourceRoot":"","sources":["../../src/tools/playlists.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"playlists.d.ts","sourceRoot":"","sources":["../../src/tools/playlists.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAmClF;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAiC9E;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA2BpF;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAsBlF"}
|
package/build/tools/playlists.js
CHANGED
|
@@ -2,42 +2,49 @@
|
|
|
2
2
|
* Playlist management tools
|
|
3
3
|
*/
|
|
4
4
|
import { getAuthenticatedClient } from "../spotify/client.js";
|
|
5
|
+
import { handleToolError } from "../spotify/errors.js";
|
|
5
6
|
export async function getPlaylists(args) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
try {
|
|
8
|
+
const client = await getAuthenticatedClient();
|
|
9
|
+
const limit = args.limit || 20;
|
|
10
|
+
const result = await client.getUserPlaylists({ limit });
|
|
11
|
+
const playlists = result.body.items;
|
|
12
|
+
if (playlists.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: "No playlists found",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const formatted = playlists.map((playlist, index) => `${index + 1}. ${playlist.name} (${playlist.tracks.total} tracks) - ${playlist.id}`);
|
|
11
23
|
return {
|
|
12
24
|
content: [
|
|
13
25
|
{
|
|
14
26
|
type: "text",
|
|
15
|
-
text:
|
|
27
|
+
text: `Your playlists:\n\n${formatted.join("\n")}`,
|
|
16
28
|
},
|
|
17
29
|
],
|
|
18
30
|
};
|
|
19
31
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{
|
|
24
|
-
type: "text",
|
|
25
|
-
text: `Your playlists:\n\n${formatted.join("\n")}`,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
};
|
|
32
|
+
catch (error) {
|
|
33
|
+
return handleToolError(error, "spotify_get_playlists");
|
|
34
|
+
}
|
|
29
35
|
}
|
|
30
36
|
export async function getPlaylist(args) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
try {
|
|
38
|
+
const client = await getAuthenticatedClient();
|
|
39
|
+
const result = await client.getPlaylist(args.playlist_id);
|
|
40
|
+
const playlist = result.body;
|
|
41
|
+
const tracks = playlist.tracks.items.slice(0, 10).map((item, index) => {
|
|
42
|
+
const track = item.track;
|
|
43
|
+
if (!track)
|
|
44
|
+
return `${index + 1}. [Unknown track]`;
|
|
45
|
+
return `${index + 1}. ${track.name} - ${track.artists.map((a) => a.name).join(", ")}`;
|
|
46
|
+
});
|
|
47
|
+
const text = `Playlist: ${playlist.name}
|
|
41
48
|
Description: ${playlist.description || "No description"}
|
|
42
49
|
Owner: ${playlist.owner.display_name}
|
|
43
50
|
Tracks: ${playlist.tracks.total}
|
|
@@ -45,49 +52,63 @@ Public: ${playlist.public ? "Yes" : "No"}
|
|
|
45
52
|
|
|
46
53
|
First 10 tracks:
|
|
47
54
|
${tracks.join("\n")}`;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
return {
|
|
56
|
+
content: [
|
|
57
|
+
{
|
|
58
|
+
type: "text",
|
|
59
|
+
text,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return handleToolError(error, "spotify_get_playlist");
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
export async function createPlaylist(args) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
try {
|
|
70
|
+
const client = await getAuthenticatedClient();
|
|
71
|
+
const options = {
|
|
72
|
+
name: args.name,
|
|
73
|
+
public: args.public !== false, // Default to true
|
|
74
|
+
};
|
|
75
|
+
if (args.description) {
|
|
76
|
+
options.description = args.description;
|
|
77
|
+
}
|
|
78
|
+
const result = await client.createPlaylist(options.name, options);
|
|
79
|
+
const playlist = result.body;
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `Created playlist: ${playlist.name} (ID: ${playlist.id})`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return handleToolError(error, "spotify_create_playlist");
|
|
65
91
|
}
|
|
66
|
-
const result = await client.createPlaylist(options.name, options);
|
|
67
|
-
const playlist = result.body;
|
|
68
|
-
return {
|
|
69
|
-
content: [
|
|
70
|
-
{
|
|
71
|
-
type: "text",
|
|
72
|
-
text: `Created playlist: ${playlist.name} (ID: ${playlist.id})`,
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
};
|
|
76
92
|
}
|
|
77
93
|
export async function addToPlaylist(args) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
try {
|
|
95
|
+
const client = await getAuthenticatedClient();
|
|
96
|
+
const options = {};
|
|
97
|
+
if (args.position !== undefined) {
|
|
98
|
+
options.position = args.position;
|
|
99
|
+
}
|
|
100
|
+
await client.addTracksToPlaylist(args.playlist_id, args.uris, options);
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: `Added ${args.uris.length} track(s) to playlist`,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return handleToolError(error, "spotify_add_to_playlist");
|
|
82
112
|
}
|
|
83
|
-
await client.addTracksToPlaylist(args.playlist_id, args.uris, options);
|
|
84
|
-
return {
|
|
85
|
-
content: [
|
|
86
|
-
{
|
|
87
|
-
type: "text",
|
|
88
|
-
text: `Added ${args.uris.length} track(s) to playlist`,
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
};
|
|
92
113
|
}
|
|
93
114
|
//# sourceMappingURL=playlists.js.map
|