Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: warriorzz/ktify
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.1.1
Choose a base ref
...
head repository: warriorzz/ktify
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 8 commits
  • 11 files changed
  • 3 contributors

Commits on May 7, 2024

  1. Fix CI version

    warriorzz authored May 7, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1d6b061 View commit details
  2. Switch GitHub Pages deployment

    warriorzz committed May 7, 2024
    Copy the full SHA
    2829ba2 View commit details
  3. Add missing environment

    warriorzz committed May 7, 2024
    Copy the full SHA
    2241ba9 View commit details

Commits on May 8, 2024

  1. Add permissions to CI

    warriorzz authored May 8, 2024
    Copy the full SHA
    c4cf3a8 View commit details
  2. Hotfix KtifyBuilder URL generation (#7)

    * Hotfix KtifyBuilder URL generation
    
    * Edit version in README
    warriorzz authored May 8, 2024
    Copy the full SHA
    d5f340c View commit details
  3. v0.1.2 - Fix Player Endpoints (#8)

    * Hotfix KtifyBuilder URL generation
    
    * Edit version in README
    
    * Fix endpoints and KtifyBuilder authorisation code
    
    * Edit README version
    warriorzz authored May 8, 2024
    Copy the full SHA
    78124af View commit details

Commits on Oct 21, 2024

  1. v0.1.3 - Expose ClientCredentials

    warriorzz committed Oct 21, 2024
    Copy the full SHA
    8d67985 View commit details

Commits on Nov 18, 2024

  1. v0.1.4 - Upgrade kotlin and ktor versions

    warriorzz committed Nov 18, 2024
    Copy the full SHA
    cded8a9 View commit details
18 changes: 14 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -80,6 +80,14 @@ jobs:
runs-on: ubuntu-latest
name: Publish Docs
needs: test
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
permissions:
contents: read
pages: write
id-token: write
actions: read
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && !contains(github.event.commits[0].message, '[skip ci]')
steps:
- name: Fetch Sources
@@ -108,8 +116,10 @@ jobs:
- name: Build Dokka Docs with Gradle
run: ./gradlew dokkaHtml

- name: Deploy Docs to GitHub Pages
uses: JamesIves/github-pages-deploy-action@4.6.0
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
branch: gh-pages
folder: build/dokka/html
path: 'build/dokka/html'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ repositories {
mavenCentral()
}
dependencies {
implementation("ee.bjarn", "ktify", "0.1.1")
implementation("ee.bjarn", "ktify", "0.1.4")
}
```

19 changes: 10 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.KotlinJvm
import com.vanniktech.maven.publish.SonatypeHost
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") version "1.9.23"
kotlin("plugin.serialization") version "1.9.23"
kotlin("jvm") version "2.0.21"
kotlin("plugin.serialization") version "2.0.21"
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
id("org.jetbrains.dokka") version "1.9.20"
id("com.vanniktech.maven.publish") version "0.28.0"
}

group = "ee.bjarn"
version = "0.1.1"
version = "0.1.4"

repositories {
mavenCentral()
}

dependencies {
implementation(platform("io.ktor:ktor-bom:2.3.10"))
implementation(platform("io.ktor:ktor-bom:3.0.0"))
implementation("io.ktor", "ktor-client-okhttp")
implementation("io.ktor", "ktor-serialization-kotlinx-json")
implementation("io.ktor", "ktor-client-content-negotiation")
implementation("org.jetbrains.kotlinx", "kotlinx-serialization-json", "1.6.3")
implementation("org.jetbrains.kotlinx", "kotlinx-serialization-json", "1.7.3")

implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.0")
implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.9.0")
implementation("io.github.microutils", "kotlin-logging-jvm", "3.0.5")
implementation("org.slf4j", "slf4j-simple", "2.0.13")
}

tasks {
withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.RequiresOptIn"
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn")
}
}
dokkaHtml {
37 changes: 28 additions & 9 deletions src/main/kotlin/ee/bjarn/ktify/Ktify.kt
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package ee.bjarn.ktify
import ee.bjarn.ktify.model.auth.ClientCredentials
import ee.bjarn.ktify.model.auth.ClientCredentialsResponse
import ee.bjarn.ktify.model.auth.Scope
import ee.bjarn.ktify.model.auth.refresh
import ee.bjarn.ktify.player.KtifyPlayer
import ee.bjarn.ktify.utils.AuthenticationErrorObject
import ee.bjarn.ktify.utils.AuthenticationException
@@ -19,9 +20,12 @@ import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.util.*
import io.ktor.utils.io.InternalAPI
import kotlinx.serialization.json.JsonObject
import mu.KotlinLogging
import java.net.URLEncoder
import java.util.UUID
import kotlin.io.encoding.Base64

/**
* The main wrapper class
@@ -82,6 +86,12 @@ class Ktify(
* Will be internal once the entire API is covered
*/
val jsonLessHttpClient = HttpClient(httpClient.engine)

/**
* Retrieve client credentials from the current [Ktify] instance
* @return The [ClientCredentials] object
*/
fun getClientCredentials() = clientCredentials
}

/**
@@ -103,22 +113,31 @@ class KtifyBuilder(
*/
fun getAuthorisationURL(scopes: List<Scope>): String {
val baseUrl = "https://accounts.spotify.com/authorize"
val scopesString = if (scopes.size > 0) scopes.map { it.value + "%20" }.reduce { acc, s -> acc + s }.dropLast(3) else "none"
return "$baseUrl?client_id=&scope=$scopesString&redirect_uri=$redirectUri&state=$state&response_type=code"
val scopesString = if (scopes.size > 0) scopes.map { it.value + " " }.reduce { acc, s -> acc + s }.dropLast(1) else "none"
return "$baseUrl?client_id=$clientId&scope=${URLEncoder.encode(scopesString, "UTF-8")}&redirect_uri=${URLEncoder.encode(redirectUri, "UTF-8")}&state=$state&response_type=code"
}

/**
* @param clientCredentials Client credentials that were previously acquired
* @return The [Ktify] instance
*/
suspend fun fromClientCredentials(clientCredentials: ClientCredentials): Ktify {
clientCredentials.refresh()
return Ktify(clientCredentials)
}

/**
* @param authorizationCode returned by the request to the user
* @return The [Ktify] instance
*/
@OptIn(InternalAPI::class)
@OptIn(InternalAPI::class, kotlin.io.encoding.ExperimentalEncodingApi::class)
suspend fun build(authorizationCode: String): Ktify {
val clientCredentialsResponse: ClientCredentialsResponse =
ktifyHttpClient.post("https://accounts.spotify.com/api/token") {
header("Content-Type", "application/x-www-form-urlencoded")
body =
"grant_type=authorization_code&client_id=$clientId&client_secret=$clientSecret&redirect_uri=$redirectUri&code=$authorizationCode"
}.body()
val clientCredentialsResponse: ClientCredentialsResponse = ktifyHttpClient.post("https://accounts.spotify.com/api/token") {
header("Content-Type", "application/x-www-form-urlencoded")
header("Authorization", "Basic ${Base64.encode("$clientId:$clientSecret".encodeToByteArray())}")
body =
"grant_type=authorization_code&redirect_uri=${URLEncoder.encode(redirectUri, "UTF-8")}&code=$authorizationCode"
}.body()
return Ktify(
ClientCredentials(
clientId,
Original file line number Diff line number Diff line change
@@ -5,9 +5,11 @@ import ee.bjarn.ktify.utils.base64encode
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.util.*
import io.ktor.utils.io.InternalAPI
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ClientCredentials(
val clientId: String,
val clientSecret: String,
38 changes: 34 additions & 4 deletions src/main/kotlin/ee/bjarn/ktify/model/player/CurrentPlayback.kt
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ package ee.bjarn.ktify.model.player

import ee.bjarn.ktify.model.Episode
import ee.bjarn.ktify.model.Track
import ee.bjarn.ktify.model.track.TrackActions
import ee.bjarn.ktify.model.util.Context
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.PrimitiveKind
@@ -32,6 +31,8 @@ sealed class CurrentPlayback {
@SerialName("repeat_state")
abstract val repeatState: String?
abstract val context: Context?

abstract val actions: CurrentPlaybackActions
}

@Serializable
@@ -49,8 +50,10 @@ class CurrentPlayingTrack(
@SerialName("repeat_state")
override val repeatState: String? = null,
override val context: Context?,
override val actions: CurrentPlaybackActions,
@SerialName("smart_shuffle")
val smartShuffle: Boolean?,
val item: Track? = null,
val actions: TrackActions,
val resuming: Boolean? = null
) : CurrentPlayback()

@@ -69,8 +72,8 @@ class CurrentPlayingEpisode(
@SerialName("repeat_state")
override val repeatState: String? = null,
override val context: Context?,
override val actions: CurrentPlaybackActions,
val item: Episode? = null,
val actions: TrackActions,
val resuming: Boolean? = null
) : CurrentPlayback()

@@ -90,9 +93,36 @@ class CurrentPlaybackNull(
override val shuffleState: Boolean? = null,
@SerialName("repeat_state")
override val repeatState: String? = null,
override val context: Context?
override val context: Context?,
override val actions: CurrentPlaybackActions,
) : CurrentPlayback()

@Serializable
data class CurrentPlaybackActions(
val disallows: Disallows
)

@Serializable
data class Disallows(
@SerialName("interrupting_playback")
val interruptingPlayback: Boolean? = null,
val pausing: Boolean? = null,
val resuming: Boolean? = null,
val seeking: Boolean? = null,
@SerialName("skipping_next")
val skippingNext: Boolean? = null,
@SerialName("skipping_prev")
val skippingPrev: Boolean? = null,
@SerialName("toggling_repeat_context")
val togglingRepeatContext: Boolean? = null,
@SerialName("toggling_shuffle")
val togglingShuffle: Boolean? = null,
@SerialName("toggling_repeat_track")
val togglingRepeatTrack: Boolean? = null,
@SerialName("transferring_playback")
val transferringPlayback: Boolean? = null,
)

object CurrentPlaybackSerializer : JsonContentPolymorphicSerializer<CurrentPlayback>(CurrentPlayback::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<CurrentPlayback> {
return when (element.jsonObject["item"]?.jsonObject?.get("type")?.jsonPrimitive?.content) {
4 changes: 3 additions & 1 deletion src/main/kotlin/ee/bjarn/ktify/model/player/Devices.kt
Original file line number Diff line number Diff line change
@@ -20,5 +20,7 @@ data class Device(
val name: String,
val type: String,
@SerialName("volume_percent")
val volumePercent: Int
val volumePercent: Int,
@SerialName("supports_volume")
val supportsVolume: Boolean,
)
13 changes: 0 additions & 13 deletions src/main/kotlin/ee/bjarn/ktify/model/track/Track.kt
Original file line number Diff line number Diff line change
@@ -43,19 +43,6 @@ data class SavedTrack(
val track: Track,
)

@Serializable
data class TrackActions(
@SerialName("is_playing")
val isPlaying: Boolean? = null,
val disallows: TrackActionsDisallows
)

@Serializable
data class TrackActionsDisallows(
val pausing: Boolean? = null,
val resuming: Boolean? = null
)

@Serializable
data class TrackRestriction(
val reason: RestrictionType
3 changes: 3 additions & 0 deletions src/main/kotlin/ee/bjarn/ktify/model/util/Context.kt
Original file line number Diff line number Diff line change
@@ -35,4 +35,7 @@ enum class ObjectType {

@SerialName("user")
USER,

@SerialName("collection")
COLLECTION,
}
1 change: 1 addition & 0 deletions src/main/kotlin/ee/bjarn/ktify/player/KtifyPlayer.kt
Original file line number Diff line number Diff line change
@@ -112,6 +112,7 @@ class KtifyPlayer internal constructor(val ktify: Ktify) {
) {
method = HttpMethod.Put
url.takeFrom(ktify.requestHelper.baseUrl + "me/player/play")
header("Content-Type", "application/json")
if (deviceId != null) {
parameter("device_id", deviceId)
}
2 changes: 1 addition & 1 deletion src/main/kotlin/ee/bjarn/ktify/utils/RequestHelper.kt
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ internal class RequestHelper(
}
if (requiresScope != null) { require(clientCredentials.scopes?.contains(requiresScope) ?: false) }
val responseData: HttpResponse =
makeRequest(requiresAuthentication = true, client = ktify.jsonLessHttpClient, builder = builder)
makeRequest(requiresAuthentication = true, builder = builder)
return responseData.status
}