From ec040176eddd281990023978135f027fe916940c Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 27 Jul 2024 21:59:47 +0100 Subject: [PATCH] [commonize] #496 Commonize `:data-sources:api` and ktor --- data-sources/api/build.gradle.kts | 44 ++++++++++++------ .../{ => commonMain/kotlin}/EpisodeSort.kt | 6 --- .../api/src/{ => commonMain/kotlin}/Media.kt | 0 .../kotlin}/MediaCacheMetadata.kt | 0 .../kotlin}/MediaExtraFiles.kt | 0 .../kotlin}/matcher/WebVideoMatcher.kt | 0 .../kotlin}/paging/PageBasedPagedSource.kt | 0 .../{ => commonMain/kotlin}/paging/Paged.kt | 0 .../kotlin}/paging/PagedSource.kt | 0 .../kotlin}/paging/SizedSource.kt | 0 .../kotlin}/source/ConnectionStatus.kt | 0 .../kotlin}/source/HttpMediaSource.kt | 46 ++++--------------- .../kotlin}/source/MediaFetchRequest.kt | 0 .../kotlin}/source/MediaMatch.kt | 0 .../kotlin}/source/MediaSource.kt | 6 +-- .../kotlin}/source/MediaSourceFactory.kt | 0 .../kotlin}/source/MediaSourceParameter.kt | 0 .../kotlin}/source/MediaSourceTesting.kt | 4 +- .../kotlin}/source/TopicMediaSource.kt | 0 .../kotlin}/subject/SubjectProvider.kt | 0 .../{ => commonMain/kotlin}/topic/Author.kt | 0 .../kotlin}/topic/EpisodeRange.kt | 9 ++-- .../{ => commonMain/kotlin}/topic/FileSize.kt | 14 ++++-- .../kotlin}/topic/FrameRate.kt | 0 .../kotlin}/topic/MediaOrigin.kt | 0 .../kotlin}/topic/Resolution.kt | 0 .../kotlin}/topic/ResourceLocation.kt | 3 +- .../kotlin}/topic/SubtitleLanguage.kt | 0 .../src/{ => commonMain/kotlin}/topic/Tag.kt | 0 .../{ => commonMain/kotlin}/topic/Topic.kt | 15 ------ .../kotlin}/topic/TopicCriteria.kt | 0 .../kotlin}/topic/UnifiedCollectionType.kt | 0 .../topic/titles/LabelFirstRawTitleParser.kt | 0 .../titles/PatternBasedRawTitleParser.kt | 0 .../kotlin}/topic/titles/RawTitleParser.kt | 0 .../api/src/jvmMain/kotlin/Episode.jvm.kt | 9 ++++ .../kotlin/source/HttpMediaSource.jvm.kt | 42 +++++++++++++++++ .../kotlin/source/HttpMediaSource.native.kt | 7 +++ data-sources/bt/acg.rip/build.gradle.kts | 15 ++++++ gradle/libs.versions.toml | 1 + utils/ktor-client/build.gradle.kts | 35 ++++++++++---- .../kotlin}/ClientProxyConfig.kt | 1 + .../{ => commonMain/kotlin}/DefaultClient.kt | 11 ++--- .../kotlin}/ClientProxyConfigValidatorTest.kt | 2 +- 44 files changed, 164 insertions(+), 106 deletions(-) rename data-sources/api/src/{ => commonMain/kotlin}/EpisodeSort.kt (95%) rename data-sources/api/src/{ => commonMain/kotlin}/Media.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/MediaCacheMetadata.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/MediaExtraFiles.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/matcher/WebVideoMatcher.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/paging/PageBasedPagedSource.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/paging/Paged.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/paging/PagedSource.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/paging/SizedSource.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/source/ConnectionStatus.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/source/HttpMediaSource.kt (63%) rename data-sources/api/src/{ => commonMain/kotlin}/source/MediaFetchRequest.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/source/MediaMatch.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/source/MediaSource.kt (98%) rename data-sources/api/src/{ => commonMain/kotlin}/source/MediaSourceFactory.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/source/MediaSourceParameter.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/source/MediaSourceTesting.kt (89%) rename data-sources/api/src/{ => commonMain/kotlin}/source/TopicMediaSource.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/subject/SubjectProvider.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/Author.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/EpisodeRange.kt (98%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/FileSize.kt (94%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/FrameRate.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/MediaOrigin.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/Resolution.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/ResourceLocation.kt (96%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/SubtitleLanguage.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/Tag.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/Topic.kt (84%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/TopicCriteria.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/UnifiedCollectionType.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/titles/LabelFirstRawTitleParser.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/titles/PatternBasedRawTitleParser.kt (100%) rename data-sources/api/src/{ => commonMain/kotlin}/topic/titles/RawTitleParser.kt (100%) create mode 100644 data-sources/api/src/jvmMain/kotlin/Episode.jvm.kt create mode 100644 data-sources/api/src/jvmMain/kotlin/source/HttpMediaSource.jvm.kt create mode 100644 data-sources/api/src/nativeMain/kotlin/source/HttpMediaSource.native.kt rename utils/ktor-client/src/{ => commonMain/kotlin}/ClientProxyConfig.kt (98%) rename utils/ktor-client/src/{ => commonMain/kotlin}/DefaultClient.kt (93%) rename utils/ktor-client/{test => src/jvmTest/kotlin}/ClientProxyConfigValidatorTest.kt (97%) diff --git a/data-sources/api/build.gradle.kts b/data-sources/api/build.gradle.kts index 9fe2ec9f58..4f88750efe 100644 --- a/data-sources/api/build.gradle.kts +++ b/data-sources/api/build.gradle.kts @@ -17,7 +17,8 @@ */ plugins { - kotlin("jvm") + kotlin("multiplatform") + `ani-mpp-lib-targets` kotlin("plugin.serialization") kotlin("plugin.compose") @@ -26,25 +27,38 @@ plugins { // See https://developer.android.com/develop/ui/compose/performance/stability/fix#configuration-file // But for simplicity, we just include compose here. id("org.jetbrains.compose") - `flatten-source-sets` idea } -dependencies { - implementation(libs.kotlinx.serialization.core) - api(libs.kotlinx.coroutines.core) - api(projects.utils.ktorClient) - api(projects.utils.serialization) - api(libs.ktor.client.logging) - api(libs.ktor.client.auth) - api(libs.jsoup) - implementation(projects.utils.logging) - testImplementation(libs.kotlinx.coroutines.test) - testImplementation(projects.utils.testing) +kotlin { + sourceSets.commonMain { + dependencies { + implementation(libs.kotlinx.serialization.core) + api(libs.kotlinx.coroutines.core) + api(projects.utils.ktorClient) + api(projects.utils.serialization) + implementation(projects.utils.platform) + api(libs.ktor.client.auth) + implementation(libs.ktor.client.logging) + implementation(projects.utils.logging) - implementation(compose.runtime) // required by the compose compiler -} + implementation(compose.runtime) // required by the compose compiler + } + } + + sourceSets.commonTest { + dependencies { + implementation(libs.kotlinx.coroutines.test) + implementation(projects.utils.testing) + } + } + sourceSets.jvmMain { + dependencies { + api(libs.jsoup) + } + } +} idea { module.generatedSourceDirs.add(file("test/title/generated")) } diff --git a/data-sources/api/src/EpisodeSort.kt b/data-sources/api/src/commonMain/kotlin/EpisodeSort.kt similarity index 95% rename from data-sources/api/src/EpisodeSort.kt rename to data-sources/api/src/commonMain/kotlin/EpisodeSort.kt index 1961a065c4..a86d83eb0b 100644 --- a/data-sources/api/src/EpisodeSort.kt +++ b/data-sources/api/src/commonMain/kotlin/EpisodeSort.kt @@ -6,7 +6,6 @@ import me.him188.ani.datasources.api.EpisodeSort.Normal import me.him188.ani.datasources.api.EpisodeSort.Special import me.him188.ani.datasources.api.topic.EpisodeRange import me.him188.ani.utils.serialization.BigNum -import java.math.BigDecimal /** * 剧集序号, 例如 "01", "24.5", "OVA". @@ -128,11 +127,6 @@ fun EpisodeSort(int: Int): EpisodeSort { return Normal(int.toFloat()) } -fun EpisodeSort(int: BigDecimal): EpisodeSort { - if (int < BigDecimal.ZERO) return Special(int.toString()) - return EpisodeSort(int.toString()) -} - fun EpisodeSort(int: BigNum): EpisodeSort { if (int.isNegative()) return Special(int.toString()) return EpisodeSort(int.toString()) diff --git a/data-sources/api/src/Media.kt b/data-sources/api/src/commonMain/kotlin/Media.kt similarity index 100% rename from data-sources/api/src/Media.kt rename to data-sources/api/src/commonMain/kotlin/Media.kt diff --git a/data-sources/api/src/MediaCacheMetadata.kt b/data-sources/api/src/commonMain/kotlin/MediaCacheMetadata.kt similarity index 100% rename from data-sources/api/src/MediaCacheMetadata.kt rename to data-sources/api/src/commonMain/kotlin/MediaCacheMetadata.kt diff --git a/data-sources/api/src/MediaExtraFiles.kt b/data-sources/api/src/commonMain/kotlin/MediaExtraFiles.kt similarity index 100% rename from data-sources/api/src/MediaExtraFiles.kt rename to data-sources/api/src/commonMain/kotlin/MediaExtraFiles.kt diff --git a/data-sources/api/src/matcher/WebVideoMatcher.kt b/data-sources/api/src/commonMain/kotlin/matcher/WebVideoMatcher.kt similarity index 100% rename from data-sources/api/src/matcher/WebVideoMatcher.kt rename to data-sources/api/src/commonMain/kotlin/matcher/WebVideoMatcher.kt diff --git a/data-sources/api/src/paging/PageBasedPagedSource.kt b/data-sources/api/src/commonMain/kotlin/paging/PageBasedPagedSource.kt similarity index 100% rename from data-sources/api/src/paging/PageBasedPagedSource.kt rename to data-sources/api/src/commonMain/kotlin/paging/PageBasedPagedSource.kt diff --git a/data-sources/api/src/paging/Paged.kt b/data-sources/api/src/commonMain/kotlin/paging/Paged.kt similarity index 100% rename from data-sources/api/src/paging/Paged.kt rename to data-sources/api/src/commonMain/kotlin/paging/Paged.kt diff --git a/data-sources/api/src/paging/PagedSource.kt b/data-sources/api/src/commonMain/kotlin/paging/PagedSource.kt similarity index 100% rename from data-sources/api/src/paging/PagedSource.kt rename to data-sources/api/src/commonMain/kotlin/paging/PagedSource.kt diff --git a/data-sources/api/src/paging/SizedSource.kt b/data-sources/api/src/commonMain/kotlin/paging/SizedSource.kt similarity index 100% rename from data-sources/api/src/paging/SizedSource.kt rename to data-sources/api/src/commonMain/kotlin/paging/SizedSource.kt diff --git a/data-sources/api/src/source/ConnectionStatus.kt b/data-sources/api/src/commonMain/kotlin/source/ConnectionStatus.kt similarity index 100% rename from data-sources/api/src/source/ConnectionStatus.kt rename to data-sources/api/src/commonMain/kotlin/source/ConnectionStatus.kt diff --git a/data-sources/api/src/source/HttpMediaSource.kt b/data-sources/api/src/commonMain/kotlin/source/HttpMediaSource.kt similarity index 63% rename from data-sources/api/src/source/HttpMediaSource.kt rename to data-sources/api/src/commonMain/kotlin/source/HttpMediaSource.kt index 3550f24e19..bf670cf377 100644 --- a/data-sources/api/src/source/HttpMediaSource.kt +++ b/data-sources/api/src/commonMain/kotlin/source/HttpMediaSource.kt @@ -2,7 +2,6 @@ package me.him188.ani.datasources.api.source import io.ktor.client.HttpClient import io.ktor.client.HttpClientConfig -import io.ktor.client.call.body import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.auth.Auth import io.ktor.client.plugins.auth.providers.BasicAuthCredentials @@ -10,21 +9,14 @@ import io.ktor.client.plugins.auth.providers.BearerTokens import io.ktor.client.plugins.auth.providers.basic import io.ktor.client.plugins.auth.providers.bearer import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.statement.HttpResponse import io.ktor.http.ContentType -import io.ktor.http.content.OutgoingContent import io.ktor.serialization.ContentConverter -import io.ktor.util.reflect.TypeInfo -import io.ktor.utils.io.ByteReadChannel -import io.ktor.utils.io.charsets.decode -import io.ktor.utils.io.jvm.javaio.toInputStream -import io.ktor.utils.io.streams.asInput +import io.ktor.utils.io.core.Closeable import me.him188.ani.utils.ktor.createDefaultHttpClient import me.him188.ani.utils.ktor.registerLogging import me.him188.ani.utils.logging.logger -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import java.nio.charset.Charset + +private fun Closeable.asAutoCloseable() = AutoCloseable { close() } /** * 支持执行 HTTP 请求的 [MediaSource]. 封装一些便捷的操作 @@ -76,37 +68,15 @@ fun HttpMediaSource.useHttpClient( } expectSuccess = true install(ContentNegotiation) { - register(ContentType.Text.Xml, XmlConverter) - register(ContentType.Text.Html, XmlConverter) + val xmlConverter = getXmlConverter() + register(ContentType.Text.Xml, xmlConverter) + register(ContentType.Text.Html, xmlConverter) } clientConfig() }.apply { registerLogging(logger) - }.also { addCloseable(it) } + }.also { addCloseable(it.asAutoCloseable()) } } -suspend inline fun HttpResponse.bodyAsDocument(): Document = body() - -private object XmlConverter : ContentConverter { - override suspend fun deserialize( - charset: Charset, - typeInfo: TypeInfo, - content: ByteReadChannel - ): Any? { - if (typeInfo.type.qualifiedName != Document::class.qualifiedName) return null - content.awaitContent() - val decoder = Charsets.UTF_8.newDecoder() - val string = decoder.decode(content.toInputStream().asInput()) - return Jsoup.parse(string, charset.name()) - } - - override suspend fun serializeNullable( - contentType: ContentType, - charset: Charset, - typeInfo: TypeInfo, - value: Any? - ): OutgoingContent? { - return null - } -} +internal expect fun getXmlConverter(): ContentConverter diff --git a/data-sources/api/src/source/MediaFetchRequest.kt b/data-sources/api/src/commonMain/kotlin/source/MediaFetchRequest.kt similarity index 100% rename from data-sources/api/src/source/MediaFetchRequest.kt rename to data-sources/api/src/commonMain/kotlin/source/MediaFetchRequest.kt diff --git a/data-sources/api/src/source/MediaMatch.kt b/data-sources/api/src/commonMain/kotlin/source/MediaMatch.kt similarity index 100% rename from data-sources/api/src/source/MediaMatch.kt rename to data-sources/api/src/commonMain/kotlin/source/MediaMatch.kt diff --git a/data-sources/api/src/source/MediaSource.kt b/data-sources/api/src/commonMain/kotlin/source/MediaSource.kt similarity index 98% rename from data-sources/api/src/source/MediaSource.kt rename to data-sources/api/src/commonMain/kotlin/source/MediaSource.kt index 6e840c4a75..6149afc964 100644 --- a/data-sources/api/src/source/MediaSource.kt +++ b/data-sources/api/src/commonMain/kotlin/source/MediaSource.kt @@ -30,8 +30,6 @@ import me.him188.ani.datasources.api.Media import me.him188.ani.datasources.api.paging.SizedSource import me.him188.ani.datasources.api.topic.EpisodeRange import me.him188.ani.datasources.api.topic.ResourceLocation -import java.io.Closeable -import java.io.File /** * 一个查询单个剧集的可下载的资源 [Media] 的服务, 称为数据源 [MediaSource]. @@ -79,7 +77,7 @@ import java.io.File * * @see MediaSourceFactory */ -interface MediaSource : Closeable { +interface MediaSource : AutoCloseable { /** * 全局唯一的 ID. 可用于保存用户偏好, 识别缓存资源的来源等. */ @@ -186,7 +184,7 @@ sealed class MediaSourceLocation { data object Lan : MediaSourceLocation() /** - * 资源位于本地文件系统. 必须是能通过 [File] 直接访问的. + * 资源位于本地文件系统. 必须是能通过 `File` 直接访问的. */ data object Local : MediaSourceLocation() diff --git a/data-sources/api/src/source/MediaSourceFactory.kt b/data-sources/api/src/commonMain/kotlin/source/MediaSourceFactory.kt similarity index 100% rename from data-sources/api/src/source/MediaSourceFactory.kt rename to data-sources/api/src/commonMain/kotlin/source/MediaSourceFactory.kt diff --git a/data-sources/api/src/source/MediaSourceParameter.kt b/data-sources/api/src/commonMain/kotlin/source/MediaSourceParameter.kt similarity index 100% rename from data-sources/api/src/source/MediaSourceParameter.kt rename to data-sources/api/src/commonMain/kotlin/source/MediaSourceParameter.kt diff --git a/data-sources/api/src/source/MediaSourceTesting.kt b/data-sources/api/src/commonMain/kotlin/source/MediaSourceTesting.kt similarity index 89% rename from data-sources/api/src/source/MediaSourceTesting.kt rename to data-sources/api/src/commonMain/kotlin/source/MediaSourceTesting.kt index a2ed311da8..461815c5e8 100644 --- a/data-sources/api/src/source/MediaSourceTesting.kt +++ b/data-sources/api/src/commonMain/kotlin/source/MediaSourceTesting.kt @@ -2,12 +2,12 @@ package me.him188.ani.datasources.api.source import me.him188.ani.datasources.api.paging.SizedSource import me.him188.ani.datasources.api.paging.emptySizedSource -import java.util.UUID +import me.him188.ani.utils.platform.Uuid import kotlin.random.Random open class TestHttpMediaSource( - override val mediaSourceId: String = UUID.randomUUID().toString(), + override val mediaSourceId: String = Uuid.randomString(), override val kind: MediaSourceKind = MediaSourceKind.BitTorrent, private val randomConnectivity: Boolean = false, private val fetch: suspend (MediaFetchRequest) -> SizedSource = { emptySizedSource() } diff --git a/data-sources/api/src/source/TopicMediaSource.kt b/data-sources/api/src/commonMain/kotlin/source/TopicMediaSource.kt similarity index 100% rename from data-sources/api/src/source/TopicMediaSource.kt rename to data-sources/api/src/commonMain/kotlin/source/TopicMediaSource.kt diff --git a/data-sources/api/src/subject/SubjectProvider.kt b/data-sources/api/src/commonMain/kotlin/subject/SubjectProvider.kt similarity index 100% rename from data-sources/api/src/subject/SubjectProvider.kt rename to data-sources/api/src/commonMain/kotlin/subject/SubjectProvider.kt diff --git a/data-sources/api/src/topic/Author.kt b/data-sources/api/src/commonMain/kotlin/topic/Author.kt similarity index 100% rename from data-sources/api/src/topic/Author.kt rename to data-sources/api/src/commonMain/kotlin/topic/Author.kt diff --git a/data-sources/api/src/topic/EpisodeRange.kt b/data-sources/api/src/commonMain/kotlin/topic/EpisodeRange.kt similarity index 98% rename from data-sources/api/src/topic/EpisodeRange.kt rename to data-sources/api/src/commonMain/kotlin/topic/EpisodeRange.kt index fb1a4f1ef9..d2acc36282 100644 --- a/data-sources/api/src/topic/EpisodeRange.kt +++ b/data-sources/api/src/commonMain/kotlin/topic/EpisodeRange.kt @@ -24,6 +24,7 @@ import me.him188.ani.datasources.api.topic.EpisodeRange.Combined import me.him188.ani.datasources.api.topic.EpisodeRange.Range import me.him188.ani.datasources.api.topic.EpisodeRange.Season import me.him188.ani.datasources.api.topic.EpisodeRange.Single +import kotlin.jvm.JvmName /** * 剧集范围: @@ -69,8 +70,8 @@ sealed class EpisodeRange { if (other is Range && other.start == other.end) { return value == other.start } - if (javaClass != other?.javaClass) return false - other as Single + if (other !is Single) return false + other return value == other.value } } @@ -104,8 +105,8 @@ sealed class EpisodeRange { override fun equals(other: Any?): Boolean { if (this === other) return true if (other is Single && other.value == start && other.value == end) return true - if (javaClass != other?.javaClass) return false - other as Range + if (other !is Range) return false + other return start == other.start && end == other.end } diff --git a/data-sources/api/src/topic/FileSize.kt b/data-sources/api/src/commonMain/kotlin/topic/FileSize.kt similarity index 94% rename from data-sources/api/src/topic/FileSize.kt rename to data-sources/api/src/commonMain/kotlin/topic/FileSize.kt index 392a0a5be1..f6c92ccd6a 100644 --- a/data-sources/api/src/topic/FileSize.kt +++ b/data-sources/api/src/commonMain/kotlin/topic/FileSize.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -@file:Suppress("NOTHING_TO_INLINE") +@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress") package me.him188.ani.datasources.api.topic @@ -27,6 +27,8 @@ import kotlinx.coroutines.flow.reduce import kotlinx.serialization.Serializable import me.him188.ani.datasources.api.topic.FileSize.Companion.Unspecified import me.him188.ani.datasources.api.topic.FileSize.Companion.Zero +import kotlin.jvm.JvmInline +import kotlin.math.round /** * ``` @@ -111,24 +113,28 @@ value class FileSize( if (gigaBytes == this.inGigaBytes.toDouble()) { return "${gigaBytes.toLong()} GB" } - return "${String.format("%.1f", gigaBytes)} GB" + return "${format1f(gigaBytes)} GB" } val megaBytes = this.inMegaBytesDouble if (megaBytes >= 1) { if (megaBytes == this.inMegaBytes.toDouble()) { return "${megaBytes.toLong()} MB" } - return "${String.format("%.1f", megaBytes)} MB" + return "${format1f(megaBytes)} MB" } val kiloBytes = this.inKiloBytesDouble if (kiloBytes >= 1) { if (kiloBytes == this.inKiloBytes.toDouble()) { return "${kiloBytes.toLong()} KB" } - return "${String.format("%.1f", kiloBytes)} KB" + return "${format1f(kiloBytes)} KB" } return "${this.inBytes} B" } + + private fun format1f(value: Double): String { // equivalent to `String.format("%.1f", value)` + return (round(value * 10) / 10.0).toString() + } } @PublishedApi diff --git a/data-sources/api/src/topic/FrameRate.kt b/data-sources/api/src/commonMain/kotlin/topic/FrameRate.kt similarity index 100% rename from data-sources/api/src/topic/FrameRate.kt rename to data-sources/api/src/commonMain/kotlin/topic/FrameRate.kt diff --git a/data-sources/api/src/topic/MediaOrigin.kt b/data-sources/api/src/commonMain/kotlin/topic/MediaOrigin.kt similarity index 100% rename from data-sources/api/src/topic/MediaOrigin.kt rename to data-sources/api/src/commonMain/kotlin/topic/MediaOrigin.kt diff --git a/data-sources/api/src/topic/Resolution.kt b/data-sources/api/src/commonMain/kotlin/topic/Resolution.kt similarity index 100% rename from data-sources/api/src/topic/Resolution.kt rename to data-sources/api/src/commonMain/kotlin/topic/Resolution.kt diff --git a/data-sources/api/src/topic/ResourceLocation.kt b/data-sources/api/src/commonMain/kotlin/topic/ResourceLocation.kt similarity index 96% rename from data-sources/api/src/topic/ResourceLocation.kt rename to data-sources/api/src/commonMain/kotlin/topic/ResourceLocation.kt index cf418ee462..00d681b05c 100644 --- a/data-sources/api/src/topic/ResourceLocation.kt +++ b/data-sources/api/src/commonMain/kotlin/topic/ResourceLocation.kt @@ -1,7 +1,6 @@ package me.him188.ani.datasources.api.topic import kotlinx.serialization.Serializable -import java.nio.file.Paths @Serializable @@ -77,7 +76,7 @@ sealed class ResourceLocation { * `file://` */ override val uri: String by lazy { - Paths.get(filePath).toUri().toString() + "file://${filePath}" } } } diff --git a/data-sources/api/src/topic/SubtitleLanguage.kt b/data-sources/api/src/commonMain/kotlin/topic/SubtitleLanguage.kt similarity index 100% rename from data-sources/api/src/topic/SubtitleLanguage.kt rename to data-sources/api/src/commonMain/kotlin/topic/SubtitleLanguage.kt diff --git a/data-sources/api/src/topic/Tag.kt b/data-sources/api/src/commonMain/kotlin/topic/Tag.kt similarity index 100% rename from data-sources/api/src/topic/Tag.kt rename to data-sources/api/src/commonMain/kotlin/topic/Tag.kt diff --git a/data-sources/api/src/topic/Topic.kt b/data-sources/api/src/commonMain/kotlin/topic/Topic.kt similarity index 84% rename from data-sources/api/src/topic/Topic.kt rename to data-sources/api/src/commonMain/kotlin/topic/Topic.kt index 7664170a88..4c59d7b6eb 100644 --- a/data-sources/api/src/topic/Topic.kt +++ b/data-sources/api/src/commonMain/kotlin/topic/Topic.kt @@ -21,10 +21,6 @@ package me.him188.ani.datasources.api.topic import kotlinx.serialization.Serializable import me.him188.ani.datasources.api.source.DownloadSearchQuery import me.him188.ani.datasources.api.source.MediaSource -import java.time.Instant -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter /** * An item search from a [MediaSource] @@ -63,17 +59,6 @@ class Topic( } } -private val DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm") - -fun Topic.publishedTimeString(): String? { - return publishedTimeMillis?.let { - LocalDateTime.ofInstant( - Instant.ofEpochMilli(it), - ZoneId.systemDefault(), - ).format(DATE_FORMAT) - } -} - @Serializable class TopicDetails( val tags: List, diff --git a/data-sources/api/src/topic/TopicCriteria.kt b/data-sources/api/src/commonMain/kotlin/topic/TopicCriteria.kt similarity index 100% rename from data-sources/api/src/topic/TopicCriteria.kt rename to data-sources/api/src/commonMain/kotlin/topic/TopicCriteria.kt diff --git a/data-sources/api/src/topic/UnifiedCollectionType.kt b/data-sources/api/src/commonMain/kotlin/topic/UnifiedCollectionType.kt similarity index 100% rename from data-sources/api/src/topic/UnifiedCollectionType.kt rename to data-sources/api/src/commonMain/kotlin/topic/UnifiedCollectionType.kt diff --git a/data-sources/api/src/topic/titles/LabelFirstRawTitleParser.kt b/data-sources/api/src/commonMain/kotlin/topic/titles/LabelFirstRawTitleParser.kt similarity index 100% rename from data-sources/api/src/topic/titles/LabelFirstRawTitleParser.kt rename to data-sources/api/src/commonMain/kotlin/topic/titles/LabelFirstRawTitleParser.kt diff --git a/data-sources/api/src/topic/titles/PatternBasedRawTitleParser.kt b/data-sources/api/src/commonMain/kotlin/topic/titles/PatternBasedRawTitleParser.kt similarity index 100% rename from data-sources/api/src/topic/titles/PatternBasedRawTitleParser.kt rename to data-sources/api/src/commonMain/kotlin/topic/titles/PatternBasedRawTitleParser.kt diff --git a/data-sources/api/src/topic/titles/RawTitleParser.kt b/data-sources/api/src/commonMain/kotlin/topic/titles/RawTitleParser.kt similarity index 100% rename from data-sources/api/src/topic/titles/RawTitleParser.kt rename to data-sources/api/src/commonMain/kotlin/topic/titles/RawTitleParser.kt diff --git a/data-sources/api/src/jvmMain/kotlin/Episode.jvm.kt b/data-sources/api/src/jvmMain/kotlin/Episode.jvm.kt new file mode 100644 index 0000000000..0d8f348050 --- /dev/null +++ b/data-sources/api/src/jvmMain/kotlin/Episode.jvm.kt @@ -0,0 +1,9 @@ +package me.him188.ani.datasources.api + +import me.him188.ani.datasources.api.EpisodeSort.Special +import java.math.BigDecimal + +fun EpisodeSort(int: BigDecimal): EpisodeSort { + if (int < BigDecimal.ZERO) return Special(int.toString()) + return EpisodeSort(int.toString()) +} diff --git a/data-sources/api/src/jvmMain/kotlin/source/HttpMediaSource.jvm.kt b/data-sources/api/src/jvmMain/kotlin/source/HttpMediaSource.jvm.kt new file mode 100644 index 0000000000..74646f2a8a --- /dev/null +++ b/data-sources/api/src/jvmMain/kotlin/source/HttpMediaSource.jvm.kt @@ -0,0 +1,42 @@ +package me.him188.ani.datasources.api.source + +import io.ktor.client.call.body +import io.ktor.client.statement.HttpResponse +import io.ktor.http.ContentType +import io.ktor.http.content.OutgoingContent +import io.ktor.serialization.ContentConverter +import io.ktor.util.reflect.TypeInfo +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.charsets.decode +import io.ktor.utils.io.jvm.javaio.toInputStream +import io.ktor.utils.io.streams.asInput +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import java.nio.charset.Charset + +suspend inline fun HttpResponse.bodyAsDocument(): Document = body() + +internal actual fun getXmlConverter(): ContentConverter = XmlConverter + +private object XmlConverter : ContentConverter { + override suspend fun deserialize( + charset: Charset, + typeInfo: TypeInfo, + content: ByteReadChannel + ): Any? { + if (typeInfo.type.qualifiedName != Document::class.qualifiedName) return null + content.awaitContent() + val decoder = Charsets.UTF_8.newDecoder() + val string = decoder.decode(content.toInputStream().asInput()) + return Jsoup.parse(string, charset.name()) + } + + override suspend fun serializeNullable( + contentType: ContentType, + charset: Charset, + typeInfo: TypeInfo, + value: Any? + ): OutgoingContent? { + return null + } +} diff --git a/data-sources/api/src/nativeMain/kotlin/source/HttpMediaSource.native.kt b/data-sources/api/src/nativeMain/kotlin/source/HttpMediaSource.native.kt new file mode 100644 index 0000000000..fcc8397ed6 --- /dev/null +++ b/data-sources/api/src/nativeMain/kotlin/source/HttpMediaSource.native.kt @@ -0,0 +1,7 @@ +package me.him188.ani.datasources.api.source + +import io.ktor.serialization.ContentConverter + +internal actual fun getXmlConverter(): ContentConverter { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/data-sources/bt/acg.rip/build.gradle.kts b/data-sources/bt/acg.rip/build.gradle.kts index 1f11cc3916..b5627bb12c 100644 --- a/data-sources/bt/acg.rip/build.gradle.kts +++ b/data-sources/bt/acg.rip/build.gradle.kts @@ -38,6 +38,21 @@ dependencies { implementation(projects.utils.logging) } + +//kotlin { +// sourceSets.commonMain { +// dependencies { +// api(projects.dataSources.api) +// implementation(projects.utils.ktorClient) +// api(libs.kotlinx.coroutines.core) +// implementation(libs.kotlinx.serialization.json) +// implementation(libs.jsoup) +// implementation(projects.utils.logging) +// } +// } +//} + + tasks.withType { duplicatesStrategy = DuplicatesStrategy.EXCLUDE // why is there a duplicate? } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 20209a8cfb..d8862a78c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -85,6 +85,7 @@ korlibs-crypto = { module = "com.soywiz:korlibs-crypto", version.ref = "korlibs" # Ktor ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor-client-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } diff --git a/utils/ktor-client/build.gradle.kts b/utils/ktor-client/build.gradle.kts index 62d0999618..1742e1b4bf 100644 --- a/utils/ktor-client/build.gradle.kts +++ b/utils/ktor-client/build.gradle.kts @@ -17,15 +17,32 @@ */ plugins { - kotlin("jvm") + kotlin("multiplatform") kotlin("plugin.serialization") - `flatten-source-sets` + `ani-mpp-lib-targets` } -dependencies { - api(libs.kotlinx.serialization.core) - api(libs.ktor.client.core) - api(libs.ktor.client.content.negotiation) - api(libs.ktor.serialization.kotlinx.json) - api(projects.utils.logging) -} +kotlin { + sourceSets.commonMain { + dependencies { + api(libs.kotlinx.serialization.core) + api(libs.ktor.client.core) + api(libs.ktor.client.content.negotiation) + api(libs.ktor.serialization.kotlinx.json) + api(projects.utils.logging) + implementation(projects.utils.platform) + } + } + + sourceSets.jvmMain { + dependencies { + implementation(libs.ktor.client.okhttp) + } + } + + sourceSets.appleMain { + dependencies { + implementation(libs.ktor.client.darwin) + } + } +} \ No newline at end of file diff --git a/utils/ktor-client/src/ClientProxyConfig.kt b/utils/ktor-client/src/commonMain/kotlin/ClientProxyConfig.kt similarity index 98% rename from utils/ktor-client/src/ClientProxyConfig.kt rename to utils/ktor-client/src/commonMain/kotlin/ClientProxyConfig.kt index c0f91b3b33..7aa2d2b7bd 100644 --- a/utils/ktor-client/src/ClientProxyConfig.kt +++ b/utils/ktor-client/src/commonMain/kotlin/ClientProxyConfig.kt @@ -13,6 +13,7 @@ import io.ktor.http.URLParserException import io.ktor.http.URLProtocol import io.ktor.http.Url import io.ktor.http.takeFrom +import io.ktor.utils.io.core.toByteArray import kotlinx.serialization.Serializable import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi diff --git a/utils/ktor-client/src/DefaultClient.kt b/utils/ktor-client/src/commonMain/kotlin/DefaultClient.kt similarity index 93% rename from utils/ktor-client/src/DefaultClient.kt rename to utils/ktor-client/src/commonMain/kotlin/DefaultClient.kt index ffe2d5adf2..8ae1402467 100644 --- a/utils/ktor-client/src/DefaultClient.kt +++ b/utils/ktor-client/src/commonMain/kotlin/DefaultClient.kt @@ -18,14 +18,13 @@ import io.ktor.http.isSuccess import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json import me.him188.ani.utils.ktor.HttpLogger.logHttp +import me.him188.ani.utils.logging.Logger import me.him188.ani.utils.logging.error import me.him188.ani.utils.logging.info import me.him188.ani.utils.logging.logger import me.him188.ani.utils.logging.warn -import org.slf4j.Logger import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.measureTimedValue fun createDefaultHttpClient( clientConfig: HttpClientConfig<*>.() -> Unit = {}, @@ -54,9 +53,9 @@ fun HttpClient.registerLogging( logger: Logger = logger("ktor"), ) { plugin(HttpSend).intercept { request -> - val t1 = System.nanoTime() - val result = kotlin.runCatching { execute(request) } - val duration = ((System.nanoTime() - t1) / 1e6).toDuration(DurationUnit.MILLISECONDS) + val (result, duration) = measureTimedValue { + kotlin.runCatching { execute(request) } + } logger.logHttp( method = request.method, diff --git a/utils/ktor-client/test/ClientProxyConfigValidatorTest.kt b/utils/ktor-client/src/jvmTest/kotlin/ClientProxyConfigValidatorTest.kt similarity index 97% rename from utils/ktor-client/test/ClientProxyConfigValidatorTest.kt rename to utils/ktor-client/src/jvmTest/kotlin/ClientProxyConfigValidatorTest.kt index 3e44dcfdc7..0decf907ac 100644 --- a/utils/ktor-client/test/ClientProxyConfigValidatorTest.kt +++ b/utils/ktor-client/src/jvmTest/kotlin/ClientProxyConfigValidatorTest.kt @@ -2,8 +2,8 @@ package me.him188.ani.utils.ktor import io.ktor.util.network.address import io.ktor.util.network.port -import org.junit.jupiter.api.Test import java.net.Proxy +import kotlin.test.Test import kotlin.test.assertEquals