mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Fix URL encoding of search queries
This commit is contained in:
@@ -9,16 +9,16 @@ dependencies {
|
||||
ksp("care.better.pf4j:pf4j-kotlin-symbol-processing:${rootProject.extra["pf4jKspVersion"]}")
|
||||
|
||||
implementation("io.ktor:ktor-client-core:$ktor_version") {
|
||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
implementation("io.ktor:ktor-client-cio:$ktor_version") {
|
||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version") {
|
||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version") {
|
||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
|
||||
implementation("me.xdrop:fuzzywuzzy:1.4.0")
|
||||
|
||||
@@ -10,6 +10,7 @@ import de.grimsi.gameyfinplugins.steam.mapper.Mapper
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
@@ -22,8 +23,6 @@ import org.pf4j.Extension
|
||||
import org.pf4j.PluginWrapper
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.URI
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class SteamPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
||||
|
||||
@@ -39,6 +38,9 @@ class SteamPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
||||
private val log = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
val client = HttpClient(CIO) {
|
||||
// Use a fake browser user agent to avoid being blocked by Steam
|
||||
BrowserUserAgent()
|
||||
|
||||
install(ContentNegotiation) {
|
||||
json(json)
|
||||
}
|
||||
@@ -64,10 +66,9 @@ class SteamPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
||||
}
|
||||
|
||||
private suspend fun searchStore(title: String): List<SteamGame> {
|
||||
val encodedTitle = URLEncoder.encode(title, StandardCharsets.UTF_8.toString())
|
||||
return try {
|
||||
val response = client.get("https://store.steampowered.com/api/storesearch") {
|
||||
parameter("term", encodedTitle)
|
||||
parameter("term", title)
|
||||
parameter("cc", "en")
|
||||
parameter("l", "en")
|
||||
}
|
||||
|
||||
+48
-29
@@ -5,6 +5,8 @@ import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneOffset
|
||||
@@ -13,46 +15,63 @@ import java.util.*
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(forClass = Instant::class)
|
||||
class SteamDateSerializer : KSerializer<Instant> {
|
||||
class SteamDateSerializer : KSerializer<Instant?> {
|
||||
|
||||
companion object {
|
||||
val log: Logger = LoggerFactory.getLogger(SteamDateSerializer::class.java)
|
||||
|
||||
const val COMING_SOON_TEXT = "Coming Soon"
|
||||
val COMING_SOON_FALLBACK_DATE: LocalDate = LocalDate.parse("2999-12-31")
|
||||
|
||||
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("d MMM, yyyy", Locale.ENGLISH)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Instant = fromString(decoder.decodeString())
|
||||
override fun deserialize(decoder: Decoder): Instant? = fromString(decoder.decodeString())
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Instant) = encoder.encodeString(value.toString())
|
||||
override fun serialize(encoder: Encoder, value: Instant?) = encoder.encodeString(value.toString())
|
||||
|
||||
private fun fromString(dateString: String): Instant {
|
||||
// Match "Coming Soon" and return a fallback date
|
||||
if (dateString.equals(COMING_SOON_TEXT, true)) {
|
||||
return COMING_SOON_FALLBACK_DATE.atStartOfDay().toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
// Match quarters like "Q1 2023", "Q2 2023", etc.
|
||||
val quarterMatch = Regex("""Q([1-4]) (\d{4})""").matchEntire(dateString)
|
||||
if (quarterMatch != null) {
|
||||
val (qStr, yearStr) = quarterMatch.destructured
|
||||
val month = when (qStr.toInt()) {
|
||||
1 -> 1
|
||||
2 -> 4
|
||||
3 -> 7
|
||||
4 -> 10
|
||||
else -> 1
|
||||
private fun fromString(dateString: String): Instant? {
|
||||
return try {
|
||||
// Return null for empty strings
|
||||
if (dateString.isBlank()) {
|
||||
return null
|
||||
}
|
||||
return LocalDate.of(yearStr.toInt(), month, 1)
|
||||
.atStartOfDay()
|
||||
.toInstant(ZoneOffset.UTC)
|
||||
|
||||
// Match "Coming Soon" and return a fallback date
|
||||
if (dateString.equals(COMING_SOON_TEXT, true)) {
|
||||
return COMING_SOON_FALLBACK_DATE.atStartOfDay().toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
// Match quarters like "Q1 2023", "Q2 2023", etc.
|
||||
val quarterMatch = Regex("""Q([1-4]) (\d{4})""").matchEntire(dateString)
|
||||
if (quarterMatch != null) {
|
||||
val (qStr, yearStr) = quarterMatch.destructured
|
||||
val month = when (qStr.toInt()) {
|
||||
1 -> 1
|
||||
2 -> 4
|
||||
3 -> 7
|
||||
4 -> 10
|
||||
else -> 1
|
||||
}
|
||||
return LocalDate.of(yearStr.toInt(), month, 1)
|
||||
.atStartOfDay()
|
||||
.toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
// Match year only
|
||||
val yearMatch = Regex("""^(\d{4})$""").matchEntire(dateString)
|
||||
if (yearMatch != null) {
|
||||
val (yearStr) = yearMatch.destructured
|
||||
return LocalDate.of(yearStr.toInt(), 1, 1)
|
||||
.atStartOfDay()
|
||||
.toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
val localDate = LocalDate.parse(dateString, formatter)
|
||||
return localDate.atStartOfDay().toInstant(ZoneOffset.UTC)
|
||||
} catch (_: Exception) {
|
||||
log.warn("Couldn't parse date string: '$dateString'")
|
||||
null // Return null if parsing fails
|
||||
}
|
||||
|
||||
val localDate = LocalDate.parse(dateString, formatter)
|
||||
return localDate.atStartOfDay().toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
private fun toString(date: Instant): String {
|
||||
return formatter.format(date.atZone(ZoneOffset.UTC))
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ class SteamGridDbApiClient(private val apiKey: String) {
|
||||
}
|
||||
|
||||
suspend fun search(term: String, block: HttpRequestBuilder.() -> Unit = {}): SteamGridDbSearchResult {
|
||||
return get("search/autocomplete/$term", block).body()
|
||||
return get("search/autocomplete/${term.encodeURLPath(encodeSlash = true, encodeEncoded = false)}", block).body()
|
||||
}
|
||||
|
||||
suspend fun grids(gameId: Int, block: HttpRequestBuilder.() -> Unit = {}): SteamGridDbGridResult {
|
||||
|
||||
Reference in New Issue
Block a user