Skip to content

Commit 8fc04c0

Browse files
committed
you can now upload cover image
1 parent 91e2d27 commit 8fc04c0

File tree

19 files changed

+122
-34
lines changed

19 files changed

+122
-34
lines changed

.idea/modules/SpotifyKotlinWrapper.iml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules/SpotifyKotlinWrapper_main.iml

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules/SpotifyKotlinWrapper_test.iml

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ To build it, you must pass the application id and secret.
4646
```kotlin
4747
val api = SpotifyAPI.Builder("application id", "application secret").build()
4848
```
49-
*Note:* You are **unable** to use any client endpoint.
49+
*Note:* You are **unable** to use any clientApi endpoint.
5050

5151
### SpotifyClientAPI
5252
All endpoints inside SpotifyAPI can be accessed within SpotifyClientAPI.
@@ -122,7 +122,7 @@ In both Track and SimpleTrack objects in an endpoint response, there is a nullab
122122
If the track is unable to be played in the specified market and there is an alternative that *is* playable, this
123123
will be populated with the href, uri, and, most importantly, the id of the track.
124124

125-
You can then use this track in client actions such as playing or saving the track, knowing that it will be playable
125+
You can then use this track in clientApi actions such as playing or saving the track, knowing that it will be playable
126126
in your market!
127127

128128
### Endpoint List

src/main/kotlin/com/adamratzman/spotify/endpoints/priv/playlists/UserPlaylistAPI.kt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ package com.adamratzman.spotify.endpoints.priv.playlists
33
import com.adamratzman.spotify.main.SpotifyAPI
44
import com.adamratzman.spotify.utils.*
55
import org.json.JSONObject
6+
import java.awt.image.BufferedImage
7+
import java.io.ByteArrayOutputStream
8+
import java.io.File
9+
import java.net.URL
610
import java.util.function.Supplier
11+
import javax.imageio.IIOException
12+
import javax.imageio.ImageIO
13+
import javax.xml.bind.DatatypeConverter
14+
715

816
/**
917
* Endpoints for retrieving information about a user’s playlists and for managing a user’s playlists.
@@ -146,5 +154,46 @@ class UserPlaylistAPI(api: SpotifyAPI) : SpotifyEndpoint(api) {
146154
})
147155
}
148156

157+
/**
158+
* Replace the image used to represent a specific playlist. Image type **must** be jpeg.
159+
*
160+
* Must specify a JPEG image path or image data, maximum payload size is 256 KB
161+
*
162+
* @param userId The user’s Spotify user ID.
163+
* @param playlistId The Spotify ID for the playlist.
164+
* @param imagePath Optionally specify the full local path to the image
165+
* @param imageUrl Optionally specify a URL to the image
166+
* @param imageFile Optionally specify the image [File]
167+
* @param image Optionally specify the image's [BufferedImage] object
168+
* @param imageData Optionally specify the Base64-encoded image data yourself
169+
*
170+
* @throws IIOException if the image is not found
171+
* @throws BadRequestException if invalid data is provided
172+
*/
173+
fun uploadPlaylistCover(userId: String, playlistId: String, imagePath: String? = null,
174+
imageFile: File? = null, image: BufferedImage? = null, imageData: String? = null,
175+
imageUrl: String? = null): SpotifyRestAction<Unit> {
176+
return toAction(Supplier {
177+
val data = imageData ?: when {
178+
image != null -> encode(image)
179+
imageFile != null -> encode(ImageIO.read(imageFile))
180+
imageUrl != null -> encode(ImageIO.read(URL(imageUrl)))
181+
imagePath != null -> encode(ImageIO.read(URL("file:///$imagePath")))
182+
else -> throw IllegalArgumentException("No cover image was specified")
183+
}
184+
println(data)
185+
put("https://api.spotify.com/v1/users/$userId/playlists/$playlistId/images",
186+
data, contentType = "image/jpeg")
187+
Unit
188+
})
189+
}
190+
191+
private fun encode(image: BufferedImage): String {
192+
val bos = ByteArrayOutputStream()
193+
ImageIO.write(image, "jpg", bos)
194+
bos.close()
195+
return DatatypeConverter.printBase64Binary(bos.toByteArray())
196+
}
197+
149198
data class Snapshot(val snapshot_id: String)
150199
}

src/main/kotlin/com/adamratzman/spotify/main/SpotifyAPI.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class SpotifyClientAPI private constructor(clientId: String, clientSecret: Strin
129129
enum class Scope(val uri: String) {
130130
PLAYLIST_READ_PRIVATE("playlist-read-private"),
131131
PLAYLIST_READ_COLLABORATIVE("playlist-read-collaborative"),
132-
PLAYLIST_MODIFY_PUBLIC("playlist-modify-pub"),
132+
PLAYLIST_MODIFY_PUBLIC("playlist-modify-public"),
133133
PLAYLIST_MODIFY_PRIVATE("playlist-modify-private"),
134134
UGC_IMAGE_UPLOAD("ugc-image-upload"),
135135
USER_FOLLOW_MODIFY("user-follow-modify"),

src/main/kotlin/com/adamratzman/spotify/utils/Helpers.kt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import java.io.InvalidObjectException
1010
import java.net.URLEncoder
1111
import java.util.*
1212
import java.util.function.Supplier
13-
import kotlin.collections.HashMap
1413

1514
abstract class SpotifyEndpoint(val api: SpotifyAPI) {
1615
fun get(url: String): String {
@@ -21,25 +20,28 @@ abstract class SpotifyEndpoint(val api: SpotifyAPI) {
2120
return execute(url, body, Connection.Method.POST)
2221
}
2322

24-
fun put(url: String, body: String? = null): String {
25-
return execute(url, body, Connection.Method.PUT)
23+
fun put(url: String, body: String? = null, contentType: String? = null): String {
24+
return execute(url, body, Connection.Method.PUT, contentType = contentType)
2625
}
2726

2827
fun delete(url: String, body: String? = null): String {
2928
return execute(url, body, Connection.Method.DELETE)
3029
}
3130

32-
private fun execute(url: String, body: String? = null, method: Connection.Method = Connection.Method.GET, retry202: Boolean = true): String {
31+
private fun execute(url: String, body: String? = null, method: Connection.Method = Connection.Method.GET, retry202: Boolean = true, contentType: String? = null): String {
3332
if (api !is SpotifyClientAPI && System.currentTimeMillis() >= api.expireTime) {
3433
api.refreshClient()
3534
api.expireTime = System.currentTimeMillis() + api.token.expires_in * 1000
3635
}
3736
var connection = Jsoup.connect(url).ignoreContentType(true)
37+
if (contentType != null) connection.header("Content-Type", contentType)
3838
if (body != null) {
39-
connection = if (method == Connection.Method.DELETE) {
40-
val key = JSONObject(body).keySet().toList()[0]
41-
connection.data(key, JSONObject(body).getJSONArray(key).toString())
42-
} else connection.requestBody(body)
39+
if (contentType != null) connection.requestBody(body)
40+
else
41+
connection = if (method == Connection.Method.DELETE) {
42+
val key = JSONObject(body).keySet().toList()[0]
43+
connection.data(key, JSONObject(body).getJSONArray(key).toString())
44+
} else connection.requestBody(body)
4345
}
4446
connection = connection.header("Authorization", "Bearer ${api.token.access_token}")
4547
val document = connection.ignoreHttpErrors(true).method(method).execute()
@@ -53,6 +55,7 @@ abstract class SpotifyEndpoint(val api: SpotifyAPI) {
5355

5456
data class CursorBasedPagingObject<out T>(val href: String, val items: List<T>, val limit: Int, val next: String?, val cursors: Cursor,
5557
val total: Int)
58+
5659
data class Cursor(val after: String)
5760
data class PagingObject<out T>(val href: String, val items: List<T>, val limit: Int, val next: String? = null, val offset: Int = 0, val previous: String? = null, val total: Int)
5861
data class PlaylistTrackPagingObject(val href: String, val items: List<PlaylistTrack>, val limit: Int, val next: String? = null, val offset: Int = 0, val previous: String? = null, val total: Int)
@@ -66,12 +69,14 @@ data class LinkedResult<out T>(val href: String, val items: List<T>) {
6669
}
6770
throw InvalidObjectException("This object is not linked to a playlist")
6871
}
72+
6973
fun getArtistId(): String {
7074
if (href.startsWith("https://api.spotify.com/v1/artists/")) {
7175
return href.removePrefix("https://api.spotify.com/v1/artists/").split("/")[0]
7276
}
7377
throw InvalidObjectException("This object is not linked to an artist")
7478
}
79+
7580
fun getAlbumId(): String {
7681
if (href.startsWith("https://api.spotify.com/v1/albums/")) {
7782
return href.removePrefix("https://api.spotify.com/v1/albums/").split("/")[0]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.adamratzman.spotify.kotlin.endpoints.priv.follow
22

3-
import com.adamratzman.spotify.main.clientApi
3+
import clientApi
44
import org.junit.Test
55

66
class UserFollowAPITest {

src/test/kotlin/com/adamratzman/spotify/endpoints/priv/personalization/PersonalizationAPITest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.adamratzman.spotify.kotlin.endpoints.priv.personalization
22

3-
import com.adamratzman.spotify.main.clientApi
3+
import clientApi
44
import org.junit.Test
55

66
internal class PersonalizationAPITest {

src/test/kotlin/com/adamratzman/spotify/endpoints/priv/player/PlayerAPITest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.adamratzman.spotify.kotlin.endpoints.priv.player
22

3+
import clientApi
34
import com.adamratzman.spotify.endpoints.priv.player.PlayerAPI
4-
import com.adamratzman.spotify.main.clientApi
55
import org.junit.Test
66

77
class PlayerAPITest {

0 commit comments

Comments
 (0)