Skip to content

Commit 9af231a

Browse files
committed
implement playback via playerapi
1 parent 0874ac5 commit 9af231a

File tree

6 files changed

+152
-13
lines changed

6 files changed

+152
-13
lines changed

src/main/kotlin/com/adamratzman/spotify/endpoints/priv/follow/UserFollowAPI.kt

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,120 @@ import java.util.function.Supplier
88
* These endpoints allow you manage the artists, users and playlists that a Spotify user follows.
99
*/
1010
class UserFollowAPI(api: SpotifyAPI) : SpotifyEndpoint(api) {
11-
fun followingUsers(vararg userIds: String): SpotifyRestAction<List<Boolean>> {
11+
/**
12+
* Check to see if the current user is following another Spotify users.
13+
*
14+
* @param userId Spotify ID to check.
15+
*
16+
* @throws BadRequestException if [userId] is a non-existing id
17+
*/
18+
fun isFollowingUser(userId: String): SpotifyRestAction<Boolean> {
1219
return toAction(Supplier {
13-
get("https://api.spotify.com/v1/me/following/contains?type=user&ids=${userIds.joinToString(",") { it.encode() }} { it.encode() }}").toObject<List<Boolean>>(api)
20+
isFollowingUsers(userId).complete()[0]
1421
})
1522
}
1623

17-
fun followingArtists(vararg userIds: String): SpotifyRestAction<List<Boolean>> {
24+
/**
25+
* Check to see if the current user is following one or more other Spotify users.
26+
*
27+
* @param userIds List of the user Spotify IDs to check. Max 50
28+
*
29+
* @throws BadRequestException if [userIds] contains a non-existing id
30+
*/
31+
fun isFollowingUsers(vararg userIds: String): SpotifyRestAction<List<Boolean>> {
1832
return toAction(Supplier {
19-
get("https://api.spotify.com/v1/me/following/contains?type=artist&ids=${userIds.joinToString(",") { it.encode() }}").toObject<List<Boolean>>(api)
33+
get("https://api.spotify.com/v1/me/following/contains?type=user&ids=${userIds.joinToString(",") { it.encode() }}").toObject<List<Boolean>>(api)
2034
})
2135
}
2236

37+
/**
38+
* Check to see if the current user is following a Spotify artist.
39+
*
40+
* @param artistId Spotify ID to check.
41+
*
42+
* @throws BadRequestException if [artistId] is a non-existing id
43+
*/
44+
fun isFollowingArtist(artistId: String): SpotifyRestAction<Boolean> {
45+
return toAction(Supplier {
46+
isFollowingArtists(artistId).complete()[0]
47+
})
48+
}
49+
50+
/**
51+
* Check to see if the current user is following one or more artists.
52+
*
53+
* @param artistIds List of the artist Spotify IDs to check. Max 50
54+
*
55+
* @throws BadRequestException if [artistIds] contains a non-existing id
56+
*/
57+
fun isFollowingArtists(vararg artistIds: String): SpotifyRestAction<List<Boolean>> {
58+
return toAction(Supplier {
59+
get("https://api.spotify.com/v1/me/following/contains?type=artist&ids=${artistIds.joinToString(",") { it.encode() }}").toObject<List<Boolean>>(api)
60+
})
61+
}
62+
63+
/**
64+
* Get the current user’s followed artists.
65+
*
66+
* @return [CursorBasedPagingObject] ([Information about them](https://github.com/adamint/spotify-web-api-kotlin/blob/master/README.md#the-benefits-of-linkedresults-pagingobjects-and-cursor-based-paging-objects)
67+
* with full [Artist] objects
68+
*/
2369
fun getFollowedArtists(): SpotifyRestAction<CursorBasedPagingObject<Artist>> {
2470
return toAction(Supplier {
2571
get("https://api.spotify.com/v1/me/following?type=artist").toCursorBasedPagingObject<Artist>("artists", api)
2672
})
2773
}
2874

75+
fun getFollowedUsers(): SpotifyRestAction<SpotifyPublicUser>
76+
= throw NotImplementedError("Though Spotify will implement this in the future, it is not currently supported.")
77+
78+
/**
79+
* Add the current user as a follower of another user
80+
*
81+
* @throws BadRequestException if an invalid id is provided
82+
*/
83+
fun followUser(userId: String): SpotifyRestAction<Unit> {
84+
return toAction(Supplier {
85+
followUsers(userId).complete()
86+
})
87+
}
88+
89+
/**
90+
* Add the current user as a follower of other users
91+
*
92+
* @throws BadRequestException if an invalid id is provided
93+
*/
2994
fun followUsers(vararg userIds: String): SpotifyRestAction<Unit> {
3095
return toAction(Supplier {
3196
put("https://api.spotify.com/v1/me/following?type=user&ids=${userIds.joinToString(",") { it.encode() }}")
3297
Unit
3398
})
3499
}
35100

101+
/**
102+
* Add the current user as a follower of an artist
103+
*
104+
* @throws BadRequestException if an invalid id is provided
105+
*/
106+
fun followArtist(artistId: String): SpotifyRestAction<Unit> {
107+
return toAction(Supplier {
108+
followArtists(artistId).complete()
109+
})
110+
}
111+
112+
/**
113+
* Add the current user as a follower of other artists
114+
*
115+
* @throws BadRequestException if an invalid id is provided
116+
*/
36117
fun followArtists(vararg artistIds: String): SpotifyRestAction<Unit> {
37118
return toAction(Supplier {
38119
put("https://api.spotify.com/v1/me/following?type=artist&ids=${artistIds.joinToString(",")}")
39120
Unit
40121
})
41122
}
42123

124+
43125
fun followPlaylist(ownerId: String, playlistId: String, followPublicly: Boolean = true): SpotifyRestAction<Unit> {
44126
return toAction(Supplier {
45127
put("https://api.spotify.com/v1/users/$ownerId/playlists/$playlistId/followers", "{\"public\": $followPublicly}")

src/main/kotlin/com/adamratzman/spotify/endpoints/priv/player/PlayerAPI.kt

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.adamratzman.spotify.endpoints.priv.player
22

33
import com.adamratzman.spotify.main.SpotifyAPI
44
import com.adamratzman.spotify.utils.*
5+
import org.json.JSONObject
56
import java.util.function.Supplier
67

78
/**
@@ -79,14 +80,53 @@ class PlayerAPI(api: SpotifyAPI) : SpotifyEndpoint(api) {
7980
})
8081
}
8182

82-
fun startPlayback(deviceId: String? = null): SpotifyRestAction<Unit> {
83+
/**
84+
* Start or resume playback.
85+
* **Note:** Only one of the following can be used: [albumId], [artistId], [playlist], or [tracksToPlay]. Else, you will
86+
* not see expected results.
87+
*
88+
* **Note also:** You can only use one of the following: [offsetNum] or [offsetTrackId]
89+
*
90+
* **Specify nothing to play to simply resume playback**
91+
*
92+
* @param albumId an album id to play
93+
* @param artistId an artist id for whom to play
94+
* @param playlist a playlist id from which to play
95+
* @param tracksToPlay track ids to play. these are converted into URIs. Max 100
96+
* @param offsetNum Indicates from where in the context playback should start. Only available with use of [albumId] or [playlist]
97+
* or when [tracksToPlay] is used.
98+
* @param offsetTrackId Does the same as [offsetNum] but with a track id instead of place number
99+
* @param deviceId the device to play on
100+
*
101+
* @throws BadRequestException if more than one type of play type is specified or the offset is illegal.
102+
*/
103+
fun startPlayback(albumId: String? = null, artistId: String? = null, playlist: PlaylistParams? = null,
104+
offsetNum: Int? = null, offsetTrackId: String? = null, deviceId: String? = null, vararg tracksToPlay: String): SpotifyRestAction<Unit> {
83105
return toAction(Supplier {
84-
put("https://api.spotify.com/v1/me/player/play${if (deviceId != null) "?device_id=${deviceId.encode()}" else ""}")
106+
val url = "https://api.spotify.com/v1/me/player/play${if (deviceId != null) "?device_id=${deviceId.encode()}" else ""}"
107+
val body = JSONObject()
108+
when {
109+
albumId != null -> body.put("context_uri", "spotify:album:$albumId")
110+
artistId != null -> body.put("context_uri", "spotify:artist:$artistId")
111+
playlist != null -> body.put("context_uri", "spotify:user:${playlist.author}:playlist:${playlist.id}")
112+
tracksToPlay.isNotEmpty() -> body.put("uris", tracksToPlay.map { "spotify:track:$it" })
113+
}
114+
if (body.keySet().isNotEmpty()) {
115+
if (offsetNum != null) body.put("offset", JSONObject().put("position", offsetNum))
116+
else if (offsetTrackId != null) body.put("offset", JSONObject().put("uri", "spotify:track:$offsetTrackId"))
117+
put(url, body.toString())
118+
}
119+
else put(url)
85120
Unit
86121
})
87122
}
88123

89-
fun resumePlayback(deviceId: String? = null) = startPlayback(deviceId)
124+
/**
125+
* Resumes playback on the current device, if [deviceId] is not specified.
126+
*
127+
* @param deviceId the device to play on
128+
*/
129+
fun resumePlayback(deviceId: String? = null) = startPlayback(deviceId = deviceId)
90130

91131
fun shufflePlayback(shuffle: Boolean = true, deviceId: String? = null): SpotifyRestAction<Unit> {
92132
return toAction(Supplier {

src/main/kotlin/com/adamratzman/spotify/endpoints/pub/browse/BrowseAPI.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class BrowseAPI(api: SpotifyAPI) : SpotifyEndpoint(api) {
153153
*/
154154
fun getRecommendations(seedArtists: List<String>? = null, seedGenres: List<String>? = null, seedTracks: List<String>? = null, limit: Int = 20,
155155
market: Market? = null, targetAttributes: HashMap<TuneableTrackAttribute, Number> = hashMapOf(),
156-
minAttributes: HashMap<TuneableTrackAttribute, Number>, maxAttributes: HashMap<TuneableTrackAttribute, Number>)
156+
minAttributes: HashMap<TuneableTrackAttribute, Number> = hashMapOf(), maxAttributes: HashMap<TuneableTrackAttribute, Number> = hashMapOf())
157157
: SpotifyRestAction<RecommendationResponse> {
158158
return toAction(Supplier {
159159
val url = StringBuilder("https://api.spotify.com/v1/recommendations?limit=$limit")

src/test/kotlin/com/adamratzman/spotify/endpoints/priv/follow/UserFollowAPITest.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,19 @@ import com.adamratzman.spotify.main.clientApi
44
import org.junit.Test
55

66
class UserFollowAPITest {
7+
@Test
8+
fun isFollowingUser() {
9+
clientApi.userFollowing.isFollowingUser("asdfasdfasdfsfasdfasdf").complete().let { println(it) }
10+
}
11+
12+
@Test
13+
fun isFollowingArtist() {
14+
println(clientApi.userFollowing.isFollowingArtist("4IS4EyXNmiI2w5SRCjMtEF").complete())
15+
}
16+
717
@Test
818
fun followingUsers() {
9-
println(clientApi.userFollowing.followingUsers("adamratzman").complete())
19+
println(clientApi.userFollowing.isFollowingUsers("adamratzman").complete())
1020
}
1121

1222
@Test
@@ -16,7 +26,7 @@ class UserFollowAPITest {
1626

1727
@Test
1828
fun followingArtists() {
19-
println(clientApi.userFollowing.followingArtists("7wjeXCtRND2ZdKfMJFu6JC").complete())
29+
println(clientApi.userFollowing.isFollowingArtists("7wjeXCtRND2ZdKfMJFu6JC").complete())
2030
}
2131

2232
@Test
@@ -26,7 +36,7 @@ class UserFollowAPITest {
2636

2737
@Test
2838
fun followArtists() {
29-
println(clientApi.userFollowing.followArtists("6rl53MP8HSoiugpqzA50Zh").complete())
39+
println(clientApi.userFollowing.followArtists("6rfl53MP8HSoiugpqzA50Zh").complete())
3040
}
3141

3242
@Test

src/test/kotlin/com/adamratzman/spotify/endpoints/pub/browse/BrowseAPITest.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.adamratzman.spotify.kotlin.endpoints.pub.browse
22

3+
import com.adamratzman.spotify.endpoints.pub.browse.TuneableTrackAttribute
34
import com.adamratzman.spotify.main.api
5+
import com.adamratzman.spotify.main.clientApi
46
import com.adamratzman.spotify.utils.Market
57
import junit.framework.TestCase
68

@@ -31,7 +33,12 @@ class BrowseAPITest : TestCase() {
3133
}
3234

3335
fun testGetRecommendations() {
34-
// println(api.browse.getRecommendations(seedArtists = listOf("3TVXtAsR1Inumwj472S9r4"), seedGenres = listOf("pop", "country"), targets = hashMapOf(Pair("speechiness", 1.0), Pair("danceability", 1.0))).complete())
36+
api.browse.getRecommendations(seedTracks = listOf("43ehiuXyqIdLyZ4eEH47nw"),
37+
targetAttributes = hashMapOf(Pair(TuneableTrackAttribute.DANCEABILITY, 1.0)),
38+
market = Market.FR)
39+
.complete().tracks.let {
40+
println(it.map { it.name })
41+
clientApi.player.startPlayback(tracksToPlay = *it.map { it.id }.toTypedArray()).complete() }
3542
}
3643

3744
}

src/test/kotlin/com/adamratzman/spotify/endpoints/pub/tracks/TracksAPITest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import junit.framework.TestCase
55

66
class TracksAPITest : TestCase() {
77
fun testGetTracks() {
8-
println(api.tracks.getTracks(null, "7cU84qjHFxJ39COxWltHyY", "4o4sj7dVrT51NKMyeG8T5y").complete())
8+
println(api.tracks.getTracks( "43ehiuXyqIdLyZ4eEH47nw", "4o4sj7dVrT51NKMyeG8T5y").complete().map { it?.name })
99
}
1010

1111
fun testGetAudioAnalysis() {

0 commit comments

Comments
 (0)