From c0c7a56f96756e72148e0938b1623aba9e33e899 Mon Sep 17 00:00:00 2001 From: william Date: Sat, 17 Dec 2022 21:26:30 -0500 Subject: [PATCH] Things --- build.gradle.kts | 9 ++- .../dev/fyloz/musicplayer/core/Application.kt | 74 ++++++++++++++++--- .../dev/fyloz/musicplayer/core/TypeAliases.kt | 3 + .../musicplayer/core/factory/TrackFactory.kt | 4 +- .../core/factory/TrackFactoryProxy.kt | 9 ++- .../musicplayer/core/http/TrackRoutes.kt | 9 ++- .../core/http/auth/AuthorizationData.kt | 14 ++++ .../musicplayer/core/logic/TrackLogic.kt | 10 ++- .../musicplayer/core/plugins/Monitoring.kt | 13 ---- .../dev/fyloz/musicplayer/modules/Module.kt | 7 ++ .../modules/spotify/SpotifyApiProvider.kt | 46 ++++++++++++ .../spotify/SpotifyAuthorizationData.kt | 3 + .../modules/spotify/SpotifyHttpProvider.kt | 34 --------- .../modules/spotify/SpotifyModule.kt | 52 ++++++++----- .../modules/spotify/SpotifyTokenLogic.kt | 29 -------- .../modules/spotify/SpotifyTrackFactory.kt | 14 +++- .../modules/spotify/api/SearchRequest.kt | 19 +++++ .../modules/spotify/api/SearchResponse.kt | 20 +++++ .../spotify/config/SpotifyConfiguration.kt | 15 ++++ src/main/resources/application.conf | 9 +++ 20 files changed, 273 insertions(+), 120 deletions(-) create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/core/TypeAliases.kt create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/core/http/auth/AuthorizationData.kt delete mode 100644 src/main/kotlin/dev/fyloz/musicplayer/core/plugins/Monitoring.kt create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyApiProvider.kt create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyAuthorizationData.kt delete mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyHttpProvider.kt delete mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTokenLogic.kt create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchRequest.kt create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchResponse.kt create mode 100644 src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/config/SpotifyConfiguration.kt create mode 100644 src/main/resources/application.conf diff --git a/build.gradle.kts b/build.gradle.kts index b2b17dc..b1205f6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,13 +32,14 @@ dependencies { implementation("io.ktor:ktor-server-netty-jvm:$ktor_version") implementation("io.ktor:ktor-client-core:$ktor_version") implementation("io.ktor:ktor-server-auth:$ktor_version") + implementation("io.ktor:ktor-server-auth-jvm:$ktor_version") + implementation("io.ktor:ktor-server-locations-jvm:$ktor_version") + implementation("io.ktor:ktor-client-core-jvm:$ktor_version") + implementation("io.ktor:ktor-client-apache-jvm:$ktor_version") + implementation("io.ktor:ktor-client-logging-jvm:$ktor_version") implementation("io.insert-koin:koin-ktor:$koin_version") implementation("io.insert-koin:koin-logger-slf4j:$koin_version") implementation("ch.qos.logback:logback-classic:$logback_version") - implementation("io.ktor:ktor-server-auth-jvm:2.1.3") - implementation("io.ktor:ktor-server-locations-jvm:2.1.3") - implementation("io.ktor:ktor-client-core-jvm:2.1.3") - implementation("io.ktor:ktor-client-apache-jvm:2.1.3") testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version") testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/Application.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/Application.kt index e04eecd..0e5be07 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/core/Application.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/Application.kt @@ -1,27 +1,24 @@ package dev.fyloz.musicplayer.core +import com.typesafe.config.ConfigFactory import dev.fyloz.musicplayer.core.data.RepositoryInjection import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy +import dev.fyloz.musicplayer.core.http.auth.AuthorizationData import dev.fyloz.musicplayer.core.http.configureTrackRoutes import dev.fyloz.musicplayer.core.logic.TrackLogic -import dev.fyloz.musicplayer.modules.spotify.SpotifyInjection -import dev.fyloz.musicplayer.modules.spotify.SpotifyModule -import dev.fyloz.musicplayer.modules.spotify.SpotifyTrackFactory +import dev.fyloz.musicplayer.modules.Module import io.ktor.client.* -import io.ktor.client.engine.apache.* -import io.ktor.http.* +import io.ktor.client.plugins.logging.* import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* -import io.ktor.server.auth.* +import io.ktor.server.config.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.plugins.callloging.* import io.ktor.server.plugins.contentnegotiation.* -import io.ktor.server.response.* import io.ktor.server.routing.* import kotlinx.serialization.json.Json import org.koin.dsl.module -import org.koin.ktor.ext.inject import org.koin.ktor.plugin.Koin import org.koin.logger.slf4jLogger import org.slf4j.event.Level @@ -32,6 +29,11 @@ fun main() { } fun Application.module() { + val config = HoconApplicationConfig(ConfigFactory.load()) + + log.info("Loading modules...") + registerModules(config) + install(CallLogging) { level = Level.DEBUG // filter { call -> call.request.path().startsWith("/") } @@ -46,24 +48,72 @@ fun Application.module() { slf4jLogger() modules( RepositoryInjection.koinBeans, - SpotifyInjection.koinBeans, module { single { TrackFactoryProxy() } single { TrackLogic() } + single { + HttpClient { + install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) { + json(Json { + ignoreUnknownKeys = true + }) + } + + install(Logging) { + level = LogLevel.ALL +// filter { call -> call.request.path().startsWith("/") } + } + } + } + + registeredModules.values.forEach { it.setupDependencyInjection(this, config) } } ) } } - val spotifyModule = SpotifyModule() - spotifyModule.configure(this) + registeredModules.values.forEach { it.configure(this) } install(Routing) { - spotifyModule.configureRoutes(this) + registeredModules.values.forEach { it.configureRoutes(this) } route("/api/v1") { configureTrackRoutes() } } } + +private val registeredModules = mutableMapOf() + +fun ApplicationCall.getAuthorizationData(): AuthorizationData { + val auth = AuthorizationData() + + registeredModules.forEach { (moduleName, module) -> + val data = module.getAuthorizationData(this) + if (data != null) { + auth.addModuleData(data, moduleName) + } + } + + return auth +} + +private fun Application.registerModules(config: ApplicationConfig) { + val enabledModules = config.property("modules.enabled").getList() + enabledModules.forEach { registerModule(it, config) } +} + +private fun Application.registerModule(moduleName: String, config: ApplicationConfig) { + log.debug("Found module '$moduleName'") + val moduleClass = config.property("modules.$moduleName.class").getString() + val module = loadModule(moduleClass) + + registeredModules[moduleName] = module + log.info("Registered module '$moduleName'") +} + +private fun Application.loadModule(moduleClass: String): Module { + log.debug("Loading module class '$moduleClass'...") + return Class.forName(moduleClass).getDeclaredConstructor().newInstance() as Module +} diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/TypeAliases.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/TypeAliases.kt new file mode 100644 index 0000000..5a07aaa --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/TypeAliases.kt @@ -0,0 +1,3 @@ +package dev.fyloz.musicplayer.core + +typealias KoinModule = org.koin.core.module.Module diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactory.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactory.kt index fae0676..764efb4 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactory.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactory.kt @@ -1,7 +1,9 @@ package dev.fyloz.musicplayer.core.factory +import dev.fyloz.musicplayer.core.http.auth.AuthorizationData import dev.fyloz.musicplayer.core.model.Track interface TrackFactory { - suspend fun createTrack(id: String, trackId: String): Track + suspend fun searchTrack(query: String, auth: AuthorizationData): Collection + suspend fun createTrack(id: String, trackId: String, auth: AuthorizationData): Track } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactoryProxy.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactoryProxy.kt index 213296c..5b4579a 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactoryProxy.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/factory/TrackFactoryProxy.kt @@ -1,5 +1,6 @@ package dev.fyloz.musicplayer.core.factory +import dev.fyloz.musicplayer.core.http.auth.AuthorizationData import dev.fyloz.musicplayer.core.model.Track class TrackFactoryProxy { @@ -9,9 +10,11 @@ class TrackFactoryProxy { factories[type] = factory; } - suspend fun createTrack(type: String, id: String, trackId: String): Track { - return getFactory(type).createTrack(id, trackId) - } + suspend fun search(query: String, auth: AuthorizationData) = + factories.values.map { it.searchTrack(query, auth) }.flatten() + + suspend fun createTrack(type: String, id: String, trackId: String, auth: AuthorizationData): Track = + getFactory(type).createTrack(id, trackId, auth) private fun getFactory(type: String) = factories[type]!!; } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/http/TrackRoutes.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/http/TrackRoutes.kt index 7c07059..a798203 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/core/http/TrackRoutes.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/http/TrackRoutes.kt @@ -1,5 +1,6 @@ package dev.fyloz.musicplayer.core.http +import dev.fyloz.musicplayer.core.getAuthorizationData import dev.fyloz.musicplayer.core.http.requests.CreateTrackRequest import dev.fyloz.musicplayer.core.logic.TrackLogic import io.ktor.server.application.* @@ -16,9 +17,15 @@ fun Route.configureTrackRoutes() { call.respond(logic.getAll().toList()) } + get("/search") { + val query = call.request.queryParameters["q"]!! + val tracks = logic.search(query, call.getAuthorizationData()) + call.respond(tracks) + } + post { val request = call.receive() - val track = logic.save(request.type, request.trackId) + val track = logic.save(request.type, request.trackId, call.getAuthorizationData()) call.respond(track) } } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/http/auth/AuthorizationData.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/http/auth/AuthorizationData.kt new file mode 100644 index 0000000..9e26ab7 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/http/auth/AuthorizationData.kt @@ -0,0 +1,14 @@ +package dev.fyloz.musicplayer.core.http.auth + +class AuthorizationData { + private val modulesData = mutableMapOf() + + fun addModuleData(data: T, moduleName: String) { + modulesData[moduleName] = data; + } + + @Suppress("UNCHECKED_CAST") + fun getModuleData(moduleName: String): T { + return modulesData[moduleName] as T + } +} diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/logic/TrackLogic.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/logic/TrackLogic.kt index 710cf66..af96ca5 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/core/logic/TrackLogic.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/core/logic/TrackLogic.kt @@ -2,8 +2,8 @@ package dev.fyloz.musicplayer.core.logic import dev.fyloz.musicplayer.core.data.TrackRepository import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy +import dev.fyloz.musicplayer.core.http.auth.AuthorizationData import dev.fyloz.musicplayer.core.model.Track -import dev.fyloz.musicplayer.modules.spotify.SpotifyTrackFactory import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.util.UUID @@ -15,9 +15,13 @@ class TrackLogic : KoinComponent { fun getAll() = repository.findAll() fun getById(id: String) = repository.findById(id) - suspend fun save(type: String, trackId: String): Track { + suspend fun search(query: String, auth: AuthorizationData): Collection { + return trackFactory.search(query, auth) + } + + suspend fun save(type: String, trackId: String, auth: AuthorizationData): Track { val id = generateId() - val track = trackFactory.createTrack(type, id, trackId) + val track = trackFactory.createTrack(type, id, trackId, auth) repository.save(track) return track } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/core/plugins/Monitoring.kt b/src/main/kotlin/dev/fyloz/musicplayer/core/plugins/Monitoring.kt deleted file mode 100644 index 26be9d2..0000000 --- a/src/main/kotlin/dev/fyloz/musicplayer/core/plugins/Monitoring.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.fyloz.musicplayer.core.plugins - -import io.ktor.server.plugins.callloging.* -import org.slf4j.event.* -import io.ktor.server.request.* -import io.ktor.server.application.* - -fun Application.configureMonitoring() { - install(CallLogging) { - level = Level.INFO - filter { call -> call.request.path().startsWith("/") } - } -} diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/Module.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/Module.kt index 98d0c9d..769a7bb 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/modules/Module.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/Module.kt @@ -1,12 +1,17 @@ package dev.fyloz.musicplayer.modules +import dev.fyloz.musicplayer.core.KoinModule import dev.fyloz.musicplayer.core.factory.TrackFactory import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy import io.ktor.server.application.* +import io.ktor.server.config.* import io.ktor.server.routing.* import org.koin.ktor.ext.inject abstract class Module(private val moduleName: String) { + open fun setupDependencyInjection(module: KoinModule, config: ApplicationConfig) { + } + open fun configure(app: Application) { with(app) { val trackFactoryProxy by inject() @@ -26,6 +31,8 @@ abstract class Module(private val moduleName: String) { } } + open fun getAuthorizationData(call: ApplicationCall): Any? = null + protected open fun Route.configureApiRoutes() { } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyApiProvider.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyApiProvider.kt new file mode 100644 index 0000000..1a75ffe --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyApiProvider.kt @@ -0,0 +1,46 @@ +package dev.fyloz.musicplayer.modules.spotify + +import dev.fyloz.musicplayer.modules.spotify.api.SearchRequest +import dev.fyloz.musicplayer.modules.spotify.api.SearchResponse +import dev.fyloz.musicplayer.modules.spotify.api.Track +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class SpotifyApiProvider : KoinComponent { + private val client by inject() + + suspend fun search(query: String, type: String, accessToken: String): Collection { + val requestBody = SearchRequest(query, type, "audio", 50, 0) + val response = client.get("https://api.spotify.com/v1/search") { + accept(ContentType.Application.Json) + headers { + append(HttpHeaders.Authorization, "Bearer $accessToken") + } +// setBody(requestBody) + url { + parameters.append("q", query) + parameters.append("type", type) + parameters.append("include_external", "audio") + parameters.append("limit", "50") + parameters.append("offset", "0") + } + }.body() + + return response.tracks.items + } + + suspend fun getTrackById(trackId: String, accessToken: String): Track { + val response = client.get("https://api.spotify.com/v1/tracks/$trackId") { + accept(ContentType.Application.Json) + headers { + append(HttpHeaders.Authorization, "Bearer $accessToken") + } + } + + return response.body() + } +} diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyAuthorizationData.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyAuthorizationData.kt new file mode 100644 index 0000000..c93ac33 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyAuthorizationData.kt @@ -0,0 +1,3 @@ +package dev.fyloz.musicplayer.modules.spotify + +data class SpotifyAuthorizationData(val accessToken: String) diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyHttpProvider.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyHttpProvider.kt deleted file mode 100644 index 03dbea8..0000000 --- a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyHttpProvider.kt +++ /dev/null @@ -1,34 +0,0 @@ -package dev.fyloz.musicplayer.modules.spotify - -import dev.fyloz.musicplayer.modules.spotify.api.Track -import io.ktor.client.* -import io.ktor.client.call.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* -import kotlinx.serialization.json.Json - -class SpotifyHttpProvider { - private val client = HttpClient { - install(ContentNegotiation) { - json(Json { - ignoreUnknownKeys = true - }) - } - } - - suspend fun getTrackById(trackId: String, token: String): Track { - val tmpToken = - "Bearer BQAtodg3W_kHLVJhGLFD0YEOh5AFfxSsrvhk1n9c0ELrlQdoEkbyOXYncN1k3umPfqqR3sesLI8qpJMxJ6-98nc4Z7KenedLBGyHfvBPzWg_U8OtwWy6jEWl7MbeFJYO50GNj-XRLPv6EAA1hPrrSIOhJBvOygs6hcDAYfD-_moA" - - val response = client.get("https://api.spotify.com/v1/tracks/$trackId") { - accept(ContentType.Application.Json) - headers { - append(HttpHeaders.Authorization, tmpToken) - } - } - - return response.body() - } -} diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyModule.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyModule.kt index 70e2da7..661313e 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyModule.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyModule.kt @@ -1,58 +1,76 @@ package dev.fyloz.musicplayer.modules.spotify +import dev.fyloz.musicplayer.core.KoinModule import dev.fyloz.musicplayer.modules.Module +import dev.fyloz.musicplayer.modules.spotify.config.SpotifyConfiguration import io.ktor.client.* import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* +import io.ktor.server.config.* import io.ktor.server.response.* import io.ktor.server.routing.* +import org.koin.ktor.ext.inject + +class SpotifyModule : Module(moduleName) { + override fun setupDependencyInjection(module: KoinModule, config: ApplicationConfig) { + with(module) { + single { SpotifyConfiguration.fromEnvironment(config) } + } + } -class SpotifyModule : Module("spotify") { override fun configure(app: Application) { super.configure(app) + + val httpClient by app.inject() + val config by app.inject() + with(app) { authentication { - oauth("auth-oauth-spotify") { + oauth(oauthName) { urlProvider = { "http://localhost:8080/module/spotify/login-callback" } providerLookup = { OAuthServerSettings.OAuth2ServerSettings( - name = "spotify", + name = moduleName, authorizeUrl = "https://accounts.spotify.com/authorize", accessTokenUrl = "https://accounts.spotify.com/api/token", requestMethod = HttpMethod.Post, - clientId = "1372bd3ebcad4f889994f9a3f675472b", - clientSecret = "26ac249dc5ca4a309aa08f8cfcec9a60" + clientId = config.clientId, + clientSecret = config.clientSecret, + defaultScopes = listOf("user-read-private user-read-email") ) } - client = HttpClient() + client = httpClient } } } } + override fun getAuthorizationData(call: ApplicationCall) = SpotifyAuthorizationData( + call.request.cookies["Spotify-Access-Token"]!! + ) + override fun Route.configureModuleRoutes() { - authenticate("auth-oauth-spotify") { + authenticate(oauthName) { get("/login") { call.respondRedirect("https://accounts.spotify.com/authorize") } get("/login-callback") { - val principal: OAuthAccessTokenResponse.OAuth2? = call.principal() - if (principal != null) { - call.response.cookies.append("Spotify-Access-Token", principal.accessToken) - - if (principal.refreshToken != null) { - call.response.cookies.append("Spotify-Refresh-Token", principal.refreshToken!!) + call.principal()?.let { + with(call.response.cookies) { + append("Spotify-Access-Token", it.accessToken, maxAge = it.expiresIn) + append("Spotify-Refresh-Token", it.refreshToken!!) } - -// SpotifyTokenLogic(http) - } else { - call.respond(HttpStatusCode.Unauthorized) } } } } override fun getTrackFactory() = SpotifyTrackFactory() + + companion object { + const val moduleName = "spotify" + const val oauthName = "auth-oauth-spotify" + } } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTokenLogic.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTokenLogic.kt deleted file mode 100644 index 562f598..0000000 --- a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTokenLogic.kt +++ /dev/null @@ -1,29 +0,0 @@ -package dev.fyloz.musicplayer.modules.spotify - -import dev.fyloz.musicplayer.modules.spotify.api.AccessTokenRequest -import dev.fyloz.musicplayer.modules.spotify.api.AccessTokenResponse -import io.ktor.client.* -import io.ktor.client.call.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.util.* -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject - -class SpotifyTokenLogic : KoinComponent { - private val httpClient by inject() - - suspend fun getAccessToken(authorizationCode: String) { - val authorizationHeader = "1372bd3ebcad4f889994f9a3f675472b:26ac249dc5ca4a309aa08f8cfcec9a60".encodeBase64() - - val call = httpClient.post("https://accounts.spotify.com/api/token") { - contentType(ContentType.Application.FormUrlEncoded) - accept(ContentType.Application.Json) - setBody(AccessTokenRequest(authorizationCode, "http://localhost:8080/module/spotify/login-callback")) - header("Authorization", "Basic $authorizationHeader") - } - - val response = call.body() - println(response.accessToken) - } -} diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTrackFactory.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTrackFactory.kt index 0901372..738692a 100644 --- a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTrackFactory.kt +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/SpotifyTrackFactory.kt @@ -1,14 +1,22 @@ package dev.fyloz.musicplayer.modules.spotify import dev.fyloz.musicplayer.core.factory.TrackFactory +import dev.fyloz.musicplayer.core.http.auth.AuthorizationData import dev.fyloz.musicplayer.core.model.Track class SpotifyTrackFactory : TrackFactory { - private val httpProvider = SpotifyHttpProvider() + private val apiProvider = SpotifyApiProvider() - override suspend fun createTrack(id: String, trackId: String): Track { - val spotifyApiTrack = httpProvider.getTrackById(trackId, "bla"); + override suspend fun searchTrack(query: String, auth: AuthorizationData): Collection { + val apiTracks = apiProvider.search(query, "track", auth.accessToken) + return apiTracks.map { SpotifyTrack("not-an-id", it.id, it.name) } + } + override suspend fun createTrack(id: String, trackId: String, auth: AuthorizationData): Track { + val spotifyApiTrack = apiProvider.getTrackById(trackId, auth.accessToken); return SpotifyTrack(id, trackId, spotifyApiTrack.name) } + + private val AuthorizationData.accessToken + get() = getModuleData(SpotifyModule.moduleName).accessToken } diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchRequest.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchRequest.kt new file mode 100644 index 0000000..a29008c --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchRequest.kt @@ -0,0 +1,19 @@ +package dev.fyloz.musicplayer.modules.spotify.api + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SearchRequest( + @SerialName("q") + val query: String, + + val type: String, + + @SerialName("include_external") + val includeExternal: String, + + val limit: Int, + + val offset: Int +) diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchResponse.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchResponse.kt new file mode 100644 index 0000000..8f28bdd --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/api/SearchResponse.kt @@ -0,0 +1,20 @@ +package dev.fyloz.musicplayer.modules.spotify.api + +import dev.fyloz.musicplayer.modules.spotify.SpotifyTrack +import kotlinx.serialization.Serializable + +@Serializable +data class SearchResponse( + val tracks: TrackSearchResponse +) + +@Serializable +data class TrackSearchResponse( + val href: String, + val items: Collection, + val limit: Int, + val next: String, + val offset: Int, + val previous: String?, + val total: Int +) diff --git a/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/config/SpotifyConfiguration.kt b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/config/SpotifyConfiguration.kt new file mode 100644 index 0000000..557cdb0 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/musicplayer/modules/spotify/config/SpotifyConfiguration.kt @@ -0,0 +1,15 @@ +package dev.fyloz.musicplayer.modules.spotify.config + +import io.ktor.server.config.* + +data class SpotifyConfiguration( + val clientId: String, + val clientSecret: String +) { + companion object { + fun fromEnvironment(config: ApplicationConfig) = SpotifyConfiguration( + config.property("modules.spotify.clientId").getString(), + config.property("modules.spotify.clientSecret").getString() + ) + } +} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf new file mode 100644 index 0000000..f21912e --- /dev/null +++ b/src/main/resources/application.conf @@ -0,0 +1,9 @@ +modules { + enabled = [ "spotify" ] + + spotify { + class = "dev.fyloz.musicplayer.modules.spotify.SpotifyModule" + clientId = "1372bd3ebcad4f889994f9a3f675472b" + clientSecret = "26ac249dc5ca4a309aa08f8cfcec9a60" + } +}