Skip to content

Commit 6694c46

Browse files
authored
Merge pull request #307 from adamint/dev/204-fix-player-getcontext-204
add getNullable method for 204-available methods to use, update dependencies
2 parents e741279 + db5dec2 commit 6694c46

File tree

14 files changed

+118
-149
lines changed

14 files changed

+118
-149
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ jobs:
1919
steps:
2020
- name: Check out repo
2121
uses: actions/checkout@v2
22+
- name: Install java 11
23+
uses: actions/setup-java@v2
24+
with:
25+
distribution: 'adopt'
26+
java-version: '11'
2227
- name: Install curl
2328
run: sudo apt-get install -y curl libcurl4-openssl-dev
2429
- name: Test secret
@@ -44,29 +49,16 @@ jobs:
4449
steps:
4550
- name: Check out repo
4651
uses: actions/checkout@v2
52+
- name: Install java 11
53+
uses: actions/setup-java@v2
54+
with:
55+
distribution: 'adopt'
56+
java-version: '11'
4757
- name: Test mac
4858
run: gradle macosX64Test
4959
- name: Archive test results
5060
uses: actions/upload-artifact@v2
5161
with:
5262
name: code-coverage-report
5363
path: build/reports
54-
if: always()
55-
test_windows:
56-
runs-on: windows-latest
57-
environment: testing
58-
env:
59-
SPOTIFY_CLIENT_ID: ${{ secrets.SPOTIFY_CLIENT_ID }}
60-
SPOTIFY_CLIENT_SECRET: ${{ secrets.SPOTIFY_CLIENT_SECRET }}
61-
steps:
62-
- name: Check out repo
63-
uses: actions/checkout@v2
64-
- name: Test windows
65-
run: gradle mingwX64Test
66-
- name: Archive test results
67-
uses: actions/upload-artifact@v2
68-
with:
69-
name: code-coverage-report
70-
path: build/reports
71-
if: always()
72-
64+
if: always()

.github/workflows/release.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ jobs:
2828
steps:
2929
- name: Check out repo
3030
uses: actions/checkout@v2
31+
- name: Install java 11
32+
uses: actions/setup-java@v2
33+
with:
34+
distribution: 'adopt'
35+
java-version: '11'
3136
- name: Install curl
3237
run: sudo apt-get install -y curl libcurl4-openssl-dev
3338
- name: Verify Android
@@ -49,6 +54,11 @@ jobs:
4954
steps:
5055
- name: Check out repo
5156
uses: actions/checkout@v2
57+
- name: Install java 11
58+
uses: actions/setup-java@v2
59+
with:
60+
distribution: 'adopt'
61+
java-version: '11'
5262
- name: Publish macOS/tvOS/iOS
5363
run: gradle publishMacosX64PublicationToNexusRepository publishIosX64PublicationToNexusRepository publishTvosX64PublicationToNexusRepository
5464
release_windows:
@@ -58,6 +68,12 @@ jobs:
5868
steps:
5969
- name: Check out repo
6070
uses: actions/checkout@v2
71+
- name: Install java 11
72+
uses: actions/setup-java@v2
73+
with:
74+
distribution: 'adopt'
75+
java-version: '11'
76+
- run: choco install curl
6177
- name: Publish windows
6278
run: gradle publishMingwX64PublicationToNexusRepository
6379
release_docs:
@@ -66,6 +82,11 @@ jobs:
6682
steps:
6783
- name: Check out repo
6884
uses: actions/checkout@v2
85+
- name: Install java 11
86+
uses: actions/setup-java@v2
87+
with:
88+
distribution: 'adopt'
89+
java-version: '11'
6990
- name: Build docs
7091
run: gradle dokkaHtml
7192
- name: Push docs to docs repo

build.gradle.kts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackOutput.Target
55

66
plugins {
77
id("lt.petuska.npm.publish") version "1.1.2"
8-
kotlin("multiplatform") version "1.5.31"
8+
kotlin("multiplatform")
99
`maven-publish`
1010
signing
1111
id("io.codearte.nexus-staging") version "0.30.0"
1212
id("com.android.library")
13-
kotlin("plugin.serialization") version "1.5.31"
14-
id("com.diffplug.spotless") version "5.14.2"
13+
kotlin("plugin.serialization")
14+
id("com.diffplug.spotless") version "6.3.0"
1515
id("com.moowork.node") version "1.3.1"
16-
id("org.jetbrains.dokka") version "1.5.0"
16+
id("org.jetbrains.dokka")
1717
}
1818

1919
repositories {
@@ -27,8 +27,8 @@ buildscript {
2727
mavenCentral()
2828
}
2929
dependencies {
30-
classpath("com.android.tools.build:gradle:4.1.3")
31-
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
30+
classpath("com.android.tools.build:gradle:")
31+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:")
3232
}
3333
}
3434

@@ -68,8 +68,9 @@ android {
6868
testOptions {
6969
this.unitTests.isReturnDefaultValues = true
7070
}
71-
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
72-
71+
//sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
72+
sourceSets["main"].setRoot("src/androidMain")
73+
sourceSets["test"].setRoot("src/androidTest")
7374
/*sourceSets {
7475
getByName("main") {
7576
manifest.srcFile("src/androidMain/AndroidManifest.xml")
@@ -121,8 +122,7 @@ kotlin {
121122

122123
}
123124

124-
val irOnlyJs = project.hasProperty("irOnly")
125-
js(if (irOnlyJs) KotlinJsCompilerType.IR else KotlinJsCompilerType.BOTH) {
125+
js(KotlinJsCompilerType.IR) {
126126

127127
mavenPublication {
128128
setupPom(artifactId)
@@ -151,7 +151,7 @@ kotlin {
151151
}
152152
}*/
153153

154-
if (irOnlyJs) binaries.executable()
154+
binaries.executable()
155155
}
156156

157157
// val hostOs = System.getProperty("os.name")
@@ -217,13 +217,13 @@ kotlin {
217217
val kotlinxDatetimeVersion = "0.3.1"
218218

219219
sourceSets {
220-
val serializationVersion = "1.3.0"
221-
val ktorVersion = "1.6.3"
220+
val serializationVersion = "1.3.2"
221+
val ktorVersion = "1.6.8"
222222
val korlibsVersion = "2.2.0"
223223
val sparkVersion = "2.9.3"
224-
val androidSpotifyAuthVersion = "1.2.3"
224+
val androidSpotifyAuthVersion = "1.2.5"
225225
val androidCryptoVersion = "1.0.0"
226-
val coroutineMTVersion = "1.5.2-native-mt"
226+
val coroutineMTVersion = "1.6.0-native-mt"
227227

228228
val commonMain by getting {
229229
dependencies {

gradle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ org.gradle.jvmargs=-Xmx8000m
1818
kotlin.native.enableDependencyPropagation=false
1919
android.useAndroidX=true
2020
android.enableJetifier=true
21-
kotlin.mpp.enableGranularSourceSetsMetadata=true
21+
kotlin.mpp.enableGranularSourceSetsMetadata=true
22+
kotlinVersion=1.6.10
23+
androidBuildToolsVersion=7.0.0
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

settings.gradle.kts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
pluginManagement {
2-
val mainKotlinVersion = "1.5.0"
2+
val kotlinVersion: String by settings
3+
val androidBuildToolsVersion: String by settings
4+
5+
plugins {
6+
id("org.jetbrains.kotlin.multiplatform").version(kotlinVersion)
7+
id("org.jetbrains.kotlin.plugin.serialization").version(kotlinVersion)
8+
id("org.jetbrains.dokka").version(kotlinVersion)
9+
}
310

411
resolutionStrategy {
512
eachPlugin {
613
if (requested.id.id == "kotlin-multiplatform") {
7-
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$mainKotlinVersion")
14+
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
815
}
916
if (requested.id.id == "org.jetbrains.kotlin.jvm") {
10-
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$mainKotlinVersion")
17+
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
1118
}
1219
if (requested.id.id == "kotlinx-serialization") {
13-
useModule("org.jetbrains.kotlin:kotlin-serialization:$mainKotlinVersion")
14-
} else if (requested.id.namespace == "com.android") {
15-
useModule("com.android.tools.build:gradle:3.5.4")
20+
useModule("org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion")
21+
} else if (requested.id.id == "com.android.library") {
22+
useModule("com.android.tools.build:gradle:$androidBuildToolsVersion")
1623
} else if (requested.id.id == "kotlin-android-extensions") {
17-
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$mainKotlinVersion")
24+
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
1825
}
1926
}
2027
}
@@ -27,7 +34,4 @@ pluginManagement {
2734
}
2835
}
2936

30-
rootProject.name = "spotify-web-api-kotlin"
31-
include("java-interop-basic-sample")
32-
findProject(":java-interop-basic-sample")?.name = "java-interop-basic"
33-
include("java-interop-sample")
37+
rootProject.name = "spotify-web-api-kotlin"

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,12 @@ public class ClientPlayerApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
9393
requireScopes(SpotifyScope.USER_READ_PLAYBACK_STATE)
9494

9595
val obj = catch {
96-
get(
96+
getNullable(
9797
endpointBuilder("/me/player")
9898
.with("additional_types", additionalTypes.joinToString(",") { it.identifier })
9999
.with("market", market?.name)
100100
.toString()
101-
)
102-
.toObject(CurrentlyPlayingContext.serializer(), api, json)
101+
)?.toObject(CurrentlyPlayingContext.serializer(), api, json)
103102
}
104103
return if (obj?.timestamp == null) null else obj
105104
}

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/pub/SearchApi.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,18 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
8787
*
8888
* **[Api Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)**
8989
*
90-
* @param query Search query keywords and optional field filters and operators.
90+
* @param query Search query keywords and optional field filters and operators. You can narrow down your search using field filters. The available filters are album, artist, track, year, upc, tag:hipster, tag:new, isrc, and genre. Each field filter only applies to certain result types.
91+
92+
The artist filter can be used while searching albums, artists or tracks.
93+
The album and year filters can be used while searching albums or tracks. You can filter on a single year or a range (e.g. 1955-1960).
94+
The genre filter can be use while searching tracks and artists.
95+
The isrc and track filters can be used while searching tracks.
96+
The upc, tag:new and tag:hipster filters can only be used while searching albums. The tag:new filter will return albums released in the past two weeks and tag:hipster can be used to return only albums with the lowest 10% popularity.
97+
98+
You can also use the NOT operator to exclude keywords from your search.
99+
100+
Example value:
101+
"remaster%20track:Doxy+artist:Miles%20Davis"
91102
* @param searchTypes A list of item types to search across. Search results include hits from all the specified item types.
92103
* @param limit Maximum number of results to return.
93104
Default: 20
@@ -103,7 +114,10 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
103114
- Playlist results are not affected by the market parameter.
104115
- If market is set to from_token, and a valid access token is specified in the request header, only content playable in the country associated with the user account, is returned.
105116
- Users can view the country that is associated with their account in the account settings. A user must grant access to the [SpotifyScope.USER_READ_PRIVATE] scope prior to when the access token is issued.
117+
**Note**: episodes will not be returned if this is NOT specified
106118
* @param includeExternal If true, the response will include any relevant audio content that is hosted externally. By default external content is filtered out from responses.
119+
*
120+
* @throws IllegalArgumentException if no search types are provided, or if [SearchType.EPISODE] is provided but [market] is not
107121
*/
108122
public suspend fun search(
109123
query: String,
@@ -114,6 +128,10 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
114128
includeExternal: Boolean? = null
115129
): SpotifySearchResult {
116130
require(searchTypes.isNotEmpty()) { "At least one search type must be provided" }
131+
if (SearchType.EPISODE in searchTypes) {
132+
requireNotNull(market) { "Market must be provided when SearchType.EPISODE is requested"}
133+
}
134+
117135
val jsonString = get(build(query, market, limit, offset, *searchTypes, includeExternal = includeExternal))
118136
val map = json.decodeFromString(MapSerializer(String.serializer(), JsonObject.serializer()), jsonString)
119137

@@ -184,6 +202,8 @@ public open class SearchApi(api: GenericSpotifyApi) : SpotifyEndpoint(api) {
184202
- Playlist results are not affected by the market parameter.
185203
- If market is set to from_token, and a valid access token is specified in the request header, only content playable in the country associated with the user account, is returned.
186204
- Users can view the country that is associated with their account in the account settings. A user must grant access to the [SpotifyScope.USER_READ_PRIVATE] scope prior to when the access token is issued.
205+
206+
**Note**: episodes will not be returned if this is NOT specified
187207
* @param includeExternal If true, the response will include any relevant audio content that is hosted externally. By default external content is filtered out from responses.
188208
*/
189209
public fun searchRestAction(

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.adamratzman.spotify.models.ErrorResponse
1111
import com.adamratzman.spotify.models.serialization.toObject
1212
import com.adamratzman.spotify.utils.ConcurrentHashMap
1313
import com.adamratzman.spotify.utils.getCurrentTimeMs
14+
import io.ktor.http.HttpStatusCode
1415
import kotlin.math.ceil
1516
import kotlinx.coroutines.CancellationException
1617
import kotlinx.coroutines.async
@@ -76,26 +77,30 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
7677

7778

7879
internal suspend fun get(url: String): String {
79-
return execute(url)
80+
return execute<String>(url)
81+
}
82+
83+
internal suspend fun getNullable(url: String): String? {
84+
return execute<String?>(url)
8085
}
8186

8287
internal suspend fun post(url: String, body: String? = null, contentType: String? = null): String {
83-
return execute(url, body, HttpRequestMethod.POST, contentType = contentType)
88+
return execute<String>(url, body, HttpRequestMethod.POST, contentType = contentType)
8489
}
8590

8691
internal suspend fun put(url: String, body: String? = null, contentType: String? = null): String {
87-
return execute(url, body, HttpRequestMethod.PUT, contentType = contentType)
92+
return execute<String>(url, body, HttpRequestMethod.PUT, contentType = contentType)
8893
}
8994

9095
internal suspend fun delete(
9196
url: String,
9297
body: String? = null,
9398
contentType: String? = null
9499
): String {
95-
return execute(url, body, HttpRequestMethod.DELETE, contentType = contentType)
100+
return execute<String>(url, body, HttpRequestMethod.DELETE, contentType = contentType)
96101
}
97102

98-
private suspend fun execute(
103+
private suspend fun <T: String?> execute(
99104
url: String,
100105
body: String? = null,
101106
method: HttpRequestMethod = HttpRequestMethod.GET,
@@ -117,7 +122,7 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
117122
}
118123

119124
try {
120-
return withTimeout(api.spotifyApiOptions.requestTimeoutMillis ?: 100 * 1000L) {
125+
return withTimeout(api.spotifyApiOptions.requestTimeoutMillis ?: (100 * 1000L)) {
121126
try {
122127
val document = createConnection(url, body, method, contentType).execute(
123128
additionalHeaders = cacheState?.eTag?.let {
@@ -126,7 +131,7 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
126131
retryIfInternalServerErrorLeft = api.spotifyApiOptions.retryOnInternalServerErrorTimes
127132
)
128133

129-
handleResponse(document, cacheState, spotifyRequest, retry202) ?: execute(
134+
handleResponse(document, cacheState, spotifyRequest, retry202) ?: execute<T>(
130135
url,
131136
body,
132137
method,
@@ -137,7 +142,7 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
137142
if (e.statusCode == 401 && !attemptedRefresh) {
138143
api.refreshToken()
139144

140-
execute(
145+
execute<T>(
141146
url,
142147
body,
143148
method,
@@ -165,10 +170,13 @@ public abstract class SpotifyEndpoint(public val api: GenericSpotifyApi) {
165170
): String? {
166171
val statusCode = document.responseCode
167172

168-
if (statusCode == HttpConnectionStatus.HTTP_NOT_MODIFIED.code) {
173+
if (statusCode == HttpStatusCode.NotModified.value) {
169174
requireNotNull(cacheState?.eTag) { "304 status only allowed on Etag-able endpoints" }
170175
return cacheState?.data
171176
}
177+
else if (statusCode == HttpStatusCode.NoContent.value) {
178+
return null
179+
}
172180

173181
val responseBody = document.body
174182

0 commit comments

Comments
 (0)