Skip to content

Commit e53fdf4

Browse files
authored
Merge pull request #239 from adamint/dev
native support
2 parents 18aa9b0 + 813ac14 commit e53fdf4

File tree

44 files changed

+672
-339
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+672
-339
lines changed

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ repositories {
4242
implementation("com.adamratzman:spotify-api-kotlin-core:3.4.03")
4343
```
4444

45-
Note that images and profiles are not supported on the Kotlin/JS target.
46-
4745
### Android
4846
**If you declare any release types not named debug or release, you may see "Could not resolve com.adamratzman:spotify-api-kotlin-android:VERSION". You need to do the following for each release type not named debug or release:**
4947
```
@@ -80,6 +78,17 @@ If you have a question, you can:
8078
2. Join our [Discord server](https://discord.gg/G6vqP3S)
8179
3. Contact me using **Adam#9261** on [Discord](https://discordapp.com)
8280

81+
## Unsupported features on each platform:
82+
| Feature | JVM | Android | JS | Native (Mac/Windows/Linux) |
83+
|-----------------------------|--------------------|--------------------|--------------------|----------------------------|
84+
| Images (Playlist covers) | :heavy_check_mark: | :heavy_check_mark: | Unsupported | Unsupported |
85+
| getSpotifyPkceCodeChallenge | :heavy_check_mark: | :heavy_check_mark: | Unsupported | Unsupported |
86+
| Edit client playlist | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Unsupported |
87+
| Remove playlist tracks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Unsupported |
88+
89+
Please feel free to open an issue/discussion on GitHub or Discord if you need access to one of these features
90+
or have an interest in implementing one, as direction can be provided.
91+
8392
## Creating a new api instance
8493
To decide which api you need (SpotifyAppApi, SpotifyClientApi, SpotifyImplicitGrantApi), you can refer
8594
to the sections below or the [Spotify authorization guide](https://developer.spotify.com/documentation/general/guides/authorization-guide/). In general:

build.gradle.kts

Lines changed: 108 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ plugins {
66
`maven-publish`
77
signing
88
id("io.codearte.nexus-staging") version "0.22.0"
9+
id("com.android.library")
910
kotlin("multiplatform") version "1.4.21"
1011
kotlin("plugin.serialization") version "1.4.21"
1112
id("com.diffplug.gradle.spotless") version "4.4.0"
1213
id("com.moowork.node") version "1.3.1"
1314
id("org.jetbrains.dokka") version "1.4.20"
14-
id("com.android.library")
1515
id("kotlin-android-extensions")
1616
}
1717

@@ -32,7 +32,7 @@ buildscript {
3232
}
3333

3434
group = "com.adamratzman"
35-
version = "3.4.03"
35+
version = "3.5.0-rc.1"
3636

3737
tasks.withType<Test> {
3838
this.testLogging {
@@ -94,7 +94,6 @@ kotlin {
9494
}
9595

9696
mavenPublication {
97-
artifactId = "spotify-api-kotlin-android"
9897
setupPom(artifactId)
9998
}
10099

@@ -112,14 +111,12 @@ kotlin {
112111
}
113112

114113
mavenPublication {
115-
artifactId = "spotify-api-kotlin"
116114
setupPom(artifactId)
117115
}
118116
}
119117

120118
js(KotlinJsCompilerType.LEGACY) {
121119
mavenPublication {
122-
artifactId = "spotify-api-kotlin-js"
123120
setupPom(artifactId)
124121
}
125122

@@ -146,19 +143,83 @@ kotlin {
146143
}
147144
}
148145

146+
val hostOs = System.getProperty("os.name")
147+
val isMainHost = hostOs.contains("mac", true)
148+
//val isMainPlatform =
149+
val isMingwX64 = hostOs.startsWith("Windows")
150+
151+
macosX64 {
152+
mavenPublication {
153+
setupPom(artifactId)
154+
}
155+
}
156+
linuxX64 {
157+
mavenPublication {
158+
setupPom(artifactId)
159+
}
160+
}
161+
mingwX64 {
162+
mavenPublication {
163+
setupPom(artifactId)
164+
}
165+
}
166+
167+
val publicationsFromMainHost =
168+
listOf(jvm(), js()).map { it.name } + "kotlinMultiplatform"
169+
170+
publishing {
171+
publications {
172+
matching { it.name in publicationsFromMainHost }.all {
173+
val targetPublication = this@all
174+
tasks.withType<AbstractPublishToMaven>()
175+
.matching { it.publication == targetPublication }
176+
.configureEach { onlyIf { findProperty("isMainHost") == "true" } }
177+
}
178+
179+
val kotlinMultiplatform by getting(MavenPublication::class) {
180+
artifactId = "spotify-api-kotlin-core"
181+
setupPom(artifactId)
182+
}
183+
184+
/*val metadata by getting(MavenPublication::class) {
185+
artifactId = "spotify-api-kotlin-metadata"
186+
setupPom(artifactId)
187+
}*/
188+
}
189+
190+
repositories {
191+
maven {
192+
name = "nexus"
193+
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
194+
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
195+
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
196+
197+
credentials {
198+
val nexusUsername: String? by project.extra
199+
val nexusPassword: String? by project.extra
200+
username = nexusUsername
201+
password = nexusPassword
202+
}
203+
}
204+
}
205+
}
206+
149207
targets {
150208
sourceSets {
151-
val coroutineVersion = "1.4.2"
209+
val coroutineVersion = "1.4.2-native-mt"
152210
val serializationVersion = "1.0.1"
153211
val ktorVersion = "1.4.1"
212+
val kotlinxDatetimeVersion = "0.1.1"
154213

155214
val commonMain by getting {
156215
dependencies {
157216
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
158217
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
159218
implementation("io.ktor:ktor-client-core:$ktorVersion")
219+
implementation("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDatetimeVersion")
160220
}
161221
}
222+
162223
val commonTest by getting {
163224
dependencies {
164225
implementation(kotlin("test-common"))
@@ -221,47 +282,57 @@ kotlin {
221282
}
222283
}
223284

224-
all {
225-
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
285+
val desktopMain by creating {
286+
dependsOn(commonMain)
287+
288+
dependencies {
289+
implementation("io.ktor:ktor-client-curl:$ktorVersion")
290+
}
226291
}
227-
}
228-
}
229-
}
230292

231-
publishing {
232-
publications {
233-
val kotlinMultiplatform by getting(MavenPublication::class) {
234-
artifactId = "spotify-api-kotlin-core"
235-
setupPom(artifactId)
236-
}
293+
val desktopTest by creating {
294+
dependencies {
237295

238-
val metadata by getting(MavenPublication::class) {
239-
artifactId = "spotify-api-kotlin-metadata"
240-
setupPom(artifactId)
241-
}
242-
}
296+
}
297+
}
243298

244-
repositories {
245-
maven {
246-
name = "nexus"
247-
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
248-
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
249-
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
250-
251-
credentials {
252-
val nexusUsername: String? by project.extra
253-
val nexusPassword: String? by project.extra
254-
username = nexusUsername
255-
password = nexusPassword
299+
val linuxX64Main by getting {
300+
dependsOn(desktopMain)
301+
}
302+
303+
val linuxX64Test by getting {
304+
dependsOn(desktopTest)
305+
}
306+
307+
val mingwX64Main by getting {
308+
dependsOn(desktopMain)
309+
}
310+
311+
val mingwX64Test by getting {
312+
dependsOn(desktopTest)
313+
}
314+
315+
val macosX64Main by getting {
316+
dependsOn(desktopMain)
317+
}
318+
319+
val macosX64Test by getting {
320+
dependsOn(desktopTest)
321+
}
322+
323+
all {
324+
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
256325
}
257326
}
258327
}
259328
}
260329

330+
261331
signing {
262332
if (project.hasProperty("signing.keyId")
263-
&& project.hasProperty("signing.password")
264-
&& project.hasProperty("signing.secretKeyRingFile")) {
333+
&& project.hasProperty("signing.password")
334+
&& project.hasProperty("signing.secretKeyRingFile")
335+
) {
265336
sign(publishing.publications)
266337
}
267338
}
@@ -333,3 +404,4 @@ fun MavenPublication.setupPom(publicationName: String) {
333404
}
334405
}
335406
}
407+

gradle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
systemProp.org.gradle.internal.publish.checksums.insecure=true
22
org.gradle.daemon=true
3-
org.gradle.jvmargs=-Xmx4000m
3+
org.gradle.jvmargs=-Xmx8000m
4+
kotlin.mpp.enableGranularSourceSetsMetadata=true
5+
kotlin.native.enableDependencyPropagation=false

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ pluginManagement {
2828
}
2929
}
3030

31-
rootProject.name = "spotify-web-api-kotlin"
31+
rootProject.name = "spotify-api-kotlin"
3232

src/androidMain/kotlin/com/adamratzman/spotify/http/Endpoints.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ internal actual fun String.encodeUrl() = URLEncoder.encode(this, "UTF-8")!!
1111
internal actual fun String.base64ByteEncode(): String {
1212
return if (VERSION.SDK_INT >= VERSION_CODES.O) {
1313
java.util.Base64.getUrlEncoder()
14-
.withoutPadding()
15-
.encodeToString(toByteArray())
14+
.withoutPadding()
15+
.encodeToString(toByteArray())
1616
} else {
1717
Base64.encodeToString(toByteArray(), Base64.DEFAULT)
1818
}
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
/* Spotify Web API, Kotlin Wrapper; MIT License, 2017-2021; Original author: Adam Ratzman */
22
package com.adamratzman.spotify.utils
33

4-
import android.annotation.SuppressLint
5-
import java.text.SimpleDateFormat
6-
import java.util.Date
7-
84
public actual fun getCurrentTimeMs(): Long = System.currentTimeMillis()
9-
10-
@SuppressLint("SimpleDateFormat")
11-
internal actual fun formatDate(format: String, date: Long): String {
12-
return SimpleDateFormat(format).format(Date(date))
13-
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,8 @@ public class SpotifyUserAuthorization(
846846
* @param requiredScopes Scopes that your application requires to function (only applicable to [SpotifyClientApi] and [SpotifyImplicitGrantApi]).
847847
* @param proxyBaseUrl Provide if you have a proxy base URL that you would like to use instead of the Spotify API base
848848
* (https://api.spotify.com/v1).
849+
* @param retryIfInternalServerError Whether to retry once if an internal server error (500..599) has been received
850+
*
849851
*/
850852
public data class SpotifyApiOptions(
851853
public var useCache: Boolean = true,
@@ -861,5 +863,6 @@ public data class SpotifyApiOptions(
861863
public var refreshTokenProducer: (suspend (GenericSpotifyApi) -> Token)? = null,
862864
public var onTokenRefresh: (suspend (GenericSpotifyApi) -> Unit)? = null,
863865
public var requiredScopes: List<SpotifyScope>? = null,
864-
public var proxyBaseUrl: String? = null
866+
public var proxyBaseUrl: String? = null,
867+
public var retryIfInternalServerError: Boolean = true
865868
)

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/public/BrowseApi.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ public class BrowseApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
101101
endpointBuilder("/browse/featured-playlists").with("limit", limit).with("offset", offset).with(
102102
"market",
103103
market?.name
104-
).with("locale", locale).with("timestamp", timestamp?.let {
105-
formatDate("yyyy-MM-dd'T'HH:mm:ss", it)
106-
}).toString()
104+
).with("locale", locale).with("timestamp", timestamp?.let { formatDate(it) }).toString()
107105
).toObject(FeaturedPlaylists.serializer(), api, json)
108106

109107
/**
@@ -183,7 +181,7 @@ public class BrowseApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
183181
limit
184182
).with("offset", offset)
185183
.with("market", market?.name).toString()
186-
).toPagingObject(SimplePlaylist.serializer(), "playlists", endpoint = this, json = json)
184+
).toPagingObject((SimplePlaylist.serializer()), "playlists", endpoint = this, json = json)
187185

188186
/**
189187
* Create a playlist-style listening experience based on seed artists, tracks and genres.

src/commonMain/kotlin/com.adamratzman.spotify/http/Endpoints.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
8787
return withTimeout(api.spotifyApiOptions.requestTimeoutMillis ?: 100 * 1000L) {
8888
try {
8989
val document = createConnection(url, body, method, contentType).execute(
90-
cacheState?.eTag?.let {
90+
additionalHeaders = cacheState?.eTag?.let {
9191
listOf(HttpHeader("If-None-Match", it))
92-
}
92+
},
93+
retryIfInternalServerError = api.spotifyApiOptions.retryIfInternalServerError
9394
)
9495

9596
handleResponse(document, cacheState, spotifyRequest, retry202) ?: execute(
@@ -100,7 +101,7 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
100101
contentType
101102
)
102103
} catch (e: BadRequestException) {
103-
if (e.statusCode?.equals(401) == true && !attemptedRefresh) {
104+
if (e.statusCode == 401 && !attemptedRefresh) {
104105
api.refreshToken()
105106

106107
execute(
@@ -147,7 +148,7 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
147148
}
148149
}
149150

150-
if (document.responseCode / 200 != 1 /* Check if status is not 2xx or 3xx */) {
151+
if (document.responseCode !in 200..399 /* Check if status is not 2xx or 3xx */) {
151152
val response = try {
152153
document.body.toObject(ErrorResponse.serializer(), api, api.spotifyApiOptions.json)
153154
} catch (e: Exception) {

0 commit comments

Comments
 (0)