diff --git a/ts/packages/agents/player/src/client.ts b/ts/packages/agents/player/src/client.ts index dba4fdafd1..e61ff7559c 100644 --- a/ts/packages/agents/player/src/client.ts +++ b/ts/packages/agents/player/src/client.ts @@ -52,6 +52,7 @@ import { addTracksToPlaylist, getRecommendationsFromTrackCollection, getRecentlyPlayed, + limitMax, } from "./endpoints.js"; import { htmlStatus, printStatus } from "./playback.js"; import { SpotifyService } from "./service.js"; @@ -451,7 +452,7 @@ export async function searchTracks( const query: SpotifyApi.SearchForItemParameterObject = { q: queryString, type: "track", - limit: 50, + limit: limitMax, offset: 0, }; const data = await search(query, context.service); @@ -467,7 +468,7 @@ export async function searchForPlaylists( const query: SpotifyApi.SearchForItemParameterObject = { q: queryString, type: "playlist", - limit: 20, + limit: limitMax, offset: 0, }; const data = await search(query, context.service); diff --git a/ts/packages/agents/player/src/endpoints.ts b/ts/packages/agents/player/src/endpoints.ts index fb4d91ecd9..85e9d18791 100644 --- a/ts/packages/agents/player/src/endpoints.ts +++ b/ts/packages/agents/player/src/endpoints.ts @@ -8,7 +8,8 @@ import { createFetchError } from "./utils.js"; const debugSpotifyRest = registerDebug("typeagent:spotify:rest"); const debugSpotifyRestVerbose = registerDebug("typeagent:spotify-verbose:rest"); -const limitMax = 50; +/** Maximum number of items per Spotify API request */ +export const limitMax = 50; export async function search( query: SpotifyApi.SearchForItemParameterObject, @@ -483,7 +484,7 @@ export async function getQueue(service: SpotifyService) { return fetchGet( service, "https://api.spotify.com/v1/me/player/queue", - { limit: 50 }, + { limit: limitMax }, ); } @@ -707,8 +708,17 @@ export async function setVolume( } function getUrlWithParams(urlString: string, queryParams: Record) { - const params = new URLSearchParams(queryParams); - const url = new URL(urlString); - url.search = params.toString(); - return url.toString(); + // Use encodeURIComponent for standard URL percent-encoding instead of + // URLSearchParams, which uses application/x-www-form-urlencoded semantics + // (encoding spaces as '+' rather than '%20'). Spotify's API expects spaces + // to be percent-encoded as '%20'. + const parts: string[] = []; + for (const [key, value] of Object.entries(queryParams)) { + if (value !== undefined && value !== null) { + parts.push( + `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`, + ); + } + } + return parts.length > 0 ? `${urlString}?${parts.join("&")}` : urlString; } diff --git a/ts/packages/agents/player/src/search.ts b/ts/packages/agents/player/src/search.ts index 7645cc80ee..58119741b8 100644 --- a/ts/packages/agents/player/src/search.ts +++ b/ts/packages/agents/player/src/search.ts @@ -21,6 +21,9 @@ const debugReuse = registerDebug("typeagent:spotify:search:reuse"); const debugVerbose = registerDebug("typeagent:spotify-verbose:search"); const debugError = registerDebug("typeagent:spotify:search:error"); +/** Number of results to fetch for targeted searches */ +const searchResultLimit = 10; + export type SpotifyQuery = { track?: string[] | undefined; album?: string[] | undefined; @@ -56,7 +59,7 @@ export async function searchArtists( const query: SpotifyApi.SearchForItemParameterObject = { q: `artist:${quoteString(searchTerm)}`, type: "artist", - limit: 50, + limit: searchResultLimit, offset: 0, }; return search(query, context.service); @@ -77,7 +80,7 @@ export async function searchAlbums( const searchQuery: SpotifyApi.SearchForItemParameterObject = { q: queryString, type: "album", - limit: 50, + limit: searchResultLimit, offset: 0, }; const result = await search(searchQuery, context.service); @@ -386,7 +389,7 @@ export async function findArtistTracksWithGenre( const param: SpotifyApi.SearchForItemParameterObject = { q: queryString, type: "track", - limit: 50, + limit: searchResultLimit, offset: 0, }; const result = await search(param, context.service); @@ -489,7 +492,7 @@ async function expandMovementTracks( const param: SpotifyApi.SearchForItemParameterObject = { q: toQueryString({ ...originalQuery, album: [album] }), type: "track", - limit: 50, + limit: searchResultLimit, offset: 0, }; const result = await search(param, context.service); @@ -564,7 +567,7 @@ export async function findTracksWithGenre( const param: SpotifyApi.SearchForItemParameterObject = { q: queryString, type: "track", - limit: 50, + limit: searchResultLimit, offset: 0, }; const result = await search(param, context.service); @@ -647,7 +650,7 @@ export async function findTracks( const param: SpotifyApi.SearchForItemParameterObject = { q: queryString, type: "track", - limit: 50, + limit: searchResultLimit, offset: 0, }; const result = await search(param, context.service);