upgrade up to multiplatform project

This commit is contained in:
InsanusMokrassar 2020-08-22 23:00:07 +06:00
parent 7cbaac8a3e
commit fe32aaacb2
33 changed files with 241 additions and 187 deletions

View File

@ -3,6 +3,13 @@
### 0.6.0 ### 0.6.0
* All known fields were added to `ResultData` * All known fields were added to `ResultData`
* Versions updates:
* `Kotlin`: `1.3.72` -> `1.4.0`
* `Coroutines`: `1.3.8` -> `1.3.9`
* `Serialization`: `0.20.0` -> `1.0.0-RC`
* `Klock`: `1.11.14` -> `1.12.0`
* `Ktor`: `1.3.2` -> `1.4.0`
## 0.5.0 ## 0.5.0

View File

@ -1,6 +1,3 @@
project.version = "0.6.0"
project.group = "com.github.insanusmokrassar"
buildscript { buildscript {
repositories { repositories {
mavenLocal() mavenLocal()
@ -15,9 +12,14 @@ buildscript {
} }
} }
apply plugin: 'kotlin' plugins {
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
}
apply plugin: 'kotlinx-serialization' apply plugin: 'kotlinx-serialization'
project.version = "0.6.0"
project.group = "com.github.insanusmokrassar"
apply from: "publish.gradle" apply from: "publish.gradle"
repositories { repositories {
@ -25,17 +27,48 @@ repositories {
jcenter() jcenter()
mavenCentral() mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" } maven { url "https://kotlin.bintray.com/kotlinx" }
maven { url "https://dl.bintray.com/kotlin/ktor" }
} }
dependencies { kotlin {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" jvm()
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" js(BOTH) {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialisation_runtime_version" browser()
implementation "com.soywiz.korlibs.klock:klock:$klock_version" nodejs()
implementation "io.ktor:ktor-client-core:$ktor_version" }
implementation "io.ktor:ktor-client-okhttp:$ktor_version"
// Use JUnit test framework
testImplementation 'junit:junit:4.13' sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlin_serialisation_runtime_version"
api "com.soywiz.korlibs.klock:klock:$klock_version"
api "io.ktor:ktor-client-core:$ktor_version"
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {
dependencies {
implementation kotlin('stdlib')
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
implementation "io.ktor:ktor-client-okhttp:$ktor_version"
}
}
jsMain {
dependencies {
implementation kotlin('test-js')
}
}
}
} }

View File

@ -1,11 +1,8 @@
kotlin.code.style=official kotlin.code.style=official
kotlin_version=1.3.72 kotlin_version=1.4.0
kotlin_coroutines_version=1.3.8 kotlin_coroutines_version=1.3.9
kotlin_serialisation_runtime_version=0.20.0 kotlin_serialisation_runtime_version=1.0.0-RC
klock_version=1.11.14 klock_version=1.12.0
ktor_version=1.3.2 ktor_version=1.4.0
project_public_name=SauceNao API gradle_bintray_plugin_version=1.8.5
project_public_description=SauceNao API library
gradle_bintray_plugin_version=1.8.4

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip

View File

@ -1,63 +1,53 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
apply plugin: 'signing'
task sourcesJar(type: Jar) { task javadocsJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}
task javadocJar(type: Jar) {
from javadoc
classifier = 'javadoc' classifier = 'javadoc'
} }
publishing { afterEvaluate {
publications { project.publishing.publications.all {
maven(MavenPublication) { // rename artifacts
from components.java groupId "${project.group}"
groupId "${project.group}" if (it.name.contains('kotlinMultiplatform')) {
artifactId "${project.name}" artifactId = "${project.name}"
version "${project.version}" } else {
artifactId = "${project.name}-$name"
artifact sourcesJar
artifact javadocJar
pom.withXml {
asNode().children().last() + {
resolveStrategy = Closure.DELEGATE_FIRST
name "${project_public_name}"
description "${project_public_description}"
url "https://insanusmokrassar.github.io/${project.name}"
scm {
connection "scm:git:git://github.com/insanusmokrassar/${project.name}.git"
developerConnection "scm:git:[fetch=]https://github.com/insanusmokrassar/${project.name}.git[push=]ssh:git@github.com:insanusmokrassar/${project.name}.git"
url "https://github.com/insanusmokrassar/${project.name}"
}
developers {
developer {
id "InsanusMokrassar"
name "Ovsyannikov Alexey"
email "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE'
distribution 'repo'
}
}
}
}
} }
} }
} }
signing { publishing {
useGpgCmd() publications.all {
sign publishing.publications.maven artifact javadocsJar
}
pom {
description = "SauceNao API library"
name = "SauceNao API"
url = "https://insanusmokrassar.github.io/${project.name}"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/insanusmokrassar/${project.name}.git[push=]https://github.com/insanusmokrassar/${project.name}.git"
url = "https://github.com/insanusmokrassar/${project.name}.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsyannikov Alexey"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
}
}
}
}
}

1
publication.kpsb Normal file
View File

@ -0,0 +1 @@
{"bintrayConfig":{"repo":"StandardRepository","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/${project.name}"},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"SauceNao API","description":"SauceNao API library","url":"https://insanusmokrassar.github.io/${project.name}","vcsUrl":"https://github.com/insanusmokrassar/${project.name}.git","developers":[{"id":"InsanusMokrassar","name":"Ovsyannikov Alexey","eMail":"ovsyannikov.alexey95@gmail.com"}]},"type":"Multiplatform"}

View File

@ -1,33 +1,55 @@
apply plugin: 'com.jfrog.bintray' apply plugin: 'com.jfrog.bintray'
ext { apply from: "maven.publish.gradle"
projectBintrayDir = "${project.group}/".replace(".", "/") + "${project.name}/${project.version}"
}
bintray { bintray {
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
publications = ["maven"]
filesSpec { filesSpec {
into "$projectBintrayDir" from "${buildDir}/publications/"
from("build/libs") { eachFile {
include "**/*.asc" String directorySubname = it.getFile().parentFile.name
} if (it.getName() == "module.json") {
from("build/publications/maven") { if (directorySubname == "kotlinMultiplatform") {
rename 'pom-default.xml(.*)', "${project.name}-${project.version}.pom\$1" it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
} else {
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
}
} else {
if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom")
} else {
it.exclude()
}
}
} }
into "${project.group}".replace(".", "/")
} }
pkg { pkg {
repo = 'StandardRepository' repo = "StandardRepository"
name = "${project.name}" name = "${project.name}"
vcsUrl = "https://github.com/InsanusMokrassar/${project.name}" vcsUrl = "https://github.com/InsanusMokrassar/${project.name}"
licenses = ['Apache-2.0'] licenses = ["Apache-2.0"]
version { version {
name = "${project.version}" name = "${project.version}"
released = new Date() released = new Date()
vcsTag = name vcsTag = "${project.version}"
gpg {
sign = true
passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase')
}
} }
} }
} }
apply from: "maven.publish.gradle" bintrayUpload.doFirst {
publications = publishing.publications.collect {
if (it.name.contains('kotlinMultiplatform')) {
null
} else {
it.name
}
} - null
}
bintrayUpload.dependsOn publishToMavenLocal

View File

@ -5,20 +5,18 @@ import com.github.insanusmokrassar.SauceNaoAPI.exceptions.sauceNaoAPIException
import com.github.insanusmokrassar.SauceNaoAPI.models.* import com.github.insanusmokrassar.SauceNaoAPI.models.*
import com.github.insanusmokrassar.SauceNaoAPI.utils.* import com.github.insanusmokrassar.SauceNaoAPI.utils.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.call
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.features.ClientRequestException import io.ktor.client.features.ClientRequestException
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.request.forms.* import io.ktor.client.request.forms.*
import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.readText import io.ktor.client.statement.readText
import io.ktor.http.* import io.ktor.http.*
import io.ktor.utils.io.core.Closeable
import io.ktor.utils.io.core.Input import io.ktor.utils.io.core.Input
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.Closeable import kotlinx.serialization.json.nonstrict
import java.util.logging.Logger
import kotlin.Result import kotlin.Result
import kotlin.coroutines.* import kotlin.coroutines.*
@ -35,15 +33,21 @@ private const val MINIMAL_SIMILARITY_FIELD = "minsim"
private const val SEARCH_URL = "https://saucenao.com/search.php" private const val SEARCH_URL = "https://saucenao.com/search.php"
val defaultSauceNaoParser = Json {
allowSpecialFloatingPointValues = true
allowStructuredMapKeys = true
ignoreUnknownKeys = true
useArrayPolymorphism = true
}
data class SauceNaoAPI( data class SauceNaoAPI(
private val apiToken: String? = null, private val apiToken: String? = null,
private val outputType: OutputType = JsonOutputType, private val outputType: OutputType = JsonOutputType,
private val client: HttpClient = HttpClient(OkHttp), private val client: HttpClient = HttpClient(),
private val searchUrl: String = SEARCH_URL, private val searchUrl: String = SEARCH_URL,
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default) private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
private val parser: Json = defaultSauceNaoParser
) : Closeable { ) : Closeable {
private val logger = Logger.getLogger("SauceNaoAPI")
private val requestsChannel = Channel<Pair<Continuation<SauceNaoAnswer>, HttpRequestBuilder>>(Channel.UNLIMITED) private val requestsChannel = Channel<Pair<Continuation<SauceNaoAnswer>, HttpRequestBuilder>>(Channel.UNLIMITED)
private val timeManager = TimeManager(scope) private val timeManager = TimeManager(scope)
private val quotaManager = RequestQuotaManager(scope) private val quotaManager = RequestQuotaManager(scope)
@ -61,7 +65,6 @@ data class SauceNaoAPI(
quotaManager.updateQuota(answer.header, timeManager) quotaManager.updateQuota(answer.header, timeManager)
} catch (e: TooManyRequestsException) { } catch (e: TooManyRequestsException) {
logger.warning("Exceed time limit. Answer was:\n${e.answerContent}")
quotaManager.happenTooManyRequests(timeManager, e) quotaManager.happenTooManyRequests(timeManager, e)
requestsChannel.send(callback to requestBuilder) requestsChannel.send(callback to requestBuilder)
} catch (e: Exception) { } catch (e: Exception) {
@ -87,7 +90,7 @@ data class SauceNaoAPI(
suspend fun request( suspend fun request(
mediaInput: Input, mediaInput: Input,
mimeType: ContentType = mediaInput.mimeType, mimeType: ContentType,
resultsCount: Int? = null, resultsCount: Int? = null,
minSimilarity: Float? = null minSimilarity: Float? = null
): SauceNaoAnswer? = makeRequest( ): SauceNaoAnswer? = makeRequest(
@ -138,9 +141,8 @@ data class SauceNaoAPI(
return try { return try {
val call = client.request<HttpResponse>(builder) val call = client.request<HttpResponse>(builder)
val answerText = call.readText() val answerText = call.readText()
logger.info(answerText)
timeManager.addTimeAndClear() timeManager.addTimeAndClear()
Json.nonstrict.parse( parser.decodeFromString(
SauceNaoAnswerSerializer, SauceNaoAnswerSerializer,
answerText answerText
) )

View File

@ -9,6 +9,7 @@ import io.ktor.http.HttpStatusCode.Companion.TooManyRequests
import io.ktor.utils.io.errors.IOException import io.ktor.utils.io.errors.IOException
internal suspend fun ClientRequestException.sauceNaoAPIException(): Exception { internal suspend fun ClientRequestException.sauceNaoAPIException(): Exception {
val response = response ?: return this
return when (response.status) { return when (response.status) {
TooManyRequests -> { TooManyRequests -> {
val answerContent = response.readText() val answerContent = response.readText()
@ -21,14 +22,14 @@ internal suspend fun ClientRequestException.sauceNaoAPIException(): Exception {
} }
} }
sealed class TooManyRequestsException : IOException() { sealed class TooManyRequestsException(message: String, cause: Throwable? = null) : IOException(message, cause) {
abstract val answerContent: String abstract val answerContent: String
abstract val waitTime: TimeSpan abstract val waitTime: TimeSpan
} }
class TooManyRequestsShortException(override val answerContent: String) : TooManyRequestsException() { class TooManyRequestsShortException(override val answerContent: String) : TooManyRequestsException("Too many requests were sent in the short period") {
override val waitTime: TimeSpan = SHORT_TIME_RECALCULATING_MILLIS override val waitTime: TimeSpan = SHORT_TIME_RECALCULATING_MILLIS
} }
class TooManyRequestsLongException(override val answerContent: String) : TooManyRequestsException() { class TooManyRequestsLongException(override val answerContent: String) : TooManyRequestsException("Too many requests were sent in the long period") {
override val waitTime: TimeSpan = LONG_TIME_RECALCULATING_MILLIS override val waitTime: TimeSpan = LONG_TIME_RECALCULATING_MILLIS
} }

View File

@ -1,9 +1,12 @@
package com.github.insanusmokrassar.SauceNaoAPI.models package com.github.insanusmokrassar.SauceNaoAPI.models
import com.github.insanusmokrassar.SauceNaoAPI.defaultSauceNaoParser
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.internal.StringDescriptor import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.json.JsonObjectSerializer import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
@Serializable @Serializable
data class Header( data class Header(
@ -39,21 +42,24 @@ data class Header(
) )
internal object IndexesSerializer : KSerializer<List<HeaderIndex?>> { internal object IndexesSerializer : KSerializer<List<HeaderIndex?>> {
override val descriptor: SerialDescriptor = StringDescriptor override val descriptor: SerialDescriptor = String.serializer().descriptor
override fun deserialize(decoder: Decoder): List<HeaderIndex?> { override fun deserialize(decoder: Decoder): List<HeaderIndex?> {
val json = JsonObjectSerializer.deserialize(decoder) val json = JsonObject.serializer().deserialize(decoder)
val parsed = json.keys.mapNotNull { it.toIntOrNull() }.sorted().mapNotNull { val parsed = json.keys.mapNotNull { it.toIntOrNull() }.sorted().mapNotNull {
val jsonObject = json.getObjectOrNull(it.toString()) ?: return@mapNotNull null val jsonObject = json[it.toString()] ?.jsonObject ?: return@mapNotNull null
val index = Json.nonstrict.parse(HeaderIndex.serializer(), Json.stringify(JsonObjectSerializer, jsonObject)) val index = defaultSauceNaoParser.decodeFromString(
HeaderIndex.serializer(),
defaultSauceNaoParser.encodeToString(JsonObject.serializer(), jsonObject)
)
it to index it to index
}.toMap() }.toMap()
return Array<HeaderIndex?>(parsed.keys.max() ?: 0) { return Array<HeaderIndex?>(parsed.keys.maxOrNull() ?: 0) {
parsed[it] parsed[it]
}.toList() }.toList()
} }
override fun serialize(encoder: Encoder, obj: List<HeaderIndex?>) { override fun serialize(encoder: Encoder, value: List<HeaderIndex?>) {
TODO() TODO()
} }
} }

View File

@ -0,0 +1,55 @@
package com.github.insanusmokrassar.SauceNaoAPI.models
import com.github.insanusmokrassar.SauceNaoAPI.defaultSauceNaoParser
import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
@Serializable
private data class TemporalSauceNaoAnswerRepresentation(
val header: Header,
val results: List<Result> = emptyList(),
)
@Serializable(SauceNaoAnswerSerializer::class)
data class SauceNaoAnswer internal constructor(
val header: Header,
val results: List<Result> = emptyList(),
val raw: JsonObject = JsonObject(emptyMap())
)
@Serializer(SauceNaoAnswer::class)
object SauceNaoAnswerSerializer : KSerializer<SauceNaoAnswer> {
private val resultsSerializer = ListSerializer(Result.serializer())
private const val headerField = "header"
private const val resultsField = "results"
private val serializer = defaultSauceNaoParser
override fun deserialize(decoder: Decoder): SauceNaoAnswer {
val raw = JsonObject.serializer().deserialize(decoder)
return serializer.decodeFromJsonElement(
TemporalSauceNaoAnswerRepresentation.serializer(),
raw
).let {
SauceNaoAnswer(
it.header,
it.results,
raw
)
}
}
override fun serialize(encoder: Encoder, value: SauceNaoAnswer) {
val resultObject = buildJsonObject {
value.raw.forEach {
put(it.key, it.value)
}
put(headerField, serializer.encodeToJsonElement(Header.serializer(), value.header))
put(resultsField, serializer.encodeToJsonElement(resultsSerializer, value.results))
}
JsonObject.serializer().serialize(encoder, resultObject)
}
}

View File

@ -3,6 +3,8 @@ package com.github.insanusmokrassar.SauceNaoAPI.utils
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.json.*
@Serializer(String::class) @Serializer(String::class)
@ -10,10 +12,10 @@ object CommonMultivariantStringSerializer : KSerializer<String> by String.serial
private val stringArraySerializer = ListSerializer(String.serializer()) private val stringArraySerializer = ListSerializer(String.serializer())
override fun deserialize(decoder: Decoder): String { override fun deserialize(decoder: Decoder): String {
return try { return when (val parsed = JsonElement.serializer().deserialize(decoder)) {
decoder.decodeSerializableValue(String.serializer()) is JsonPrimitive -> parsed.content
} catch (e: Exception) { is JsonArray -> parsed.joinToString { it.jsonPrimitive.content }
decoder.decodeSerializableValue(stringArraySerializer).joinToString() else -> error("Unexpected answer object has been received: $parsed")
} }
} }
} }

View File

@ -7,9 +7,9 @@ import com.github.insanusmokrassar.SauceNaoAPI.exceptions.TooManyRequestsLongExc
import com.github.insanusmokrassar.SauceNaoAPI.models.Header import com.github.insanusmokrassar.SauceNaoAPI.models.Header
import com.github.insanusmokrassar.SauceNaoAPI.models.LimitsState import com.github.insanusmokrassar.SauceNaoAPI.models.LimitsState
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import io.ktor.utils.io.core.Closeable
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import java.io.Closeable
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min

View File

@ -3,11 +3,10 @@ package com.github.insanusmokrassar.SauceNaoAPI.utils
import com.github.insanusmokrassar.SauceNaoAPI.additional.LONG_TIME_RECALCULATING_MILLIS import com.github.insanusmokrassar.SauceNaoAPI.additional.LONG_TIME_RECALCULATING_MILLIS
import com.github.insanusmokrassar.SauceNaoAPI.additional.SHORT_TIME_RECALCULATING_MILLIS import com.github.insanusmokrassar.SauceNaoAPI.additional.SHORT_TIME_RECALCULATING_MILLIS
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.TimeSpan import io.ktor.utils.io.core.Closeable
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.Closeable
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@ -44,7 +43,7 @@ private data class TimeManagerMostOldestInLongGetter(
) : TimeManagerAction { ) : TimeManagerAction {
override suspend fun makeChangeWith(times: MutableList<DateTime>) { override suspend fun makeChangeWith(times: MutableList<DateTime>) {
times.clearTooOldTimes() times.clearTooOldTimes()
continuation.resumeWith(Result.success(times.min())) continuation.resumeWith(Result.success(times.minOrNull()))
} }
} }
@ -62,7 +61,7 @@ private data class TimeManagerMostOldestInShortGetter(
Result.success( Result.success(
times.asSequence().filter { times.asSequence().filter {
limitTime < it limitTime < it
}.min() }.minOrNull()
) )
) )
} }

View File

@ -1,5 +1,4 @@
package com.github.insanusmokrassar.SauceNaoAPI import com.github.insanusmokrassar.SauceNaoAPI.SauceNaoAPI
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.utils.io.streams.asInput import io.ktor.utils.io.streams.asInput
import kotlinx.coroutines.* import kotlinx.coroutines.*

View File

@ -1,44 +0,0 @@
package com.github.insanusmokrassar.SauceNaoAPI.models
import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.*
@Serializable
data class SauceNaoAnswer internal constructor(
val header: Header,
val results: List<Result> = emptyList(),
val raw: JsonObject = JsonObject(emptyMap())
)
@Serializer(SauceNaoAnswer::class)
object SauceNaoAnswerSerializer : KSerializer<SauceNaoAnswer> {
private val resultsSerializer = ListSerializer(Result.serializer())
private const val headerField = "header"
private const val resultsField = "results"
private val serializer = Json.nonstrict
override fun deserialize(decoder: Decoder): SauceNaoAnswer {
val raw = JsonObjectSerializer.deserialize(decoder)
val stringRaw = serializer.stringify(JsonObjectSerializer, raw)
return serializer.parse(
SauceNaoAnswer.serializer(),
stringRaw
).copy(
raw = raw
)
}
override fun serialize(encoder: Encoder, obj: SauceNaoAnswer) {
val resultObject = JsonObject(
obj.raw.content.let {
it + mapOf(
headerField to serializer.toJson(Header.serializer(), obj.header),
resultsField to serializer.toJson(resultsSerializer, obj.results)
)
}
)
JsonObject.serializer().serialize(encoder, resultObject)
}
}

View File

@ -1,16 +0,0 @@
package com.github.insanusmokrassar.SauceNaoAPI.utils
import io.ktor.http.ContentType
import io.ktor.util.asStream
import io.ktor.utils.io.core.Input
import java.io.InputStream
import java.net.URLConnection
val InputStream.mimeType: ContentType
get() {
val contentType = URLConnection.guessContentTypeFromStream(this)
return ContentType.parse(contentType)
}
val Input.mimeType: ContentType
get() = asStream().mimeType