Skip to content

Commit 99d6c98

Browse files
committed
add filters to search
1 parent 6889cbc commit 99d6c98

File tree

4 files changed

+94
-43
lines changed

4 files changed

+94
-43
lines changed

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/pub/SearchApi.kt

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.adamratzman.spotify.http.SpotifyEndpoint
88
import com.adamratzman.spotify.models.Artist
99
import com.adamratzman.spotify.models.PagingObject
1010
import com.adamratzman.spotify.models.Playlist
11+
import com.adamratzman.spotify.models.SearchFilter
1112
import com.adamratzman.spotify.models.SimpleAlbum
1213
import com.adamratzman.spotify.models.SimpleEpisode
1314
import com.adamratzman.spotify.models.SimplePlaylist
@@ -43,7 +44,8 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
4344
ARTIST("artist"),
4445
PLAYLIST("playlist"),
4546
SHOW("show"),
46-
EPISODE("episode");
47+
EPISODE("episode"),
48+
AUDIOBOOK("audiobook");
4749
}
4850

4951
/**
@@ -67,10 +69,10 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
6769
* Note: Operators must be specified in uppercase. Otherwise, they are handled as normal keywords to be matched.
6870
*
6971
* Wildcards: The asterisk (*) character can, with some limitations, be used as a wildcard (maximum: 2 per query). It matches a variable number of non-white-space characters. It cannot be used:
70-
- in a quoted phrase
71-
- in a field filter
72-
- when there is a dash (“-“) in the query
73-
- or as the first character of the keyword string Field filters: By default, results are returned when a match is found in any field of the target object type. Searches can be made more specific by specifying an album, artist or track field filter.
72+
- in a quoted phrase
73+
- in a field filter
74+
- when there is a dash (“-“) in the query
75+
- or as the first character of the keyword string Field filters: By default, results are returned when a match is found in any field of the target object type. Searches can be made more specific by specifying an album, artist or track field filter.
7476
*
7577
* For example: The query q=album:gold%20artist:abba&type=album returns only albums with the text “gold” in the album name and the text “abba” in the artist name.
7678
*
@@ -86,33 +88,34 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
8688
*
8789
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
8890
*
89-
* @param query Search query keywords and optional field filters and operators. You can narrow down your search using field filters. The available filters are album, artist, track, year, upc, tag:hipster, tag:new, isrc, and genre. Each field filter only applies to certain result types.
91+
* @param query Search query keywords and optional field filters and operators (filters and operators can be provided in [filters]). You can narrow down your search using field filters. The available filters are album, artist, track, year, upc, tag:hipster, tag:new, isrc, and genre. Each field filter only applies to certain result types.
9092
91-
The artist filter can be used while searching albums, artists or tracks.
92-
The album and year filters can be used while searching albums or tracks. You can filter on a single year or a range (e.g. 1955-1960).
93-
The genre filter can be use while searching tracks and artists.
94-
The isrc and track filters can be used while searching tracks.
95-
The upc, tag:new and tag:hipster filters can only be used while searching albums. The tag:new filter will return albums released in the past two weeks and tag:hipster can be used to return only albums with the lowest 10% popularity.
93+
The artist filter can be used while searching albums, artists or tracks.
94+
The album and year filters can be used while searching albums or tracks. You can filter on a single year or a range (e.g. 1955-1960).
95+
The genre filter can be use while searching tracks and artists.
96+
The isrc and track filters can be used while searching tracks.
97+
The upc, tag:new and tag:hipster filters can only be used while searching albums. The tag:new filter will return albums released in the past two weeks and tag:hipster can be used to return only albums with the lowest 10% popularity.
9698
97-
You can also use the NOT operator to exclude keywords from your search.
99+
You can also use the NOT operator to exclude keywords from your search.
98100
99-
Example value:
100-
"remaster%20track:Doxy+artist:Miles%20Davis"
101+
Example value:
102+
"remaster%20track:Doxy+artist:Miles%20Davis"
103+
* @param filters Optional list of [SearchFilter] to apply to this search.
101104
* @param searchTypes A list of item types to search across. Search results include hits from all the specified item types.
102105
* @param limit Maximum number of results to return.
103-
Default: 20
104-
Minimum: 1
105-
Maximum: 50
106-
Note: The limit is applied within each type, not on the total response.
107-
For example, if the limit value is 3 and the type is artist,album, the response contains 3 artists and 3 albums.
106+
Default: 20
107+
Minimum: 1
108+
Maximum: 50
109+
Note: The limit is applied within each type, not on the total response.
110+
For example, if the limit value is 3 and the type is artist,album, the response contains 3 artists and 3 albums.
108111
* @param offset The index of the first result to return.
109-
Default: 0 (the first result).
110-
Maximum offset (including limit): 10,000.
111-
Use with limit to get the next page of search results.
112+
Default: 0 (the first result).
113+
Maximum offset (including limit): 10,00.
114+
Use with limit to get the next page of search results.
112115
* @param market If a country code is specified, only artists, albums, and tracks with content that is playable in that market is returned. Note:
113-
- Playlist results are not affected by the market parameter.
114-
- If market is set to from_token, and a valid access token is specified in the request header, only content playable in the country associated with the user account, is returned.
115-
- Users can view the country that is associated with their account in the account settings. A user must grant access to the [SpotifyScope.USER_READ_PRIVATE] scope prior to when the access token is issued.
116+
- Playlist results are not affected by the market parameter.
117+
- If market is set to from_token, and a valid access token is specified in the request header, only content playable in the country associated with the user account, is returned.
118+
- Users can view the country that is associated with their account in the account settings. A user must grant access to the [SpotifyScope.USER_READ_PRIVATE] scope prior to when the access token is issued.
116119
**Note**: episodes will not be returned if this is NOT specified
117120
* @param includeExternal If true, the response will include any relevant audio content that is hosted externally. By default external content is filtered out from responses.
118121
*
@@ -121,6 +124,7 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
121124
public suspend fun search(
122125
query: String,
123126
vararg searchTypes: SearchType,
127+
filters: List<SearchFilter> = listOf(),
124128
limit: Int? = api.spotifyApiOptions.defaultLimit,
125129
offset: Int? = null,
126130
market: Market? = null,
@@ -131,7 +135,8 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
131135
requireNotNull(market) { "Market must be provided when SearchType.EPISODE is requested" }
132136
}
133137

134-
val jsonString = get(build(query, market, limit, offset, *searchTypes, includeExternal = includeExternal))
138+
val jsonString =
139+
get(build(query, market, limit, offset, filters, *searchTypes, includeExternal = includeExternal))
135140
val map = json.decodeFromString(MapSerializer(String.serializer(), JsonObject.serializer()), jsonString)
136141

137142
return SpotifySearchResult(
@@ -151,7 +156,8 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
151156
*
152157
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
153158
*
154-
* @param query Search query keywords and optional field filters and operators.
159+
* @param query Search query keywords *without filters*.
160+
* @param filters Optional list of [SearchFilter] to apply to this search.
155161
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
156162
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
157163
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -163,18 +169,20 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
163169
*/
164170
public suspend fun searchPlaylist(
165171
query: String,
172+
filters: List<SearchFilter> = listOf(),
166173
limit: Int? = api.spotifyApiOptions.defaultLimit,
167174
offset: Int? = null,
168175
market: Market? = null
169-
): PagingObject<SimplePlaylist> = get(build(query, market, limit, offset, SearchType.PLAYLIST))
176+
): PagingObject<SimplePlaylist> = get(build(query, market, limit, offset, filters, SearchType.PLAYLIST))
170177
.toNonNullablePagingObject(SimplePlaylist.serializer(), "playlists", api, json)
171178

172179
/**
173180
* Get Spotify Catalog information about artists that match the keyword string. See [SearchApi.search] for more information
174181
*
175182
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
176183
*
177-
* @param query Search query keywords and optional field filters and operators.
184+
* @param query Search query keywords *without filters*.
185+
* @param filters Optional list of [SearchFilter] to apply to this search.
178186
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
179187
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
180188
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -187,18 +195,20 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
187195
*/
188196
public suspend fun searchArtist(
189197
query: String,
198+
filters: List<SearchFilter> = listOf(),
190199
limit: Int? = api.spotifyApiOptions.defaultLimit,
191200
offset: Int? = null,
192201
market: Market? = null
193-
): PagingObject<Artist> = get(build(query, market, limit, offset, SearchType.ARTIST))
202+
): PagingObject<Artist> = get(build(query, market, limit, offset, filters, SearchType.ARTIST))
194203
.toNonNullablePagingObject(Artist.serializer(), "artists", api, json)
195204

196205
/**
197206
* Get Spotify Catalog information about albums that match the keyword string. See [SearchApi.search] for more information
198207
*
199208
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
200209
*
201-
* @param query Search query keywords and optional field filters and operators.
210+
* @param query Search query keywords *without filters*.
211+
* @param filters Optional list of [SearchFilter] to apply to this search.
202212
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
203213
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
204214
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -211,18 +221,20 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
211221
*/
212222
public suspend fun searchAlbum(
213223
query: String,
224+
filters: List<SearchFilter> = listOf(),
214225
limit: Int? = api.spotifyApiOptions.defaultLimit,
215226
offset: Int? = null,
216227
market: Market? = null
217-
): PagingObject<SimpleAlbum> = get(build(query, market, limit, offset, SearchType.ALBUM))
228+
): PagingObject<SimpleAlbum> = get(build(query, market, limit, offset, filters, SearchType.ALBUM))
218229
.toNonNullablePagingObject(SimpleAlbum.serializer(), "albums", api, json)
219230

220231
/**
221232
* Get Spotify Catalog information about tracks that match the keyword string. See [SearchApi.search] for more information
222233
*
223234
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
224235
*
225-
* @param query Search query keywords and optional field filters and operators.
236+
* @param query Search query keywords *without filters*.
237+
* @param filters Optional list of [SearchFilter] to apply to this search.
226238
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
227239
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
228240
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -235,18 +247,20 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
235247
*/
236248
public suspend fun searchTrack(
237249
query: String,
250+
filters: List<SearchFilter> = listOf(),
238251
limit: Int? = api.spotifyApiOptions.defaultLimit,
239252
offset: Int? = null,
240253
market: Market? = null
241-
): PagingObject<Track> = get(build(query, market, limit, offset, SearchType.TRACK))
254+
): PagingObject<Track> = get(build(query, market, limit, offset, filters, SearchType.TRACK))
242255
.toNonNullablePagingObject(Track.serializer(), "tracks", api, json)
243256

244257
/**
245258
* Get Spotify Catalog information about shows that match the keyword string. See [SearchApi.search] for more information
246259
*
247260
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
248261
*
249-
* @param query Search query keywords and optional field filters and operators.
262+
* @param query Search query keywords *without filters*.
263+
* @param filters Optional list of [SearchFilter] to apply to this search.
250264
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
251265
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
252266
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -259,18 +273,20 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
259273
*/
260274
public suspend fun searchShow(
261275
query: String,
276+
filters: List<SearchFilter> = listOf(),
262277
limit: Int? = api.spotifyApiOptions.defaultLimit,
263278
offset: Int? = null,
264279
market: Market
265-
): PagingObject<SimpleShow> = get(build(query, market, limit, offset, SearchType.SHOW))
280+
): PagingObject<SimpleShow> = get(build(query, market, limit, offset, filters, SearchType.SHOW))
266281
.toNonNullablePagingObject(SimpleShow.serializer(), "shows", api, json)
267282

268283
/**
269284
* Get Spotify Catalog information about episodes that match the keyword string. See [SearchApi.search] for more information
270285
*
271286
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
272287
*
273-
* @param query Search query keywords and optional field filters and operators.
288+
* @param query Search query keywords *without filters*.
289+
* @param filters Optional list of [SearchFilter] to apply to this search.
274290
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
275291
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
276292
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -283,18 +299,20 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
283299
*/
284300
public suspend fun searchEpisode(
285301
query: String,
302+
filters: List<SearchFilter> = listOf(),
286303
limit: Int? = api.spotifyApiOptions.defaultLimit,
287304
offset: Int? = null,
288305
market: Market
289-
): PagingObject<SimpleEpisode> = get(build(query, market, limit, offset, SearchType.EPISODE))
306+
): PagingObject<SimpleEpisode> = get(build(query, market, limit, offset, filters, SearchType.EPISODE))
290307
.toNonNullablePagingObject(SimpleEpisode.serializer(), "episodes", api, json)
291308

292309
/**
293310
* Get Spotify Catalog information about any searchable type that match the keyword string. See [SearchApi.search] for more information
294311
*
295312
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
296313
*
297-
* @param query Search query keywords and optional field filters and operators.
314+
* @param query Search query keywords *without filters*.
315+
* @param filters Optional list of [SearchFilter] to apply to this search.
298316
* @param market Provide this parameter if you want to apply [Track Relinking](https://github.com/adamint/spotify-web-api-kotlin#track-relinking)
299317
* @param limit The number of objects to return. Default: 50 (or api limit). Minimum: 1. Maximum: 50.
300318
* @param offset The index of the first item to return. Default: 0. Use with limit to get the next set of items
@@ -305,20 +323,28 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
305323
*/
306324
public suspend fun searchAllTypes(
307325
query: String,
326+
filters: List<SearchFilter> = listOf(),
308327
limit: Int? = api.spotifyApiOptions.defaultLimit,
309328
offset: Int? = null,
310329
market: Market
311-
): SpotifySearchResult = search(query, *SearchType.values(), limit = limit, offset = offset, market = market)
330+
): SpotifySearchResult =
331+
search(query, filters = filters, searchTypes = SearchType.values(), limit = limit, offset = offset, market = market)
312332

313333
protected fun build(
314334
query: String,
315335
market: Market?,
316336
limit: Int?,
317337
offset: Int?,
338+
filters: List<SearchFilter> = listOf(),
318339
vararg types: SearchType,
319340
includeExternal: Boolean? = null
320341
): String {
321-
return endpointBuilder("/search").with("q", query.encodeUrl()).with("type", types.joinToString(",") { it.id })
342+
val queryString = if (filters.isEmpty()) query
343+
else "$query ${filters.joinToString(" ") { "${it.filterType.id}:${it.filterValue}" }}"
344+
345+
return endpointBuilder("/search")
346+
.with("q", queryString.encodeUrl())
347+
.with("type", types.joinToString(",") { it.id })
322348
.with("market", market?.name).with("limit", limit).with("offset", offset)
323349
.with("include_external", if (includeExternal == true) "audio" else null).toString()
324350
}

src/commonMain/kotlin/com.adamratzman.spotify/models/LocalTracks.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public data class LocalTrack(
8484
limit: Int? = null,
8585
offset: Int? = null,
8686
market: Market? = null
87-
): PagingObject<Track> = api.search.searchTrack(name, limit, offset, market)
87+
): PagingObject<Track> = api.search.searchTrack(name, limit = limit, offset = offset, market = market)
8888

8989
/**
9090
* Search for this local track by name in Spotify's track catalog.

src/commonMain/kotlin/com.adamratzman.spotify/models/SpotifySearchResult.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,29 @@ package com.adamratzman.spotify.models
33

44
import kotlinx.serialization.Serializable
55

6+
/**
7+
* Available filters that Spotify allows in search, in addition to filtering by object type.
8+
*/
9+
public enum class SearchFilterType(public val id: String) {
10+
Album("album"),
11+
Artist("artist"),
12+
Track("track"),
13+
Year("year"),
14+
Upc("upc"),
15+
Hipster("tag:hipster"),
16+
New("tag:new"),
17+
Isrc("isrc"),
18+
Genre("genre")
19+
}
20+
21+
/**
22+
* A filter of type [SearchFilterType]. Should be unique by type.
23+
*
24+
* @param filterValue A string to match, or in the case of [SearchFilterType.Year] can be a range of years in the form
25+
* A-B. Example: 2000-2010
26+
*/
27+
public data class SearchFilter(val filterType: SearchFilterType, val filterValue: String)
28+
629
@Serializable
730
public data class SpotifySearchResult(
831
val albums: PagingObject<SimpleAlbum>? = null,

0 commit comments

Comments
 (0)