adamint / spotify-web-api-kotlin

Spotify Web API wrapper for Kotlin, Java, JS, and Native - Targets JVM, Android, JS (browser), Native (Desktop), and Apple tvOS/iOS. Includes a Spotify Web Playback SDK wrapper for Kotlin/JS, and a spotify-auth wrapper for Kotlin/Android.
https://adamint.github.io/spotify-web-api-kotlin-docs/
MIT License
194 stars 21 forks source link

Player getCurrentContext 204 not handled #305

Closed Nielssg closed 2 years ago

Nielssg commented 2 years ago

Describe the bug If you try to get the current player context, but the user is not playing anything for some time, the API will respond with a HTTP 204 status (API doc). The problem is that this library expects to parse its response, which does not exist because of the 204 status

To Reproduce Steps to reproduce the behavior: Use the getCurrentContext endpoint to retrieve the player state (spotifyClientAPI?.player?.getCurrentContext()) when you don't have Spotify open or haven't played for a while. It will return status 204 with an empty body. The full url which will be called by default is https://api.spotify.com/v1/me/player?additional_types=track%2Cepisode`

Expected behavior That's actually a good question, normally you would expect some kind of result, maybe return null of something to indicate that it cannot be retrieved.

Additional context Error log:

W/System.err: com.adamratzman.spotify.SpotifyException$ParseException: Unable to parse  (Expected start of the object '{', but had 'EOF' instead
W/System.err: JSON input: )
W/System.err:     at com.adamratzman.spotify.models.serialization.SerializationUtilsKt.parseJson(SerializationUtils.kt:33)
W/System.err:     at com.adamratzman.spotify.models.serialization.SerializationUtilsKt.toObject(SerializationUtils.kt:41)
W/System.err:     at com.adamratzman.spotify.endpoints.client.ClientPlayerApi.getCurrentContext(ClientPlayerApi.kt:102)
W/System.err:     at com.adamratzman.spotify.endpoints.client.ClientPlayerApi$getCurrentContext$1.invokeSuspend(Unknown Source:15)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
W/System.err:     at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
W/System.err:     at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
W/System.err: Caused by: kotlinx.serialization.json.internal.JsonDecodingException: Expected start of the object '{', but had 'EOF' instead
W/System.err: JSON input: 
W/System.err:     at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
W/System.err:     at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
W/System.err:     at kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:514)
W/System.err:     at kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:219)
W/System.err:     at kotlinx.serialization.json.internal.AbstractJsonLexer.unexpectedToken(AbstractJsonLexer.kt:202)
W/System.err:     at kotlinx.serialization.json.internal.StringJsonLexer.consumeNextToken(StringJsonLexer.kt:72)
W/System.err:     at kotlinx.serialization.json.internal.StreamingJsonDecoder.beginStructure(StreamingJsonDecoder.kt:40)
W/System.err:     at com.adamratzman.spotify.models.CurrentlyPlayingContext$$serializer.deserialize(Player.kt:107)
W/System.err:     at com.adamratzman.spotify.models.CurrentlyPlayingContext$$serializer.deserialize(Player.kt:107)
W/System.err:     at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:59)
W/System.err:     at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:35)
W/System.err:     at kotlinx.serialization.json.Json.decodeFromString(Json.kt:100)
W/System.err:     at com.adamratzman.spotify.models.serialization.SerializationUtilsKt$toObject$1.invoke(SerializationUtils.kt:42)
W/System.err:     at com.adamratzman.spotify.models.serialization.SerializationUtilsKt$toObject$1.invoke(SerializationUtils.kt:41)
W/System.err:     at com.adamratzman.spotify.models.serialization.SerializationUtilsKt.parseJson(SerializationUtils.kt:31)
W/System.err:   ... 32 more
adamint commented 2 years ago

This is going to be an interesting problem, thanks for reporting. The correct behavior should be to return null and this should be handled in Endpoint. I will investigate what specifically needs to change

adamint commented 2 years ago

Endpoints.execute should return null with code 204. Nullable request methods will be created as-needed (we just need a get for this)