summaryrefslogtreecommitdiff
path: root/core/src/commonMain/kotlin/coffee/liz/rookery/images/BraveImages.kt
blob: 33b3a224c1ee7362ce0c9f2b3381befeb2756430 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package coffee.liz.rookery.images

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonObject

/** One image search result: a small [thumbnail] to show in the picker and the [full] image to keep. */
data class ImageHit(val title: String, val thumbnail: String, val full: String, val source: String)

/** Brave Search image API: request building and response parsing (the HTTP call itself is the host's job). */
object BraveImages {
    const val KEY_HEADER = "X-Subscription-Token"

    fun url(query: String, count: Int = 12): String =
        "https://api.search.brave.com/res/v1/images/search?q=${encode(query)}&count=$count&safesearch=strict"

    fun parse(body: String): List<ImageHit> {
        val root = Json.parseToJsonElement(body).jsonObject
        return (root["results"] as? JsonArray).orEmpty().mapNotNull { e ->
            val o = e as? JsonObject ?: return@mapNotNull null
            val thumb = (o["thumbnail"] as? JsonObject)?.str("src")
            val full = (o["properties"] as? JsonObject)?.str("url")
            val any = thumb ?: full ?: return@mapNotNull null
            if (!any.startsWith("https://")) return@mapNotNull null
            ImageHit(o.str("title").orEmpty(), thumb ?: any, full?.takeIf { it.startsWith("https://") } ?: any, o.str("source").orEmpty())
        }
    }

    private fun JsonObject.str(k: String) = (this[k] as? JsonPrimitive)?.contentOrNull

    private fun encode(s: String): String = buildString {
        for (b in s.encodeToByteArray()) {
            val c = b.toInt().toChar()
            if (c.isLetterOrDigit() && b >= 0 || c in "-_.~") append(c)
            else append('%').append((b.toInt() and 0xFF).toString(16).uppercase().padStart(2, '0'))
        }
    }
}