@@ -20,13 +20,15 @@ import com.adamratzman.spotify.http.HttpHeader
20
20
import com.adamratzman.spotify.http.HttpRequestMethod
21
21
import com.adamratzman.spotify.http.SpotifyEndpoint
22
22
import com.adamratzman.spotify.http.byteEncode
23
+ import com.adamratzman.spotify.models.AuthenticationError
24
+ import com.adamratzman.spotify.models.BadRequestException
23
25
import com.adamratzman.spotify.models.Token
24
26
import com.adamratzman.spotify.models.serialization.getAlbumConverter
25
27
import com.adamratzman.spotify.models.serialization.getFeaturedPlaylistsConverter
26
28
import com.adamratzman.spotify.models.serialization.getPlaylistConverter
27
29
import com.adamratzman.spotify.models.serialization.getPublicUserConverter
28
30
import com.adamratzman.spotify.models.serialization.getSavedTrackConverter
29
- import com.adamratzman.spotify.models.serialization.toObjectNullable
31
+ import com.adamratzman.spotify.models.serialization.toObject
30
32
import com.beust.klaxon.Klaxon
31
33
import java.util.concurrent.Executors
32
34
import java.util.concurrent.ScheduledFuture
@@ -59,7 +61,8 @@ abstract class SpotifyAPI internal constructor(
59
61
val clientId : String ,
60
62
val clientSecret : String ,
61
63
var token : Token ,
62
- useCache : Boolean
64
+ useCache : Boolean ,
65
+ var automaticRefresh : Boolean
63
66
) {
64
67
private var refreshFuture: ScheduledFuture <* >? = null
65
68
@@ -72,7 +75,7 @@ abstract class SpotifyAPI internal constructor(
72
75
}
73
76
74
77
internal var expireTime = System .currentTimeMillis() + token.expiresIn * 1000
75
- internal val executor = Executors .newScheduledThreadPool(2 )
78
+ internal val executor = Executors .newScheduledThreadPool(0 )
76
79
77
80
abstract val search: SearchAPI
78
81
abstract val albums: AlbumAPI
@@ -91,9 +94,10 @@ abstract class SpotifyAPI internal constructor(
91
94
* If the method used to create the [token] supports token refresh and
92
95
* the information in [token] is accurate, attempt to refresh the token
93
96
*
94
- * @return The old access token if refresh was successful, otherwise null
97
+ * @return The old access token if refresh was successful
98
+ * @throws BadRequestException if refresh fails
95
99
*/
96
- abstract fun refreshToken (): Token ?
100
+ abstract fun refreshToken (): Token
97
101
98
102
/* *
99
103
* If the cache is enabled, clear all stored queries in the cache
@@ -147,8 +151,12 @@ abstract class SpotifyAPI internal constructor(
147
151
* An API instance created with application credentials, not through
148
152
* client authentication
149
153
*/
150
- class SpotifyAppAPI internal constructor(clientId : String , clientSecret : String , token : Token , useCache : Boolean ) :
151
- SpotifyAPI (clientId, clientSecret, token, useCache) {
154
+ class SpotifyAppAPI internal constructor(clientId : String ,
155
+ clientSecret : String ,
156
+ token : Token ,
157
+ useCache : Boolean ,
158
+ automaticRefresh : Boolean ) :
159
+ SpotifyAPI (clientId, clientSecret, token, useCache, automaticRefresh) {
152
160
153
161
override val search: SearchAPI = SearchAPI (this )
154
162
override val albums: AlbumAPI = AlbumAPI (this )
@@ -173,22 +181,16 @@ class SpotifyAppAPI internal constructor(clientId: String, clientSecret: String,
173
181
174
182
override val klaxon: Klaxon = getKlaxon(this )
175
183
176
- init {
177
- if (clientId == " not-set" || clientSecret == " not-set" ) {
178
- logger.logWarning(" Token refresh is disabled - application parameters not set" )
179
- }
180
- }
181
-
182
- override fun refreshToken (): Token ? {
184
+ override fun refreshToken (): Token {
183
185
if (clientId != " not-set" && clientSecret != " not-set" ) {
184
186
val currentToken = this .token
185
187
186
- getCredentialedToken(clientId, clientSecret)?. let { token = it }
188
+ token = getCredentialedToken(clientId, clientSecret)
187
189
expireTime = System .currentTimeMillis() + token.expiresIn * 1000
188
190
189
191
return currentToken
190
192
}
191
- return null
193
+ throw BadRequestException ( " Either the client id or the client secret is not set " )
192
194
}
193
195
194
196
override fun clearCache () = clearAllCaches(
@@ -225,7 +227,7 @@ class SpotifyClientAPI internal constructor(
225
227
automaticRefresh : Boolean = false ,
226
228
var redirectUri : String ,
227
229
useCache : Boolean
228
- ) : SpotifyAPI(clientId, clientSecret, token, useCache) {
230
+ ) : SpotifyAPI(clientId, clientSecret, token, useCache, automaticRefresh ) {
229
231
override val search: SearchAPI = SearchAPI (this )
230
232
override val albums: AlbumAPI = AlbumAPI (this )
231
233
override val browse: BrowseAPI = BrowseAPI (this )
@@ -285,57 +287,37 @@ class SpotifyClientAPI internal constructor(
285
287
val userId: String
286
288
287
289
init {
288
- init (automaticRefresh)
289
290
userId = users.getUserProfile().complete().id
290
291
}
291
292
292
- private fun init (automaticRefresh : Boolean ) {
293
- if (automaticRefresh) {
294
- if (clientId != " not-set" && clientSecret != " not-set" && redirectUri != " not-set" ) {
295
- if (token.expiresIn > 60 ) {
296
- executor.scheduleAtFixedRate(
297
- { refreshToken() },
298
- (token.expiresIn - 30 ).toLong(),
299
- (token.expiresIn - 30 ).toLong(),
300
- TimeUnit .SECONDS
301
- )
302
- } else {
303
- refreshToken()
304
- init (automaticRefresh)
305
- }
306
- } else logger.logWarning(" Automatic refresh unavailable - client parameters not set" )
307
- }
308
- }
309
-
310
293
/* *
311
294
* Stop all automatic functions like refreshToken or clearCache and shut down the scheduled
312
295
* executor
313
296
* */
314
297
fun cancelAutomatics () = executor.shutdown()
315
298
316
- override fun refreshToken (): Token ? {
299
+ override fun refreshToken (): Token {
317
300
val currentToken = this .token
318
301
319
- val tempToken =
302
+ val response =
320
303
HttpConnection (
321
304
url = " https://accounts.spotify.com/api/token" ,
322
305
method = HttpRequestMethod .POST ,
323
306
body = " grant_type=refresh_token&refresh_token=${token.refreshToken ? : " " } " ,
324
307
contentType = " application/x-www-form-urlencoded" ,
325
308
api = this
326
- ).execute(HttpHeader (" Authorization" , " Basic ${" $clientId :$clientSecret " .byteEncode()} " )).body
327
- .toObjectNullable<Token >(null )
328
- return if (tempToken?.accessToken == null ) {
329
- logger.logWarning(" Spotify token refresh failed" )
330
- null
331
- } else {
309
+ ).execute(HttpHeader (" Authorization" , " Basic ${" $clientId :$clientSecret " .byteEncode()} " ))
310
+
311
+ if (response.responseCode / 200 == 1 ) {
312
+ val tempToken = response.body.toObject<Token >(this )
332
313
this .token = tempToken.copy(
333
314
refreshToken = tempToken.refreshToken ? : this .token.refreshToken,
334
315
scopes = tempToken.scopes
335
316
)
336
317
logger.logInfo(" Successfully refreshed the Spotify token" )
337
- currentToken
318
+ return currentToken
338
319
}
320
+ else throw BadRequestException (response.body.toObject<AuthenticationError >(this ))
339
321
}
340
322
341
323
override fun clearCache () = clearAllCaches(
@@ -378,22 +360,26 @@ class SpotifyClientAPI internal constructor(
378
360
}
379
361
}
380
362
381
- fun getAuthUrlFull (vararg scopes : SpotifyScope , clientId : String , redirectUri : String ): String {
363
+ fun getAuthUrlFull (vararg scopes : SpotifyScope , clientId : String , redirectUri : String ): String {
382
364
return " https://accounts.spotify.com/authorize/?client_id=$clientId " +
383
365
" &response_type=code" +
384
366
" &redirect_uri=$redirectUri " +
385
367
if (scopes.isEmpty()) " " else " &scope=${scopes.joinToString(" %20" ) { it.uri }} "
386
368
}
387
369
388
- fun getCredentialedToken (clientId : String , clientSecret : String ) =
389
- HttpConnection (
390
- url = " https://accounts.spotify.com/api/token" ,
391
- method = HttpRequestMethod .POST ,
392
- body = " grant_type=client_credentials" ,
393
- contentType = " application/x-www-form-urlencoded" ,
394
- api = null
395
- ).execute(HttpHeader (" Authorization" , " Basic ${" $clientId :$clientSecret " .byteEncode()} " )).body
396
- .toObjectNullable<Token >(null )
370
+ fun getCredentialedToken (clientId : String , clientSecret : String ): Token {
371
+ val response = HttpConnection (
372
+ url = " https://accounts.spotify.com/api/token" ,
373
+ method = HttpRequestMethod .POST ,
374
+ body = " grant_type=client_credentials" ,
375
+ contentType = " application/x-www-form-urlencoded" ,
376
+ api = null
377
+ ).execute(HttpHeader (" Authorization" , " Basic ${" $clientId :$clientSecret " .byteEncode()} " ))
378
+
379
+ if (response.responseCode / 200 == 1 ) return response.body.toObject(null )
380
+
381
+ throw BadRequestException (response.body.toObject<AuthenticationError >(null ))
382
+ }
397
383
398
384
private fun getKlaxon (api : SpotifyAPI ) = Klaxon ()
399
385
.converter(getFeaturedPlaylistsConverter(api))
0 commit comments