Skip to content

Commit 29f5ca8

Browse files
authored
Merge pull request #300 from adamint/dev
3.8.4 release
2 parents f0d3c84 + f691e61 commit 29f5ca8

File tree

16 files changed

+200
-78
lines changed

16 files changed

+200
-78
lines changed

TESTING.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ To run **only** public endpoint tests, you only need `SPOTIFY_CLIENT_ID` and `SP
99
To additionally run **all** private (client) endpoint tests, you need a valid Spotify application, redirect uri, and token string.
1010
The additional environment variables you will need to add are `SPOTIFY_REDIRECT_URI` and `SPOTIFY_TOKEN_STRING`.
1111

12+
To specifically run player tests, you must include the `SPOTIFY_ENABLE_PLAYER_TESTS`=true environment variable.
13+
1214
Some tests may fail if you do not allow access to all required scopes. To mitigate this, you can individually grant
1315
each scope or use the following code snippet to print out the Spotify token string (given a generated authorization code).
14-
However, you can painlessly generate a valid token by using the [spotify-web-api-token-helper](https://github.com/adamint/spotify-web-api-token-helper)
15-
library.
16-
16+
However, you can painlessly generate a valid token by using this site: https://adamratzman.com/projects/spotify/generate-token
1717

18-
To run tests, run `gradle check`
18+
To run tests, run `gradle jvmTest`, `gradle macosX64Test`, `gradle testDebugUnitTest`, or any other target.
1919

20+
To output all http requests to the console, set the `SPOTIFY_LOG_HTTP`=true environment variable.
2021

2122
To build the maven artifact locally, you will need to follow these steps:
2223
- Create `gradle.properties` if it doesn't exist already.
@@ -25,5 +26,5 @@ To build the maven artifact locally, you will need to follow these steps:
2526

2627
You can use this artifact to test locally by adding the `mavenLocal()` repository in any local gradle project.
2728

28-
To build docs, run `gradle dokkaHtml`. They will be located under the docs directory in the repostiory root, and
29+
To build docs, run `gradle dokka`. They will be located under the docs directory in the repostiory root, and
2930
are ignored. This is how we generate release docs.

build.gradle.kts

+11-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ plugins {
1414
id("com.diffplug.spotless") version "5.14.2"
1515
id("com.moowork.node") version "1.3.1"
1616
id("org.jetbrains.dokka") version "1.5.0"
17-
id("kotlin-android-extensions")
1817
}
1918

2019
repositories {
@@ -28,7 +27,7 @@ buildscript {
2827
mavenCentral()
2928
}
3029
dependencies {
31-
classpath("com.android.tools.build:gradle:4.1.0")
30+
classpath("com.android.tools.build:gradle:4.1.3")
3231
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
3332
}
3433
}
@@ -56,7 +55,7 @@ android {
5655
exclude("META-INF/*.md")
5756
}
5857
defaultConfig {
59-
minSdkVersion(15)
58+
minSdkVersion(23)
6059
targetSdkVersion(30)
6160
compileSdkVersion(30)
6261
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
@@ -69,7 +68,9 @@ android {
6968
testOptions {
7069
this.unitTests.isReturnDefaultValues = true
7170
}
72-
sourceSets {
71+
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
72+
73+
/*sourceSets {
7374
getByName("main") {
7475
manifest.srcFile("src/androidMain/AndroidManifest.xml")
7576
java.setSrcDirs(listOf("src/androidMain/kotlin"))
@@ -79,7 +80,7 @@ android {
7980
java.setSrcDirs(listOf("src/androidTest/kotlin"))
8081
res.setSrcDirs(listOf("src/androidTest/res"))
8182
}
82-
}
83+
}*/
8384
}
8485

8586
val dokkaJar by tasks.registering(Jar::class) {
@@ -213,16 +214,16 @@ kotlin {
213214
}
214215

215216
targets {
216-
val kotlinxDatetimeVersion = "0.2.1"
217+
val kotlinxDatetimeVersion = "0.3.1"
217218

218219
sourceSets {
219-
val serializationVersion = "1.2.2"
220-
val ktorVersion = "1.6.2"
220+
val serializationVersion = "1.3.0"
221+
val ktorVersion = "1.6.3"
221222
val korlibsVersion = "2.2.0"
222223
val sparkVersion = "2.9.3"
223224
val androidSpotifyAuthVersion = "1.2.3"
224225
val androidCryptoVersion = "1.0.0"
225-
val coroutineMTVersion = "1.5.1-native-mt"
226+
val coroutineMTVersion = "1.5.2-native-mt"
226227

227228
val commonMain by getting {
228229
dependencies {
@@ -394,7 +395,7 @@ kotlin {
394395
}*/
395396

396397
all {
397-
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
398+
languageSettings.optIn("kotlin.RequiresOptIn")
398399
}
399400
}
400401
}

src/androidMain/kotlin/com/adamratzman/spotify/auth/implicit/SpotifyImplicitLoginActivity.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ public interface SpotifyImplicitLoginActivity {
8585
if (requestCode == state) {
8686
val response = AuthorizationClient.getResponse(resultCode, intent)
8787
logToConsole("Got implicit auth response of ${response.type}")
88-
when (response.type) {
89-
AuthorizationResponse.Type.TOKEN -> {
88+
when {
89+
response.type == AuthorizationResponse.Type.TOKEN -> {
9090
val token = Token(
9191
response.accessToken,
9292
response.type.name,
@@ -102,11 +102,11 @@ public interface SpotifyImplicitLoginActivity {
102102
}
103103
// AuthorizationResponse.Type.CODE -> TODO()
104104
// AuthorizationResponse.Type.UNKNOWN -> TODO()
105-
AuthorizationResponse.Type.ERROR -> {
105+
response.type == AuthorizationResponse.Type.ERROR -> {
106106
logToConsole("Got error in authorization... executing error handler")
107107
onFailure(response.error ?: "Generic authentication error")
108108
}
109-
AuthorizationResponse.Type.EMPTY -> {
109+
response.type == AuthorizationResponse.Type.EMPTY -> {
110110
logToConsole("Got empty authorization... executing error handler")
111111
onFailure(response.error ?: "Authentication empty")
112112
}

src/commonJvmLikeMain/kotlin/com/adamratzman/spotify/utils/DateTimeUtils.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import kotlinx.datetime.Instant
66
/**
77
* The current time in milliseconds since UNIX epoch.
88
*/
9-
actual fun getCurrentTimeMs(): Long = System.currentTimeMillis()
9+
public actual fun getCurrentTimeMs(): Long = System.currentTimeMillis()
1010

1111
/**
1212
* Format date to ISO 8601 format

src/commonMain/kotlin/com.adamratzman.spotify/SpotifyRestAction.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import kotlin.coroutines.suspendCoroutine
1111
import kotlin.jvm.JvmOverloads
1212
import kotlinx.coroutines.CancellationException
1313
import kotlinx.coroutines.CoroutineScope
14+
import kotlinx.coroutines.DelicateCoroutinesApi
1415
import kotlinx.coroutines.Dispatchers
1516
import kotlinx.coroutines.GlobalScope
1617
import kotlinx.coroutines.delay
@@ -78,6 +79,7 @@ public open class SpotifyRestAction<T> internal constructor(public val supplier:
7879
* @param failure Consumer to invoke when an exception is thrown by [supplier]
7980
* @param consumer to be invoked with [T] after successful completion of [supplier]
8081
*/
82+
@OptIn(DelicateCoroutinesApi::class)
8183
@JvmOverloads
8284
public fun queue(failure: ((Throwable) -> Unit) = { throw it }, consumer: ((T) -> Unit) = {}) {
8385
hasRunBacking = true
@@ -100,6 +102,7 @@ public open class SpotifyRestAction<T> internal constructor(public val supplier:
100102
* @param timeUnit the unit that [quantity] is in
101103
* @param consumer to be invoked with [T] after successful completion of [supplier]
102104
*/
105+
@OptIn(DelicateCoroutinesApi::class)
103106
@JvmOverloads
104107
public fun queueAfter(
105108
quantity: Int,
@@ -109,7 +112,7 @@ public open class SpotifyRestAction<T> internal constructor(public val supplier:
109112
consumer: (T) -> Unit
110113
) {
111114
val runAt = getCurrentTimeMs() + timeUnit.toMillis(quantity.toLong())
112-
GlobalScope.launch {
115+
scope.launch {
113116
delay(getCurrentTimeMs() - runAt)
114117

115118
try {

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/client/ClientPlayerApi.kt

+75-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package com.adamratzman.spotify.endpoints.client
33

44
import com.adamratzman.spotify.GenericSpotifyApi
5+
import com.adamratzman.spotify.SpotifyAppApi
56
import com.adamratzman.spotify.SpotifyException
67
import com.adamratzman.spotify.SpotifyException.BadRequestException
78
import com.adamratzman.spotify.SpotifyRestAction
@@ -10,6 +11,7 @@ import com.adamratzman.spotify.http.SpotifyEndpoint
1011
import com.adamratzman.spotify.models.ContextUri
1112
import com.adamratzman.spotify.models.CurrentlyPlayingContext
1213
import com.adamratzman.spotify.models.CurrentlyPlayingObject
14+
import com.adamratzman.spotify.models.CurrentlyPlayingType
1315
import com.adamratzman.spotify.models.CursorBasedPagingObject
1416
import com.adamratzman.spotify.models.Device
1517
import com.adamratzman.spotify.models.PlayHistory
@@ -26,6 +28,7 @@ import com.adamratzman.spotify.models.toLocalTrackUri
2628
import com.adamratzman.spotify.models.toPlaylistUri
2729
import com.adamratzman.spotify.models.toShowUri
2830
import com.adamratzman.spotify.models.toTrackUri
31+
import com.adamratzman.spotify.utils.Market
2932
import com.adamratzman.spotify.utils.catch
3033
import com.adamratzman.spotify.utils.jsonMap
3134
import kotlinx.serialization.builtins.ListSerializer
@@ -73,12 +76,29 @@ public class ClientPlayerApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
7376
* **Requires** the [SpotifyScope.USER_READ_PLAYBACK_STATE] scope
7477
*
7578
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-information-about-the-users-current-playback/)**
76-
*/
77-
public suspend fun getCurrentContext(): CurrentlyPlayingContext? {
79+
*
80+
* @param additionalTypes A list of types to return in addition to [CurrentlyPlayingType.TRACK]. Ad type not allowed.
81+
* @param market If a country code is specified, only shows and episodes that are available in that market will be returned.
82+
* If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
83+
* Note: If neither market or user country are provided, the content is considered unavailable for the client.
84+
* Users can view the country that is associated with their account in the account settings. Required for [SpotifyAppApi], but **you may use [Market.FROM_TOKEN] to get the user market**
85+
*/
86+
public suspend fun getCurrentContext(
87+
additionalTypes: List<CurrentlyPlayingType> = listOf(
88+
CurrentlyPlayingType.TRACK,
89+
CurrentlyPlayingType.EPISODE
90+
),
91+
market: Market? = null
92+
): CurrentlyPlayingContext? {
7893
requireScopes(SpotifyScope.USER_READ_PLAYBACK_STATE)
7994

8095
val obj = catch {
81-
get(endpointBuilder("/me/player").toString())
96+
get(
97+
endpointBuilder("/me/player")
98+
.with("additional_types", additionalTypes.joinToString(",") { it.identifier })
99+
.with("market", market?.name)
100+
.toString()
101+
)
82102
.toObject(CurrentlyPlayingContext.serializer(), api, json)
83103
}
84104
return if (obj?.timestamp == null) null else obj
@@ -90,12 +110,24 @@ public class ClientPlayerApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
90110
* **Requires** the [SpotifyScope.USER_READ_PLAYBACK_STATE] scope
91111
*
92112
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-information-about-the-users-current-playback/)**
113+
*
114+
* @param additionalTypes A list of types to return in addition to [CurrentlyPlayingType.TRACK]. Ad type not allowed.
115+
* @param market If a country code is specified, only shows and episodes that are available in that market will be returned.
116+
* If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
117+
* Note: If neither market or user country are provided, the content is considered unavailable for the client.
118+
* Users can view the country that is associated with their account in the account settings. Required for [SpotifyAppApi], but **you may use [Market.FROM_TOKEN] to get the user market**
93119
*/
94-
public fun getCurrentContextRestAction(): SpotifyRestAction<CurrentlyPlayingContext?> =
95-
SpotifyRestAction { getCurrentContext() }
120+
public fun getCurrentContextRestAction(
121+
additionalTypes: List<CurrentlyPlayingType> = listOf(
122+
CurrentlyPlayingType.TRACK,
123+
CurrentlyPlayingType.EPISODE
124+
),
125+
market: Market? = null
126+
): SpotifyRestAction<CurrentlyPlayingContext?> =
127+
SpotifyRestAction { getCurrentContext(additionalTypes, market) }
96128

97129
/**
98-
* Get tracks from the current user’s recently played tracks.
130+
* Get tracks from the current user’s recently played tracks. Note: Currently doesn't support podcast episodes.
99131
*
100132
* **Requires** the [SpotifyScope.USER_READ_RECENTLY_PLAYED] scope
101133
*
@@ -120,7 +152,7 @@ public class ClientPlayerApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
120152
}
121153

122154
/**
123-
* Get tracks from the current user’s recently played tracks.
155+
* Get tracks from the current user’s recently played tracks. Note: Currently doesn't support podcast episodes.
124156
*
125157
* **Requires** the [SpotifyScope.USER_READ_RECENTLY_PLAYED] scope
126158
*
@@ -144,18 +176,36 @@ public class ClientPlayerApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
144176
* **Requires** *either* the [SpotifyScope.USER_READ_PLAYBACK_STATE] or [SpotifyScope.USER_READ_CURRENTLY_PLAYING] scopes
145177
*
146178
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-the-users-currently-playing-track/)**
147-
*/
148-
public suspend fun getCurrentlyPlaying(): CurrentlyPlayingObject? {
179+
*
180+
* @param additionalTypes A list of types to return in addition to [CurrentlyPlayingType.TRACK]. Ad type not allowed.
181+
* @param market If a country code is specified, only shows and episodes that are available in that market will be returned.
182+
* If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
183+
* Note: If neither market or user country are provided, the content is considered unavailable for the client.
184+
* Users can view the country that is associated with their account in the account settings. Required for [SpotifyAppApi], but **you may use [Market.FROM_TOKEN] to get the user market**
185+
*/
186+
public suspend fun getCurrentlyPlaying(
187+
additionalTypes: List<CurrentlyPlayingType> = listOf(
188+
CurrentlyPlayingType.TRACK,
189+
CurrentlyPlayingType.EPISODE
190+
),
191+
market: Market? = null
192+
): CurrentlyPlayingObject? {
149193
requireScopes(SpotifyScope.USER_READ_PLAYBACK_STATE, SpotifyScope.USER_READ_CURRENTLY_PLAYING, anyOf = true)
150194

151195
return try {
152196
val obj =
153197
catch {
154-
get(endpointBuilder("/me/player/currently-playing").toString())
198+
get(
199+
endpointBuilder("/me/player/currently-playing")
200+
.with("additional_types", additionalTypes.joinToString(",") { it.identifier })
201+
.with("market", market?.name)
202+
.toString()
203+
)
155204
.toObject(CurrentlyPlayingObject.serializer(), api, json)
156205
}
157206
if (obj?.timestamp == null) null else obj
158207
} catch (pe: SpotifyException.ParseException) {
208+
pe.printStackTrace()
159209
null
160210
}
161211
}
@@ -166,9 +216,21 @@ public class ClientPlayerApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
166216
* **Requires** *either* the [SpotifyScope.USER_READ_PLAYBACK_STATE] or [SpotifyScope.USER_READ_CURRENTLY_PLAYING] scopes
167217
*
168218
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-the-users-currently-playing-track/)**
169-
*/
170-
public fun getCurrentlyPlayingRestAction(): SpotifyRestAction<CurrentlyPlayingObject?> =
171-
SpotifyRestAction { getCurrentlyPlaying() }
219+
*
220+
* @param additionalTypes A list of types to return in addition to [CurrentlyPlayingType.TRACK]. Ad type not allowed.
221+
* @param market If a country code is specified, only shows and episodes that are available in that market will be returned.
222+
* If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
223+
* Note: If neither market or user country are provided, the content is considered unavailable for the client.
224+
* Users can view the country that is associated with their account in the account settings. Required for [SpotifyAppApi], but **you may use [Market.FROM_TOKEN] to get the user market**
225+
*/
226+
public fun getCurrentlyPlayingRestAction(
227+
additionalTypes: List<CurrentlyPlayingType> = listOf(
228+
CurrentlyPlayingType.TRACK,
229+
CurrentlyPlayingType.EPISODE
230+
),
231+
market: Market? = null
232+
): SpotifyRestAction<CurrentlyPlayingObject?> =
233+
SpotifyRestAction { getCurrentlyPlaying(additionalTypes, market) }
172234

173235
/**
174236
* Pause playback on the user’s account.

0 commit comments

Comments
 (0)