Skip to content

kingg22/deezer-client-kt

Repository files navigation

🎧 Unofficial Deezer Client KMP

Kotlin Multiplatform Ktor Client KtorGen

Maven Central Version Maven Central Last Update

GitHub License GitHub last commit (branch)

A Kotlin Multiplatform client for Deezer’s official REST API. Using Ktor Client. Supports Android (min SDK 24 / JVM 8+), JVM Java, Kotlin/JVM, Kotlin/Native platforms.

πŸ› οΈ InstalaciΓ³n

  • Android minSdk Version 24
  • JVM target Java 8+ / Kotlin JVM 1.8

Maven Central Version

Gradle

// common main
implementation("io.github.kingg22:deezer-client-kt:<latest-version>")

Maven

<dependency>
    <groupId>io.github.kingg22</groupId>
    <artifactId>deezer-client-kt-jvm</artifactId>
    <version>current-version</version>
</dependency>

Install a Ktor client engine, see detail.

Ktor Maven Central Version

Example with CIO (Coroutines based):

Gradle

implementation("io.ktor:ktor-client-cio:$ktor_version")

Maven

<dependency>
    <groupId>io.ktor</groupId>
    <artifactId>ktor-client-cio-jvm</artifactId>
    <version>${ktor_version}</version>
</dependency>

πŸ§ͺ Usage

  • βœ… Kotlin
val client = DeezerApiClient()
val artist: Artist = client.artists.getById(27) // suspend fun
println("Artist: ${artist.name}") // Artist: Daft Punk
  • ⚠️ Java (Blocking)
public class Test {
  public static void main(String[] args) {
    final var client = new DeezerApiJavaClient();
    final Artist artist = client.artists.getById(27);
    System.out.println("Artist: " + artist.getName()); // Artist: Daft Punk
  }
}
  • βœ… Java (CompletableFuture)
import java.util.concurrent.CompletableFuture;

public class Test {
  public static void main(String[] args) {
    final var client = new DeezerApiJavaClient();
    final CompletableFuture<Artist> future = client.artists.getByIdFuture(27);
    future.whenComplete((artist, err) -> {
      if (err != null) err.printStackTrace();
      else System.out.println("Artist: " + artist.getName()); // Artist: Daft Punk
    });
  }
}

Paginated Response

Kotlin

val client: DeezerApiClient // use the same client in the app
val result: PaginatedResponse<Track> = client.searches.search("eminem")
checkNotNull(result.next)
val nextPage: PaginatedResponse<Track> = checkNotNull(tested.fetchNext(expand=true))
// because the next is not null, fetchNext don't return null
// expand means the previous data (List<Track>) going to expand with the new response
// fetchNext is an extension function*

Java

import java.util.concurrent.CompletableFuture;
import io.github.kingg22.deezer.client.api.objects.PaginatedResponses;

public class Test {
  public static void main(String[] args) {
    final DeezerApiJavaClient client = "";// use the same client in the app
    final PaginatedResponse<Track> response = client.searches.search("eminem");
    final PaginatedResponse<Track> nextPage =
      // Import the java helper
      // Blocking
      PaginatedResponses.fetchNext(response, Track.class, /* expand */ true);

    final CompletableFuture<PaginatedResponse<Track>> nextPageFuture =
      // async
      PaginatedResponses.fetchNextFuture(response, Track.class, /* expand */ true);

    // Null type is the same, but in java isn't a mandatory check, is recommended!
  }
}

Reloading resources

Kotlin

val client: DeezerApiClient // use the same client in the app
val episode: Episode = client.episodes.getById(526673645) // suspend fun
// after do things ...
val freshEpisode: Episode = episode.reload() // suspend fun

Java

import java.util.concurrent.CompletableFuture;

class Test {
  public static void main(String[] args) {
    final DeezerApiJavaClient client = "";// use the same client in the app
    final Episode episode = client.episodes.getById(526673645);
    final Episode freshEpisode = Resources.reload(tested); // blocking
    final CompletableFuture<Episode> freshEpisodeFuture = Resources.reloadFuture(tested); // async
  }
}

Advanced search

Kotlin

import io.github.kingg22.deezer.client.api.objects.SearchOrder
import io.github.kingg22.deezer.client.api.routes.SearchRoutes.Companion.buildAdvancedQuery
import io.github.kingg22.deezer.client.api.routes.SearchRoutes.Companion.setStrict
import kotlin.time.Duration.Companion.minutes

val client: DeezerApiClient // use the same client in the app

val query = buildAdvancedQuery { // DSL builder
  artist = "eminem"
  durationMin = 1.minutes
}
client.searches.searchPlaylist(query, strict = setString(true))

client.searches.searchTrack(
  q = buildAdvancedQuery(q = "Not Afraid", artist = "eminem"), // function
  order = SearchOrder.RANKING,
  limit = 15,
  index = 10,
)

Java

import static io.github.kingg22.deezer.client.api.routes.SearchRoutes.buildAdvancedQuery;
import static io.github.kingg22.deezer.client.api.routes.SearchRoutes.setStrict;

public class Test {
  public static void main(String[] args) {
    final DeezerApiJavaClient client = "";// use the same client in the app
    client.searches.searchAlbum(
      /* q =*/ buildAdvancedQuery(/* q = */ "King") // Only access to Java builder style
        .artist("eminem")
        .build(),
      /* strict = */ setStrict(true),
      /* order = */ SearchOrder.RATING_DESC,
      /* index = */ 10,
      /* limit = */ 15
    );
  }
}

But where is the http client of ktor?

Because the client needs to configure custom validators, throw exceptions; etc. I create an HttpClientBuilder, you can use it to configure some specs. The default constructor of the client expects you to add an engine on your dependencies, and that's it.

Why not data class?

Read more about this here. I take the decision to use class with Poko because immutability is guaranteed and equals are generated. The idea of this api client is stateless, fetch what u need, and that's it. Only PaginatedResponse is data class to easy duplicate and fetch data.

πŸ†š Differences between kotlin and java versions

  • Kotlin can't access to the java version and viceversa
  • Kotlin extensions vs. an external object with static methods (helpers / utility class)
  • Java can't access some kotlin specific like DSL, reified types, suspend methods, function with kotlin duration
  • Java wrappers of kotlin stuffs, so kotlin is a priority and dictates the route
  • Java needs to use kotlin datetime LocalDate and LocalDateTime when get dates :/
  • Similitude kotlin suspend and java blocking it reads the same >_<

πŸ›£ Roadmap

  • Support authenticated endpoints (user edit, user playlist edit, etc.)
  • OAuth flow and token refresh support

⛓️‍πŸ’₯ Dependencies

πŸ“ƒ References

🐞 Issue Guidelines

Please include one of the following types in your issue title or description:

  • Serialization error – e.g. the JSON response missing required field, non-nullable property, etc.
  • Missing field in response – field present in official API but absent in models (or extra non‑official data)
  • Others

Also state if the issue refers to an official Deezer change or unofficial additional field.

🀝 Contributions

We welcome contributions if:

  • Code is written in Kotlin
  • Uses the official Deezer API (no scraping or unofficial endpoints)
  • Includes at least one test with a sample JSON response

Please fork, open PRs, and ensure tests pass.

πŸ“„ License

Licensed under AGPL-3.0 β€” contributions must respect Deezer API Terms & Conditions, including attribution and rate‑limit policies.

About

Unofficial Kotlin Multiplatform client for Deezer public API

Topics

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •