Skip to content

Commit a167862

Browse files
authored
Merge pull request #166 from adamint/dev
Dev
2 parents 353d288 + a958f24 commit a167862

File tree

6 files changed

+139
-115
lines changed

6 files changed

+139
-115
lines changed

build.gradle.kts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ plugins {
66
signing
77
`java-library`
88
id("io.codearte.nexus-staging") version "0.21.1"
9-
kotlin("multiplatform") version "1.3.50"
10-
kotlin("plugin.serialization") version "1.3.50"
9+
kotlin("multiplatform") version "1.3.61"
10+
kotlin("plugin.serialization") version "1.3.61"
1111
id("com.diffplug.gradle.spotless") version "3.26.0"
1212
id("com.moowork.node") version "1.3.1"
1313
id("org.jetbrains.dokka") version "0.10.0"
1414
}
1515

1616
group = "com.adamratzman"
17-
version = "3.0.0-rc.2"
17+
version = "3.0.0-rc.4"
1818

1919
java {
2020
withSourcesJar()
@@ -253,7 +253,10 @@ tasks {
253253
archives(javadocJar)
254254
}
255255

256-
"publish" {
257-
dependsOn(dokka)
256+
val publishJvm by registering(Task::class) {
257+
dependsOn.add(check)
258+
dependsOn.add(dokka)
259+
dependsOn.add("publishJvmPublicationToNexusRepository")
258260
}
261+
259262
}

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

Lines changed: 81 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.adamratzman.spotify.models.serialization.toObject
2424
import com.adamratzman.spotify.models.serialization.toPagingObject
2525
import com.adamratzman.spotify.utils.Market
2626
import com.adamratzman.spotify.utils.formatDate
27+
import kotlin.reflect.KClass
2728
import kotlinx.serialization.list
2829
import kotlinx.serialization.serializer
2930

@@ -41,9 +42,9 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
4142
fun getAvailableGenreSeeds(): SpotifyRestAction<List<String>> {
4243
return toAction {
4344
get(EndpointBuilder("/recommendations/available-genre-seeds").toString()).toInnerArray(
44-
String.serializer().list,
45-
"genres",
46-
json
45+
String.serializer().list,
46+
"genres",
47+
json
4748
)
4849
}
4950
}
@@ -66,10 +67,10 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
6667
): SpotifyRestActionPaging<SimpleAlbum, PagingObject<SimpleAlbum>> {
6768
return toActionPaging {
6869
get(
69-
EndpointBuilder("/browse/new-releases").with("limit", limit).with("offset", offset).with(
70-
"country",
71-
market?.name
72-
).toString()
70+
EndpointBuilder("/browse/new-releases").with("limit", limit).with("offset", offset).with(
71+
"country",
72+
market?.name
73+
).toString()
7374
).toPagingObject(SimpleAlbum.serializer(), "albums", endpoint = this, json = json)
7475
}
7576
}
@@ -101,12 +102,12 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
101102
): SpotifyRestAction<FeaturedPlaylists> {
102103
return toAction {
103104
get(
104-
EndpointBuilder("/browse/featured-playlists").with("limit", limit).with("offset", offset).with(
105-
"market",
106-
market?.name
107-
).with("locale", locale).with("timestamp", timestamp?.let {
108-
formatDate("yyyy-MM-dd'T'HH:mm:ss", it)
109-
}).toString()
105+
EndpointBuilder("/browse/featured-playlists").with("limit", limit).with("offset", offset).with(
106+
"market",
107+
market?.name
108+
).with("locale", locale).with("timestamp", timestamp?.let {
109+
formatDate("yyyy-MM-dd'T'HH:mm:ss", it)
110+
}).toString()
110111
).toObject(FeaturedPlaylists.serializer(), api, json)
111112
}
112113
}
@@ -134,10 +135,10 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
134135
): SpotifyRestActionPaging<SpotifyCategory, PagingObject<SpotifyCategory>> {
135136
return toActionPaging {
136137
get(
137-
EndpointBuilder("/browse/categories").with("limit", limit).with("offset", offset).with(
138-
"market",
139-
market?.name
140-
).with("locale", locale).toString()
138+
EndpointBuilder("/browse/categories").with("limit", limit).with("offset", offset).with(
139+
"market",
140+
market?.name
141+
).with("locale", locale).toString()
141142
).toPagingObject(SpotifyCategory.serializer(), "categories", endpoint = this, json = json)
142143
}
143144
}
@@ -162,8 +163,8 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
162163
): SpotifyRestAction<SpotifyCategory> {
163164
return toAction {
164165
get(
165-
EndpointBuilder("/browse/categories/${categoryId.encodeUrl()}").with("market", market?.name)
166-
.with("locale", locale).toString()
166+
EndpointBuilder("/browse/categories/${categoryId.encodeUrl()}").with("market", market?.name)
167+
.with("locale", locale).toString()
167168
).toObject(SpotifyCategory.serializer(), api, json)
168169
}
169170
}
@@ -187,11 +188,11 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
187188
): SpotifyRestActionPaging<SimplePlaylist, PagingObject<SimplePlaylist>> {
188189
return toActionPaging {
189190
get(
190-
EndpointBuilder("/browse/categories/${categoryId.encodeUrl()}/playlists").with(
191-
"limit",
192-
limit
193-
).with("offset", offset)
194-
.with("market", market?.name).toString()
191+
EndpointBuilder("/browse/categories/${categoryId.encodeUrl()}/playlists").with(
192+
"limit",
193+
limit
194+
).with("offset", offset)
195+
.with("market", market?.name).toString()
195196
).toPagingObject(SimplePlaylist.serializer(), "playlists", endpoint = this, json = json)
196197
}
197198
}
@@ -237,16 +238,16 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
237238
minAttributes: List<TrackAttribute<*>> = listOf(),
238239
maxAttributes: List<TrackAttribute<*>> = listOf()
239240
): SpotifyRestAction<RecommendationResponse> =
240-
getRecommendations(
241-
seedArtists,
242-
seedGenres,
243-
seedTracks,
244-
limit,
245-
market,
246-
targetAttributes.map { it.tuneableTrackAttribute to it.value }.toMap(),
247-
minAttributes.map { it.tuneableTrackAttribute to it.value }.toMap(),
248-
maxAttributes.map { it.tuneableTrackAttribute to it.value }.toMap()
249-
)
241+
getRecommendations(
242+
seedArtists,
243+
seedGenres,
244+
seedTracks,
245+
limit,
246+
market,
247+
targetAttributes.map { it.tuneableTrackAttribute to it.value }.toMap(),
248+
minAttributes.map { it.tuneableTrackAttribute to it.value }.toMap(),
249+
maxAttributes.map { it.tuneableTrackAttribute to it.value }.toMap()
250+
)
250251

251252
/**
252253
* Create a playlist-style listening experience based on seed artists, tracks and genres.
@@ -292,18 +293,18 @@ class BrowseApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
292293
): SpotifyRestAction<RecommendationResponse> {
293294
if (seedArtists?.isEmpty() != false && seedGenres?.isEmpty() != false && seedTracks?.isEmpty() != false) {
294295
throw SpotifyException.BadRequestException(
295-
ErrorObject(
296-
400,
297-
"At least one seed (genre, artist, track) must be provided."
298-
)
296+
ErrorObject(
297+
400,
298+
"At least one seed (genre, artist, track) must be provided."
299+
)
299300
)
300301
}
301302

302303
return toAction {
303304
val builder = EndpointBuilder("/recommendations").with("limit", limit).with("market", market?.name)
304-
.with("seed_artists", seedArtists?.joinToString(",") { ArtistUri(it).id.encodeUrl() })
305-
.with("seed_genres", seedGenres?.joinToString(",") { it.encodeUrl() })
306-
.with("seed_tracks", seedTracks?.joinToString(",") { TrackUri(it).id.encodeUrl() })
305+
.with("seed_artists", seedArtists?.joinToString(",") { ArtistUri(it).id.encodeUrl() })
306+
.with("seed_genres", seedGenres?.joinToString(",") { it.encodeUrl() })
307+
.with("seed_tracks", seedTracks?.joinToString(",") { TrackUri(it).id.encodeUrl() })
307308
targetAttributes.forEach { (attribute, value) -> builder.with("target_$attribute", value) }
308309
minAttributes.forEach { (attribute, value) -> builder.with("min_$attribute", value) }
309310
maxAttributes.forEach { (attribute, value) -> builder.with("max_$attribute", value) }
@@ -321,33 +322,34 @@ sealed class TuneableTrackAttribute<T : Number>(
321322
val attribute: String,
322323
val integerOnly: Boolean,
323324
val min: T?,
324-
val max: T?
325+
val max: T?,
326+
val tClazz: KClass<T>
325327
) {
326328
/**
327329
* A confidence measure from 0.0 to 1.0 of whether the track is acoustic.
328330
* 1.0 represents high confidence the track is acoustic.
329331
*/
330-
object ACOUSTICNESS : TuneableTrackAttribute<Float>("acousticness", false, 0f, 1f)
332+
object ACOUSTICNESS : TuneableTrackAttribute<Float>("acousticness", false, 0f, 1f, Float::class)
331333

332334
/**
333335
* Danceability describes how suitable a track is for dancing based on a combination of musical
334336
* elements including tempo, rhythm stability, beat strength, and overall regularity. A value of 0.0 is
335337
* least danceable and 1.0 is most danceable.
336338
*/
337-
object DANCEABILITY : TuneableTrackAttribute<Float>("danceability", false, 0f, 1f)
339+
object DANCEABILITY : TuneableTrackAttribute<Float>("danceability", false, 0f, 1f, Float::class)
338340

339341
/**
340342
* The duration of the track in milliseconds.
341343
*/
342-
object DURATION_IN_MILLISECONDS : TuneableTrackAttribute<Int>("duration_ms", true, 0, null)
344+
object DURATION_IN_MILLISECONDS : TuneableTrackAttribute<Int>("duration_ms", true, 0, null, Int::class)
343345

344346
/**
345347
* Energy is a measure from 0.0 to 1.0 and represents a perceptual measure of intensity and activity.
346348
* Typically, energetic tracks feel fast, loud, and noisy. For example, death metal has high energy,
347349
* while a Bach prelude scores low on the scale. Perceptual features contributing to this attribute
348350
* include dynamic range, perceived loudness, timbre, onset rate, and general entropy.
349351
*/
350-
object ENERGY : TuneableTrackAttribute<Float>("energy", false, 0f, 1f)
352+
object ENERGY : TuneableTrackAttribute<Float>("energy", false, 0f, 1f, Float::class)
351353

352354
/**
353355
* Predicts whether a track contains no vocals. “Ooh” and “aah” sounds are treated as
@@ -356,34 +358,34 @@ sealed class TuneableTrackAttribute<T : Number>(
356358
* no vocal content. Values above 0.5 are intended to represent instrumental tracks, but
357359
* confidence is higher as the value approaches 1.0.
358360
*/
359-
object INSTRUMENTALNESS : TuneableTrackAttribute<Float>("instrumentalness", false, 0f, 1f)
361+
object INSTRUMENTALNESS : TuneableTrackAttribute<Float>("instrumentalness", false, 0f, 1f, Float::class)
360362

361363
/**
362364
* The key the track is in. Integers map to pitches using standard Pitch Class notation.
363365
* E.g. 0 = C, 1 = C♯/D♭, 2 = D, and so on.
364366
*/
365-
object KEY : TuneableTrackAttribute<Int>("key", true, 0, 11)
367+
object KEY : TuneableTrackAttribute<Int>("key", true, 0, 11, Int::class)
366368

367369
/**
368370
* Detects the presence of an audience in the recording. Higher liveness values represent an increased
369371
* probability that the track was performed live. A value above 0.8 provides strong likelihood
370372
* that the track is live.
371373
*/
372-
object LIVENESS : TuneableTrackAttribute<Float>("liveness", false, 0f, 1f)
374+
object LIVENESS : TuneableTrackAttribute<Float>("liveness", false, 0f, 1f, Float::class)
373375

374376
/**
375377
* The overall loudness of a track in decibels (dB). Loudness values are averaged across the
376378
* entire track and are useful for comparing relative loudness of tracks. Loudness is the
377379
* quality of a sound that is the primary psychological correlate of physical strength (amplitude).
378380
* Values typically range between -60 and 0 db.
379381
*/
380-
object LOUDNESS : TuneableTrackAttribute<Float>("loudness", false, null, null)
382+
object LOUDNESS : TuneableTrackAttribute<Float>("loudness", false, null, null, Float::class)
381383

382384
/**
383385
* Mode indicates the modality (major or minor) of a track, the type of scale from which its
384386
* melodic content is derived. Major is represented by 1 and minor is 0.
385387
*/
386-
object MODE : TuneableTrackAttribute<Int>("mode", true, 0, 1)
388+
object MODE : TuneableTrackAttribute<Int>("mode", true, 0, 1, Int::class)
387389

388390
/**
389391
* The popularity of the track. The value will be between 0 and 100, with 100 being the most popular.
@@ -392,7 +394,7 @@ sealed class TuneableTrackAttribute<T : Number>(
392394
* the market parameter, it is expected to find relinked tracks with popularities that do not match
393395
* min_*, max_*and target_* popularities. These relinked tracks are accurate replacements for unplayable tracks with the expected popularity scores. Original, non-relinked tracks are available via the linked_from attribute of the relinked track response.
394396
*/
395-
object POPULARITY : TuneableTrackAttribute<Int>("popularity", true, 0, 100)
397+
object POPULARITY : TuneableTrackAttribute<Int>("popularity", true, 0, 100, Int::class)
396398

397399
/**
398400
* Speechiness detects the presence of spoken words in a track. The more exclusively speech-like the
@@ -402,61 +404,67 @@ sealed class TuneableTrackAttribute<T : Number>(
402404
* such cases as rap music. Values below 0.33 most likely represent music and other non-speech-like
403405
* tracks.
404406
*/
405-
object SPEECHINESS : TuneableTrackAttribute<Float>("speechiness", false, 0f, 1f)
407+
object SPEECHINESS : TuneableTrackAttribute<Float>("speechiness", false, 0f, 1f, Float::class)
406408

407409
/**
408410
* The overall estimated tempo of a track in beats per minute (BPM). In musical terminology, tempo is the
409411
* speed or pace of a given piece and derives directly from the average beat duration.
410412
*/
411-
object TEMPO : TuneableTrackAttribute<Float>("tempo", false, 0f, null)
413+
object TEMPO : TuneableTrackAttribute<Float>("tempo", false, 0f, null, Float::class)
412414

413415
/**
414416
* An estimated overall time signature of a track. The time signature (meter)
415417
* is a notational convention to specify how many beats are in each bar (or measure).
416418
* The time signature ranges from 3 to 7 indicating time signatures of 3/4, to 7/4.
417419
* A value of -1 may indicate no time signature, while a value of 1 indicates a rather complex or changing time signature.
418420
*/
419-
object TIME_SIGNATURE : TuneableTrackAttribute<Int>("time_signature", true, -1, 7)
421+
object TIME_SIGNATURE : TuneableTrackAttribute<Int>("time_signature", true, -1, 7, Int::class)
420422

421423
/**
422424
* A measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks with high
423425
* valence sound more positive (e.g. happy, cheerful, euphoric), while tracks with low valence
424426
* sound more negative (e.g. sad, depressed, angry).
425427
*/
426-
object VALENCE : TuneableTrackAttribute<Float>("valence", false, 0f, 1f)
428+
object VALENCE : TuneableTrackAttribute<Float>("valence", false, 0f, 1f, Float::class)
427429

428430
override fun toString() = attribute
429431

430-
fun asTrackAttribute(value: T): TrackAttribute<T> {
432+
fun <V : Number> asTrackAttribute(value: V): TrackAttribute<T> {
431433
require(!(min != null && min.toDouble() > value.toDouble())) { "Attribute value for $this must be greater than $min!" }
432434
require(!(max != null && max.toDouble() < value.toDouble())) { "Attribute value for $this must be less than $max!" }
433435

434-
return TrackAttribute(this, value)
436+
@Suppress("UNCHECKED_CAST")
437+
return TrackAttribute(this, when (tClazz) {
438+
Int::class -> value.toInt() as T
439+
Float::class -> value.toFloat() as T
440+
Double::class -> value.toDouble() as T
441+
else -> value.toDouble() as T
442+
})
435443
}
436444

437445
companion object {
438446
fun values() = listOf(
439-
ACOUSTICNESS,
440-
DANCEABILITY,
441-
DURATION_IN_MILLISECONDS,
442-
ENERGY,
443-
INSTRUMENTALNESS,
444-
KEY,
445-
LIVENESS,
446-
LOUDNESS,
447-
MODE,
448-
POPULARITY,
449-
SPEECHINESS,
450-
TEMPO,
451-
TIME_SIGNATURE,
452-
VALENCE
447+
ACOUSTICNESS,
448+
DANCEABILITY,
449+
DURATION_IN_MILLISECONDS,
450+
ENERGY,
451+
INSTRUMENTALNESS,
452+
KEY,
453+
LIVENESS,
454+
LOUDNESS,
455+
MODE,
456+
POPULARITY,
457+
SPEECHINESS,
458+
TEMPO,
459+
TIME_SIGNATURE,
460+
VALENCE
453461
)
454462
}
455463
}
456464

457465
data class TrackAttribute<T : Number>(val tuneableTrackAttribute: TuneableTrackAttribute<T>, val value: T) {
458466
companion object {
459467
fun <T : Number> create(tuneableTrackAttribute: TuneableTrackAttribute<T>, value: T) =
460-
tuneableTrackAttribute.asTrackAttribute(value)
468+
tuneableTrackAttribute.asTrackAttribute(value)
461469
}
462470
}

src/commonMain/kotlin/com.adamratzman.spotify/models/Playlist.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import kotlinx.serialization.Transient
2727
* @property type The object type: “playlist”
2828
* @property snapshot The version identifier for the current playlist. Can be supplied in other
2929
* requests to target a specific playlist version
30+
* @property description The playlist description. Only returned for modified, verified playlists, otherwise null.
3031
*/
3132
@Serializable
3233
data class SimplePlaylist(
@@ -38,6 +39,7 @@ data class SimplePlaylist(
3839
val collaborative: Boolean,
3940
val images: List<SpotifyImage>,
4041
val name: String,
42+
val description: String?,
4143
val owner: SpotifyPublicUser,
4244
@SerialName("primary_color") val primaryColor: String? = null,
4345
val public: Boolean? = null,

0 commit comments

Comments
 (0)