Things
This commit is contained in:
parent
1e9be2c7c2
commit
c0c7a56f96
|
@ -32,13 +32,14 @@ dependencies {
|
||||||
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
||||||
implementation("io.ktor:ktor-client-core:$ktor_version")
|
implementation("io.ktor:ktor-client-core:$ktor_version")
|
||||||
implementation("io.ktor:ktor-server-auth:$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-ktor:$koin_version")
|
||||||
implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
|
implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
|
||||||
implementation("ch.qos.logback:logback-classic:$logback_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("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||||
|
|
|
@ -1,27 +1,24 @@
|
||||||
package dev.fyloz.musicplayer.core
|
package dev.fyloz.musicplayer.core
|
||||||
|
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
import dev.fyloz.musicplayer.core.data.RepositoryInjection
|
import dev.fyloz.musicplayer.core.data.RepositoryInjection
|
||||||
import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy
|
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.http.configureTrackRoutes
|
||||||
import dev.fyloz.musicplayer.core.logic.TrackLogic
|
import dev.fyloz.musicplayer.core.logic.TrackLogic
|
||||||
import dev.fyloz.musicplayer.modules.spotify.SpotifyInjection
|
import dev.fyloz.musicplayer.modules.Module
|
||||||
import dev.fyloz.musicplayer.modules.spotify.SpotifyModule
|
|
||||||
import dev.fyloz.musicplayer.modules.spotify.SpotifyTrackFactory
|
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.apache.*
|
import io.ktor.client.plugins.logging.*
|
||||||
import io.ktor.http.*
|
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.config.*
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
import io.ktor.server.plugins.callloging.*
|
import io.ktor.server.plugins.callloging.*
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
import io.ktor.server.response.*
|
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
import org.koin.ktor.plugin.Koin
|
import org.koin.ktor.plugin.Koin
|
||||||
import org.koin.logger.slf4jLogger
|
import org.koin.logger.slf4jLogger
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
|
@ -32,6 +29,11 @@ fun main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Application.module() {
|
fun Application.module() {
|
||||||
|
val config = HoconApplicationConfig(ConfigFactory.load())
|
||||||
|
|
||||||
|
log.info("Loading modules...")
|
||||||
|
registerModules(config)
|
||||||
|
|
||||||
install(CallLogging) {
|
install(CallLogging) {
|
||||||
level = Level.DEBUG
|
level = Level.DEBUG
|
||||||
// filter { call -> call.request.path().startsWith("/") }
|
// filter { call -> call.request.path().startsWith("/") }
|
||||||
|
@ -46,24 +48,72 @@ fun Application.module() {
|
||||||
slf4jLogger()
|
slf4jLogger()
|
||||||
modules(
|
modules(
|
||||||
RepositoryInjection.koinBeans,
|
RepositoryInjection.koinBeans,
|
||||||
SpotifyInjection.koinBeans,
|
|
||||||
|
|
||||||
module {
|
module {
|
||||||
single { TrackFactoryProxy() }
|
single { TrackFactoryProxy() }
|
||||||
single { TrackLogic() }
|
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()
|
registeredModules.values.forEach { it.configure(this) }
|
||||||
spotifyModule.configure(this)
|
|
||||||
|
|
||||||
install(Routing) {
|
install(Routing) {
|
||||||
spotifyModule.configureRoutes(this)
|
registeredModules.values.forEach { it.configureRoutes(this) }
|
||||||
|
|
||||||
route("/api/v1") {
|
route("/api/v1") {
|
||||||
configureTrackRoutes()
|
configureTrackRoutes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val registeredModules = mutableMapOf<String, Module>()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package dev.fyloz.musicplayer.core
|
||||||
|
|
||||||
|
typealias KoinModule = org.koin.core.module.Module
|
|
@ -1,7 +1,9 @@
|
||||||
package dev.fyloz.musicplayer.core.factory
|
package dev.fyloz.musicplayer.core.factory
|
||||||
|
|
||||||
|
import dev.fyloz.musicplayer.core.http.auth.AuthorizationData
|
||||||
import dev.fyloz.musicplayer.core.model.Track
|
import dev.fyloz.musicplayer.core.model.Track
|
||||||
|
|
||||||
interface TrackFactory {
|
interface TrackFactory {
|
||||||
suspend fun createTrack(id: String, trackId: String): Track
|
suspend fun searchTrack(query: String, auth: AuthorizationData): Collection<Track>
|
||||||
|
suspend fun createTrack(id: String, trackId: String, auth: AuthorizationData): Track
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.fyloz.musicplayer.core.factory
|
package dev.fyloz.musicplayer.core.factory
|
||||||
|
|
||||||
|
import dev.fyloz.musicplayer.core.http.auth.AuthorizationData
|
||||||
import dev.fyloz.musicplayer.core.model.Track
|
import dev.fyloz.musicplayer.core.model.Track
|
||||||
|
|
||||||
class TrackFactoryProxy {
|
class TrackFactoryProxy {
|
||||||
|
@ -9,9 +10,11 @@ class TrackFactoryProxy {
|
||||||
factories[type] = factory;
|
factories[type] = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createTrack(type: String, id: String, trackId: String): Track {
|
suspend fun search(query: String, auth: AuthorizationData) =
|
||||||
return getFactory(type).createTrack(id, trackId)
|
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]!!;
|
private fun getFactory(type: String) = factories[type]!!;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.fyloz.musicplayer.core.http
|
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.http.requests.CreateTrackRequest
|
||||||
import dev.fyloz.musicplayer.core.logic.TrackLogic
|
import dev.fyloz.musicplayer.core.logic.TrackLogic
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
@ -16,9 +17,15 @@ fun Route.configureTrackRoutes() {
|
||||||
call.respond(logic.getAll().toList())
|
call.respond(logic.getAll().toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get("/search") {
|
||||||
|
val query = call.request.queryParameters["q"]!!
|
||||||
|
val tracks = logic.search(query, call.getAuthorizationData())
|
||||||
|
call.respond(tracks)
|
||||||
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
val request = call.receive<CreateTrackRequest>()
|
val request = call.receive<CreateTrackRequest>()
|
||||||
val track = logic.save(request.type, request.trackId)
|
val track = logic.save(request.type, request.trackId, call.getAuthorizationData())
|
||||||
call.respond(track)
|
call.respond(track)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.fyloz.musicplayer.core.http.auth
|
||||||
|
|
||||||
|
class AuthorizationData {
|
||||||
|
private val modulesData = mutableMapOf<String, Any>()
|
||||||
|
|
||||||
|
fun <T : Any> addModuleData(data: T, moduleName: String) {
|
||||||
|
modulesData[moduleName] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : Any> getModuleData(moduleName: String): T {
|
||||||
|
return modulesData[moduleName] as T
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ package dev.fyloz.musicplayer.core.logic
|
||||||
|
|
||||||
import dev.fyloz.musicplayer.core.data.TrackRepository
|
import dev.fyloz.musicplayer.core.data.TrackRepository
|
||||||
import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy
|
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.core.model.Track
|
||||||
import dev.fyloz.musicplayer.modules.spotify.SpotifyTrackFactory
|
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -15,9 +15,13 @@ class TrackLogic : KoinComponent {
|
||||||
fun getAll() = repository.findAll()
|
fun getAll() = repository.findAll()
|
||||||
fun getById(id: String) = repository.findById(id)
|
fun getById(id: String) = repository.findById(id)
|
||||||
|
|
||||||
suspend fun save(type: String, trackId: String): Track {
|
suspend fun search(query: String, auth: AuthorizationData): Collection<Track> {
|
||||||
|
return trackFactory.search(query, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun save(type: String, trackId: String, auth: AuthorizationData): Track {
|
||||||
val id = generateId()
|
val id = generateId()
|
||||||
val track = trackFactory.createTrack(type, id, trackId)
|
val track = trackFactory.createTrack(type, id, trackId, auth)
|
||||||
repository.save(track)
|
repository.save(track)
|
||||||
return track
|
return track
|
||||||
}
|
}
|
||||||
|
|
|
@ -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("/") }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,17 @@
|
||||||
package dev.fyloz.musicplayer.modules
|
package dev.fyloz.musicplayer.modules
|
||||||
|
|
||||||
|
import dev.fyloz.musicplayer.core.KoinModule
|
||||||
import dev.fyloz.musicplayer.core.factory.TrackFactory
|
import dev.fyloz.musicplayer.core.factory.TrackFactory
|
||||||
import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy
|
import dev.fyloz.musicplayer.core.factory.TrackFactoryProxy
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.config.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import org.koin.ktor.ext.inject
|
import org.koin.ktor.ext.inject
|
||||||
|
|
||||||
abstract class Module(private val moduleName: String) {
|
abstract class Module(private val moduleName: String) {
|
||||||
|
open fun setupDependencyInjection(module: KoinModule, config: ApplicationConfig) {
|
||||||
|
}
|
||||||
|
|
||||||
open fun configure(app: Application) {
|
open fun configure(app: Application) {
|
||||||
with(app) {
|
with(app) {
|
||||||
val trackFactoryProxy by inject<TrackFactoryProxy>()
|
val trackFactoryProxy by inject<TrackFactoryProxy>()
|
||||||
|
@ -26,6 +31,8 @@ abstract class Module(private val moduleName: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun getAuthorizationData(call: ApplicationCall): Any? = null
|
||||||
|
|
||||||
protected open fun Route.configureApiRoutes() {
|
protected open fun Route.configureApiRoutes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<HttpClient>()
|
||||||
|
|
||||||
|
suspend fun search(query: String, type: String, accessToken: String): Collection<Track> {
|
||||||
|
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<SearchResponse>()
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package dev.fyloz.musicplayer.modules.spotify
|
||||||
|
|
||||||
|
data class SpotifyAuthorizationData(val accessToken: String)
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +1,76 @@
|
||||||
package dev.fyloz.musicplayer.modules.spotify
|
package dev.fyloz.musicplayer.modules.spotify
|
||||||
|
|
||||||
|
import dev.fyloz.musicplayer.core.KoinModule
|
||||||
import dev.fyloz.musicplayer.modules.Module
|
import dev.fyloz.musicplayer.modules.Module
|
||||||
|
import dev.fyloz.musicplayer.modules.spotify.config.SpotifyConfiguration
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
|
import io.ktor.server.config.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
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) {
|
override fun configure(app: Application) {
|
||||||
super.configure(app)
|
super.configure(app)
|
||||||
|
|
||||||
|
val httpClient by app.inject<HttpClient>()
|
||||||
|
val config by app.inject<SpotifyConfiguration>()
|
||||||
|
|
||||||
with(app) {
|
with(app) {
|
||||||
authentication {
|
authentication {
|
||||||
oauth("auth-oauth-spotify") {
|
oauth(oauthName) {
|
||||||
urlProvider = { "http://localhost:8080/module/spotify/login-callback" }
|
urlProvider = { "http://localhost:8080/module/spotify/login-callback" }
|
||||||
providerLookup = {
|
providerLookup = {
|
||||||
OAuthServerSettings.OAuth2ServerSettings(
|
OAuthServerSettings.OAuth2ServerSettings(
|
||||||
name = "spotify",
|
name = moduleName,
|
||||||
authorizeUrl = "https://accounts.spotify.com/authorize",
|
authorizeUrl = "https://accounts.spotify.com/authorize",
|
||||||
accessTokenUrl = "https://accounts.spotify.com/api/token",
|
accessTokenUrl = "https://accounts.spotify.com/api/token",
|
||||||
requestMethod = HttpMethod.Post,
|
requestMethod = HttpMethod.Post,
|
||||||
clientId = "1372bd3ebcad4f889994f9a3f675472b",
|
clientId = config.clientId,
|
||||||
clientSecret = "26ac249dc5ca4a309aa08f8cfcec9a60"
|
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() {
|
override fun Route.configureModuleRoutes() {
|
||||||
authenticate("auth-oauth-spotify") {
|
authenticate(oauthName) {
|
||||||
get("/login") {
|
get("/login") {
|
||||||
call.respondRedirect("https://accounts.spotify.com/authorize")
|
call.respondRedirect("https://accounts.spotify.com/authorize")
|
||||||
}
|
}
|
||||||
|
|
||||||
get("/login-callback") {
|
get("/login-callback") {
|
||||||
val principal: OAuthAccessTokenResponse.OAuth2? = call.principal()
|
call.principal<OAuthAccessTokenResponse.OAuth2>()?.let {
|
||||||
if (principal != null) {
|
with(call.response.cookies) {
|
||||||
call.response.cookies.append("Spotify-Access-Token", principal.accessToken)
|
append("Spotify-Access-Token", it.accessToken, maxAge = it.expiresIn)
|
||||||
|
append("Spotify-Refresh-Token", it.refreshToken!!)
|
||||||
if (principal.refreshToken != null) {
|
|
||||||
call.response.cookies.append("Spotify-Refresh-Token", principal.refreshToken!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpotifyTokenLogic(http)
|
|
||||||
} else {
|
|
||||||
call.respond(HttpStatusCode.Unauthorized)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTrackFactory() = SpotifyTrackFactory()
|
override fun getTrackFactory() = SpotifyTrackFactory()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val moduleName = "spotify"
|
||||||
|
const val oauthName = "auth-oauth-spotify"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<HttpClient>()
|
|
||||||
|
|
||||||
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<AccessTokenResponse>()
|
|
||||||
println(response.accessToken)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,22 @@
|
||||||
package dev.fyloz.musicplayer.modules.spotify
|
package dev.fyloz.musicplayer.modules.spotify
|
||||||
|
|
||||||
import dev.fyloz.musicplayer.core.factory.TrackFactory
|
import dev.fyloz.musicplayer.core.factory.TrackFactory
|
||||||
|
import dev.fyloz.musicplayer.core.http.auth.AuthorizationData
|
||||||
import dev.fyloz.musicplayer.core.model.Track
|
import dev.fyloz.musicplayer.core.model.Track
|
||||||
|
|
||||||
class SpotifyTrackFactory : TrackFactory {
|
class SpotifyTrackFactory : TrackFactory {
|
||||||
private val httpProvider = SpotifyHttpProvider()
|
private val apiProvider = SpotifyApiProvider()
|
||||||
|
|
||||||
override suspend fun createTrack(id: String, trackId: String): Track {
|
override suspend fun searchTrack(query: String, auth: AuthorizationData): Collection<Track> {
|
||||||
val spotifyApiTrack = httpProvider.getTrackById(trackId, "bla");
|
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)
|
return SpotifyTrack(id, trackId, spotifyApiTrack.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val AuthorizationData.accessToken
|
||||||
|
get() = getModuleData<SpotifyAuthorizationData>(SpotifyModule.moduleName).accessToken
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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<Track>,
|
||||||
|
val limit: Int,
|
||||||
|
val next: String,
|
||||||
|
val offset: Int,
|
||||||
|
val previous: String?,
|
||||||
|
val total: Int
|
||||||
|
)
|
|
@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
modules {
|
||||||
|
enabled = [ "spotify" ]
|
||||||
|
|
||||||
|
spotify {
|
||||||
|
class = "dev.fyloz.musicplayer.modules.spotify.SpotifyModule"
|
||||||
|
clientId = "1372bd3ebcad4f889994f9a3f675472b"
|
||||||
|
clientSecret = "26ac249dc5ca4a309aa08f8cfcec9a60"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue