diff --git a/src/albums.ts b/src/albums.ts index 5a5fc78..68ae139 100644 --- a/src/albums.ts +++ b/src/albums.ts @@ -1,7 +1,7 @@ import type { MaxInt } from '@spotify/web-api-ts-sdk'; import { z } from 'zod'; import type { SpotifyHandlerExtra, tool } from './types.js'; -import { formatDuration, handleSpotifyRequest } from './utils.js'; +import { createSpotifyApi, formatDuration, handleSpotifyRequest, loadSpotifyConfig } from './utils.js'; const getAlbums: tool<{ albumIds: z.ZodUnion<[z.ZodString, z.ZodArray]>; @@ -204,11 +204,24 @@ const saveOrRemoveAlbumForUser: tool<{ } try { - await handleSpotifyRequest(async (spotifyApi) => { - return action === 'save' - ? await spotifyApi.currentUser.albums.saveAlbums(albumIds) - : await spotifyApi.currentUser.albums.removeSavedAlbums(albumIds); - }); + await createSpotifyApi(); + const config = loadSpotifyConfig(); + + const uris = albumIds.map((id) => `spotify:album:${id}`).join(','); + const response = await fetch( + `https://api.spotify.com/v1/me/library?uris=${encodeURIComponent(uris)}`, + { + method: action === 'save' ? 'PUT' : 'DELETE', + headers: { + Authorization: `Bearer ${config.accessToken}`, + }, + }, + ); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error(`Spotify API error ${response.status}: ${errorData}`); + } const actionPastTense = action === 'save' ? 'saved' : 'removed'; const preposition = action === 'save' ? 'to' : 'from'; diff --git a/src/play.ts b/src/play.ts index 641ddd1..9b566e3 100644 --- a/src/play.ts +++ b/src/play.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import type { SpotifyHandlerExtra, tool } from './types.js'; -import { handleSpotifyRequest } from './utils.js'; +import { createSpotifyApi, handleSpotifyRequest, loadSpotifyConfig } from './utils.js'; const playMusic: tool<{ uri: z.ZodOptional; @@ -182,24 +182,47 @@ const createPlaylist: tool<{ handler: async (args, _extra: SpotifyHandlerExtra) => { const { name, description, public: isPublic = false } = args; - const result = await handleSpotifyRequest(async (spotifyApi) => { - const me = await spotifyApi.currentUser.profile(); + try { + await createSpotifyApi(); + const config = loadSpotifyConfig(); - return await spotifyApi.playlists.createPlaylist(me.id, { - name, - description, - public: isPublic, - }); - }); + const body: Record = { name, public: isPublic }; + if (description) body.description = description; - return { - content: [ - { - type: 'text', - text: `Successfully created playlist "${name}"\nPlaylist ID: ${result.id}\nPlaylist URL: ${result.external_urls.spotify}`, + const response = await fetch('https://api.spotify.com/v1/me/playlists', { + method: 'POST', + headers: { + Authorization: `Bearer ${config.accessToken}`, + 'Content-Type': 'application/json', }, - ], - }; + body: JSON.stringify(body), + }); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error(`Spotify API error ${response.status}: ${errorData}`); + } + + const result = await response.json(); + + return { + content: [ + { + type: 'text', + text: `Successfully created playlist "${name}"\nPlaylist ID: ${result.id}\nPlaylist URL: ${result.external_urls?.spotify ?? ''}`, + }, + ], + }; + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error creating playlist: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + }; + } }, }; @@ -236,13 +259,28 @@ const addTracksToPlaylist: tool<{ try { const trackUris = trackIds.map((id) => `spotify:track:${id}`); - await handleSpotifyRequest(async (spotifyApi) => { - await spotifyApi.playlists.addItemsToPlaylist( - playlistId, - trackUris, - position, - ); - }); + await createSpotifyApi(); + const config = loadSpotifyConfig(); + + const body: Record = { uris: trackUris }; + if (position !== undefined) body.position = position; + + const response = await fetch( + `https://api.spotify.com/v1/playlists/${playlistId}/items`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${config.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }, + ); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error(`Spotify API error ${response.status}: ${errorData}`); + } return { content: [ diff --git a/src/playlist.ts b/src/playlist.ts index e052c0a..c5145f9 100644 --- a/src/playlist.ts +++ b/src/playlist.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import type { SpotifyHandlerExtra, tool } from './types.js'; -import { handleSpotifyRequest } from './utils.js'; +import { createSpotifyApi, handleSpotifyRequest, loadSpotifyConfig } from './utils.js'; const getPlaylist: tool<{ playlistId: z.ZodString; @@ -173,14 +173,29 @@ const removeTracksFromPlaylist: tool<{ const { playlistId, trackIds, snapshotId } = args; try { - const tracks = trackIds.map((id) => ({ uri: `spotify:track:${id}` })); + await createSpotifyApi(); + const config = loadSpotifyConfig(); - await handleSpotifyRequest(async (spotifyApi) => { - await spotifyApi.playlists.removeItemsFromPlaylist(playlistId, { - tracks, - ...(snapshotId ? { snapshot_id: snapshotId } : {}), - }); - }); + const items = trackIds.map((id) => ({ uri: `spotify:track:${id}` })); + const body: Record = { tracks: items }; + if (snapshotId) body.snapshot_id = snapshotId; + + const response = await fetch( + `https://api.spotify.com/v1/playlists/${playlistId}/items`, + { + method: 'DELETE', + headers: { + Authorization: `Bearer ${config.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }, + ); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error(`Spotify API error ${response.status}: ${errorData}`); + } return { content: [ @@ -246,14 +261,32 @@ const reorderPlaylistItems: tool<{ args; try { - await handleSpotifyRequest(async (spotifyApi) => { - await spotifyApi.playlists.updatePlaylistItems(playlistId, { - range_start: rangeStart, - insert_before: insertBefore, - ...(rangeLength !== undefined ? { range_length: rangeLength } : {}), - ...(snapshotId ? { snapshot_id: snapshotId } : {}), - }); - }); + await createSpotifyApi(); + const config = loadSpotifyConfig(); + + const body: Record = { + range_start: rangeStart, + insert_before: insertBefore, + }; + if (rangeLength !== undefined) body.range_length = rangeLength; + if (snapshotId) body.snapshot_id = snapshotId; + + const response = await fetch( + `https://api.spotify.com/v1/playlists/${playlistId}/items`, + { + method: 'PUT', + headers: { + Authorization: `Bearer ${config.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }, + ); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error(`Spotify API error ${response.status}: ${errorData}`); + } const count = rangeLength ?? 1; return {