Add plugin for SteamGridDB game covers

This commit is contained in:
grimsi
2025-05-10 13:00:14 +02:00
parent e49f61a1db
commit 432b27adfc
10 changed files with 218 additions and 10 deletions
+1 -5
View File
@@ -1,14 +1,10 @@
val ktor_version = "3.1.2"
val ktor_version = "3.1.3"
plugins {
id("com.google.devtools.ksp")
kotlin("plugin.serialization")
}
repositories {
maven(url = "https://jitpack.io")
}
dependencies {
ksp("care.better.pf4j:pf4j-kotlin-symbol-processing:${rootProject.extra["pf4jKspVersion"]}")
+15
View File
@@ -0,0 +1,15 @@
val ktor_version = "3.1.3"
plugins {
id("com.google.devtools.ksp")
kotlin("plugin.serialization")
}
dependencies {
ksp("care.better.pf4j:pf4j-kotlin-symbol-processing:${rootProject.extra["pf4jKspVersion"]}")
implementation("io.ktor:ktor-client-core:${ktor_version}")
implementation("io.ktor:ktor-client-cio:${ktor_version}")
implementation("io.ktor:ktor-client-content-negotiation:${ktor_version}")
implementation("io.ktor:ktor-serialization-kotlinx-json:${ktor_version}")
}
@@ -0,0 +1,104 @@
package de.grimsi.gameyfin.plugins.steamgriddb
import de.grimsi.gameyfin.pluginapi.core.GameyfinPlugin
import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
import de.grimsi.gameyfin.pluginapi.core.PluginConfigError
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
import de.grimsi.gameyfin.plugins.steamgriddb.api.SteamGridDbApiClient
import de.grimsi.gameyfin.plugins.steamgriddb.dto.SteamGridDbGame
import de.grimsi.gameyfin.plugins.steamgriddb.dto.SteamGridDbGrid
import kotlinx.coroutines.runBlocking
import org.pf4j.Extension
import org.pf4j.PluginWrapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URI
class SteamGridDbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
companion object {
private var client: SteamGridDbApiClient? = null
}
val log: Logger = LoggerFactory.getLogger(javaClass)
override val configMetadata: List<PluginConfigElement> = listOf(
PluginConfigElement("apiKey", "SteamGridDB API key", "Your SteamGridDB API key", true)
)
override fun validateConfig(config: Map<String, String?>): Boolean {
try {
runBlocking { authenticate() }
return true
} catch (e: PluginConfigError) {
log.error(e.message)
return false
}
}
override fun start() {
try {
runBlocking { authenticate() }
} catch (e: PluginConfigError) {
log.error(e.message)
}
}
private suspend fun authenticate() {
log.debug("Authenticating on SteamGridDB API...")
val apiKey: String = config["apiKey"] ?: throw PluginConfigError("SteamGridDB API key not set")
val client = SteamGridDbApiClient(apiKey)
if (!client.isApiKeyValid()) {
throw PluginConfigError("Failed to authenticate on SteamGridDB API with provided credentials")
}
SteamGridDbPlugin.client = client
log.debug("Authentication successful")
}
@Extension
class SteamGridDBGameCoverProvider : GameMetadataProvider {
override fun fetchMetadata(gameId: String, maxResults: Int): List<GameMetadata> {
return runBlocking {
var searchResults = searchSteamGridDb(gameId)
if (searchResults.isEmpty()) return@runBlocking emptyList()
if (searchResults.size > maxResults) searchResults = searchResults.slice(0 until maxResults)
return@runBlocking searchResults
.map { game ->
GameMetadata(
originalId = game.id.toString(),
title = game.name,
coverUrl = getGridForGame(game.id)?.let { grid -> URI(grid.url) }
)
}
.filter { it.coverUrl != null }
}
}
private suspend fun searchSteamGridDb(term: String): List<SteamGridDbGame> {
val client = client ?: throw PluginConfigError("SteamGridDB API client not initialized")
val searchResult = client.search(term)
return if (searchResult.success && searchResult.data !== null) {
searchResult.data
} else {
emptyList()
}
}
private suspend fun getGridForGame(gameId: Int): SteamGridDbGrid? {
val client = client ?: throw PluginConfigError("SteamGridDB API client not initialized")
val gameDetails = client.grids(gameId)
return gameDetails.data?.firstOrNull()
}
}
}
@@ -0,0 +1,54 @@
package de.grimsi.gameyfin.plugins.steamgriddb.api
import de.grimsi.gameyfin.plugins.steamgriddb.dto.SteamGridDbGridResult
import de.grimsi.gameyfin.plugins.steamgriddb.dto.SteamGridDbSearchResult
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
class SteamGridDbApiClient(private val apiKey: String) {
companion object {
private val json = Json {
isLenient = true
ignoreUnknownKeys = true
}
private const val BASE_URL = "https://www.steamgriddb.com/api/v2"
}
private val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(json)
}
}
suspend fun isApiKeyValid(): Boolean {
return try {
val response = get("grids/game/1")
response.status == HttpStatusCode.OK
} catch (_: Exception) {
false
}
}
suspend fun search(term: String, block: HttpRequestBuilder.() -> Unit = {}): SteamGridDbSearchResult {
return get("search/autocomplete/$term", block).body()
}
suspend fun grids(gameId: Int, block: HttpRequestBuilder.() -> Unit = {}): SteamGridDbGridResult {
return get("grids/game/$gameId", block).body()
}
private suspend fun get(endpoint: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse {
return client.get("$BASE_URL/$endpoint".encodeURLPath(encodeEncoded = false)) {
bearerAuth(apiKey)
block()
}
}
}
@@ -0,0 +1,15 @@
package de.grimsi.gameyfin.plugins.steamgriddb.dto
import kotlinx.serialization.Serializable
@Serializable
data class SteamGridDbSearchResult(
val success: Boolean,
val data: List<SteamGridDbGame>?
)
@Serializable
data class SteamGridDbGame(
val id: Int,
val name: String
)
@@ -0,0 +1,17 @@
package de.grimsi.gameyfin.plugins.steamgriddb.dto
import kotlinx.serialization.Serializable
@Serializable
data class SteamGridDbGridResult(
val success: Boolean,
val data: List<SteamGridDbGrid>?
)
@Serializable
data class SteamGridDbGrid(
val id: Int,
val width: Int,
val height: Int,
val url: String
)
@@ -0,0 +1,6 @@
Manifest-Version: 1.0
Plugin-Class: de.grimsi.gameyfin.plugins.steamgriddb.SteamGridDbPlugin
Plugin-Id: steamgriddb
Plugin-Description: Steam Grid DB covers
Plugin-Version: 1.0.0-alpha1
Plugin-Provider: grimsi
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 50 24"><path fill="#305b79" d="M0 0h41v18H0V0z"/><path fill="#4787b4" d="M3 2h41v18H3V2z"/><path fill="#5fb4f0" d="M6 4h41v18H6V4z"/><path fill="#3a6e92" d="M9 6h41v18H9V6z"/></svg>

After

Width:  |  Height:  |  Size: 246 B